mirror of
https://github.com/penpot/penpot.git
synced 2026-07-27 16:38:13 +00:00
wip
This commit is contained in:
parent
4e98dfb99f
commit
6d6f961b89
@ -210,7 +210,9 @@
|
||||
on-pointer-down (actions/on-pointer-down @hover selected edition drawing-tool text-editing? path-editing? grid-editing?
|
||||
path-drawing? create-comment? space? panning z? read-only?)
|
||||
|
||||
on-pointer-up (actions/on-pointer-up disable-paste-ref)
|
||||
on-pointer-up (actions/on-pointer-up disable-paste-ref)
|
||||
on-pointer-cancel (actions/on-pointer-cancel)
|
||||
on-lost-pointer-capture (actions/on-lost-pointer-capture)
|
||||
|
||||
on-pointer-enter (actions/on-pointer-enter in-viewport-ref)
|
||||
on-pointer-leave (actions/on-pointer-leave in-viewport-ref)
|
||||
@ -430,11 +432,13 @@
|
||||
:on-drag-over on-drag-over
|
||||
:on-drag-end on-drag-end
|
||||
:on-drop on-drop
|
||||
:on-pointer-down on-pointer-down
|
||||
:on-pointer-enter on-pointer-enter
|
||||
:on-pointer-leave on-pointer-leave
|
||||
:on-pointer-move on-pointer-move
|
||||
:on-pointer-up on-pointer-up}
|
||||
:on-pointer-down on-pointer-down
|
||||
:on-pointer-enter on-pointer-enter
|
||||
:on-pointer-leave on-pointer-leave
|
||||
:on-pointer-move on-pointer-move
|
||||
:on-pointer-up on-pointer-up
|
||||
:on-pointer-cancel on-pointer-cancel
|
||||
:on-lost-pointer-capture on-lost-pointer-capture}
|
||||
|
||||
[:defs
|
||||
;; This clip is so the handlers are not over the rulers
|
||||
|
||||
@ -39,6 +39,54 @@
|
||||
|
||||
(def scale-per-pixel -0.0057)
|
||||
|
||||
;; FIXME(drag-debug): temporary instrumentation to diagnose Firefox firing
|
||||
;; pointercancel/lostpointercapture mid-drag. Remove once fixed.
|
||||
(defonce ^:private drag-debug-state
|
||||
#js {:downTs 0
|
||||
:moveCount 0
|
||||
:pointerId nil
|
||||
:captureNode nil
|
||||
:captureTag nil
|
||||
:captureClass nil})
|
||||
|
||||
(defn- log-drag!
|
||||
[phase event]
|
||||
(let [state drag-debug-state
|
||||
target (dom/get-target event)
|
||||
captured (.-captureNode state)
|
||||
cap-conn (when captured (.-isConnected captured))
|
||||
cap-same (and captured (= captured target))]
|
||||
(js/console.log
|
||||
(str "[drag-debug] " phase)
|
||||
#js {:pointerId (.-pointerId event)
|
||||
:pointerType (.-pointerType event)
|
||||
:button (.-button event)
|
||||
:buttons (.-buttons event)
|
||||
:isPrimary (.-isPrimary event)
|
||||
:tag (some-> target .-tagName)
|
||||
:class (some-> target .-className)
|
||||
:captureTag (.-captureTag state)
|
||||
:captureClass (.-captureClass state)
|
||||
:captureConnected cap-conn
|
||||
:captureIsTarget cap-same
|
||||
:movesSinceDown (.-moveCount state)
|
||||
:timeFromDownMs (when (pos? (.-downTs state))
|
||||
(- (.-timeStamp event) (.-downTs state)))})))
|
||||
|
||||
(defn- drag-debug-record-down!
|
||||
[event capture-node]
|
||||
(let [state drag-debug-state]
|
||||
(set! (.-downTs state) (.-timeStamp event))
|
||||
(set! (.-moveCount state) 0)
|
||||
(set! (.-pointerId state) (.-pointerId event))
|
||||
(set! (.-captureNode state) capture-node)
|
||||
(set! (.-captureTag state) (some-> capture-node .-tagName))
|
||||
(set! (.-captureClass state) (some-> capture-node .-className))))
|
||||
|
||||
(defn- drag-debug-bump-move! []
|
||||
(set! (.-moveCount drag-debug-state)
|
||||
(inc (.-moveCount drag-debug-state))))
|
||||
|
||||
(defn on-pointer-down
|
||||
[{:keys [id blocked hidden type]} selected edition drawing-tool text-editing?
|
||||
node-editing? grid-editing? drawing-path? create-comment? space? panning z? read-only?]
|
||||
@ -50,13 +98,14 @@
|
||||
;; We need to handle editor related stuff here because
|
||||
;; handling on editor dom node does not works properly.
|
||||
(let [target (dom/get-target event)
|
||||
editor (txu/closest-text-editor-content target)]
|
||||
editor (txu/closest-text-editor-content target)
|
||||
capture-node (or editor target)]
|
||||
;; Capture mouse pointer to detect the movements even if cursor
|
||||
;; leaves the viewport or the browser itself
|
||||
;; https://developer.mozilla.org/en-US/docs/Web/API/Element/setPointerCapture
|
||||
(if editor
|
||||
(.setPointerCapture editor (.-pointerId event))
|
||||
(.setPointerCapture target (.-pointerId event))))
|
||||
(.setPointerCapture capture-node (.-pointerId event))
|
||||
(drag-debug-record-down! event capture-node)
|
||||
(log-drag! "down" event))
|
||||
|
||||
(when (or (dom/class? (dom/get-target event) "viewport-controls")
|
||||
(dom/class? (dom/get-target event) "viewport-selrect")
|
||||
@ -281,6 +330,7 @@
|
||||
[disable-paste-ref]
|
||||
(mf/use-callback
|
||||
(fn [event]
|
||||
(log-drag! "up" event)
|
||||
(dom/stop-propagation event)
|
||||
|
||||
(let [target (dom/get-target event)]
|
||||
@ -309,6 +359,40 @@
|
||||
(st/emit! (dw/finish-panning)
|
||||
(dw/finish-zooming))))))
|
||||
|
||||
(defn on-pointer-cancel
|
||||
[]
|
||||
(mf/use-fn
|
||||
(fn [event]
|
||||
(log-drag! "cancel" event)
|
||||
(dom/stop-propagation event)
|
||||
|
||||
(let [target (dom/get-target event)]
|
||||
(.releasePointerCapture target (.-pointerId event)))
|
||||
|
||||
;; `pointercancel` fires when the browser takes over the gesture
|
||||
;; (system swipe, stylus disconnect, touch cancelled). Emit `:interrupt`
|
||||
;; so any in-flight `drag-stopper` stream terminates instead of waiting
|
||||
;; for a `mouseup` that will never come, and clear pan/zoom state which
|
||||
;; isn't gated by drag-stopper.
|
||||
(st/emit! :interrupt
|
||||
(dw/finish-panning)
|
||||
(dw/finish-zooming)))))
|
||||
|
||||
(defn on-lost-pointer-capture
|
||||
[]
|
||||
(mf/use-fn
|
||||
(fn [event]
|
||||
(log-drag! "lostcapture" event)
|
||||
;; Backstop: `lostpointercapture` fires whenever the captured pointer
|
||||
;; is released — after `pointerup`, after `pointercancel`, or when the
|
||||
;; browser releases capture unilaterally (capturing element removed,
|
||||
;; another element stealing capture, navigation). This is the W3C
|
||||
;; signal for "the gesture is definitively over", so it's the safest
|
||||
;; place to ensure no drag stream is left hanging.
|
||||
(st/emit! :interrupt
|
||||
(dw/finish-panning)
|
||||
(dw/finish-zooming)))))
|
||||
|
||||
(defn on-pointer-enter
|
||||
[in-viewport-ref]
|
||||
(mf/use-fn #(mf/set-ref-val! in-viewport-ref true)))
|
||||
@ -358,6 +442,7 @@
|
||||
(let [last-position (mf/use-var nil)]
|
||||
(mf/use-fn
|
||||
(fn [event]
|
||||
(drag-debug-bump-move!)
|
||||
(let [raw-pt (dom/get-client-position event)
|
||||
pt (uwvv/point->viewport raw-pt)
|
||||
|
||||
|
||||
@ -58,7 +58,11 @@
|
||||
parent? (dom/get-parent-with-data target "dont-clear-path")]
|
||||
(when (and drawing-path? (not parent?))
|
||||
(st/emit! (dwe/clear-edition-mode))))))
|
||||
on-blur (mf/use-fn #(st/emit! (mse/->BlurEvent)))]
|
||||
on-blur (mf/use-fn
|
||||
(fn []
|
||||
;; FIXME(drag-debug): remove with rest of drag-debug
|
||||
(js/console.log "[drag-debug] window-blur -> BlurEvent")
|
||||
(st/emit! (mse/->BlurEvent))))]
|
||||
|
||||
(mf/with-effect [drawing-tool drawing-path?]
|
||||
(let [key (events/listen js/window EventType.POINTERDOWN on-pointer-down)]
|
||||
|
||||
@ -49,7 +49,11 @@
|
||||
(mf/set-ref-val! ref node)
|
||||
(reset! viewport-ref node)
|
||||
(when (some? node)
|
||||
(events/listen node EventType.MOUSELEAVE (fn [] (st/emit! (mse/->BlurEvent)))))
|
||||
(events/listen node EventType.MOUSELEAVE
|
||||
(fn []
|
||||
;; FIXME(drag-debug): remove with rest of drag-debug
|
||||
(js/console.log "[drag-debug] viewport-mouseleave -> BlurEvent")
|
||||
(st/emit! (mse/->BlurEvent)))))
|
||||
(init-observer node on-change-bounds)))]))
|
||||
|
||||
(defn point->viewport
|
||||
|
||||
@ -267,7 +267,9 @@
|
||||
on-pointer-down (actions/on-pointer-down @hover selected edition drawing-tool text-editing? path-editing? grid-editing?
|
||||
path-drawing? create-comment? space? panning z? read-only?)
|
||||
|
||||
on-pointer-up (actions/on-pointer-up disable-paste-ref)
|
||||
on-pointer-up (actions/on-pointer-up disable-paste-ref)
|
||||
on-pointer-cancel (actions/on-pointer-cancel)
|
||||
on-lost-pointer-capture (actions/on-lost-pointer-capture)
|
||||
|
||||
on-pointer-enter (actions/on-pointer-enter in-viewport-ref)
|
||||
on-pointer-leave (actions/on-pointer-leave in-viewport-ref)
|
||||
@ -577,11 +579,13 @@
|
||||
:on-drag-over on-drag-over
|
||||
:on-drag-end on-drag-end
|
||||
:on-drop on-drop
|
||||
:on-pointer-down on-pointer-down
|
||||
:on-pointer-enter on-pointer-enter
|
||||
:on-pointer-leave on-pointer-leave
|
||||
:on-pointer-move on-pointer-move
|
||||
:on-pointer-up on-pointer-up}
|
||||
:on-pointer-down on-pointer-down
|
||||
:on-pointer-enter on-pointer-enter
|
||||
:on-pointer-leave on-pointer-leave
|
||||
:on-pointer-move on-pointer-move
|
||||
:on-pointer-up on-pointer-up
|
||||
:on-pointer-cancel on-pointer-cancel
|
||||
:on-lost-pointer-capture on-lost-pointer-capture}
|
||||
|
||||
[:defs
|
||||
;; This clip is so the handlers are not over the rulers
|
||||
|
||||
@ -1091,26 +1091,69 @@
|
||||
(= result 1))
|
||||
false))
|
||||
|
||||
(defn- gesture-active?
|
||||
"True while the user is actively panning, zooming, or transforming. We
|
||||
suppress the debounced full-quality render in this state because each
|
||||
firing kicks off an async _render that ties up rAFs and the main thread,
|
||||
delaying the next set-view-box and causing the debounce to expire again
|
||||
mid-gesture (a feedback loop visible as multiple full renders during a
|
||||
single continuous pan, especially on Firefox)."
|
||||
[]
|
||||
(let [ws (:workspace-local @st/state)]
|
||||
(or (:panning ws)
|
||||
(:zooming ws)
|
||||
(some? (:transform ws)))))
|
||||
|
||||
(defn- do-render-finish! []
|
||||
(when (and wasm/context-initialized? (not @wasm/context-lost?))
|
||||
(perf/begin-measure "render-finish")
|
||||
;; FIXME(pan-end-debug): remove with rest of pan-end debug instrumentation
|
||||
(js/console.log "[pan-end-debug] render-finish FIRED")
|
||||
(h/call wasm/internal-module "_set_view_end")
|
||||
(perf/end-measure "render-finish")
|
||||
;; Use async _render: visible tiles render synchronously
|
||||
;; (no yield), interest-area tiles render progressively
|
||||
;; via rAF. _set_view_end already rebuilt the tile
|
||||
;; index. For pan, most tiles are cached so the render
|
||||
;; completes in the first frame. For zoom, interest-
|
||||
;; area tiles (~3 tile margin) don't block the main
|
||||
;; thread.
|
||||
(h/call wasm/internal-module "_render" 0)))
|
||||
|
||||
(def render-finish
|
||||
(letfn [(do-render []
|
||||
;; Check if context is still initialized before executing
|
||||
;; to prevent errors when navigating quickly
|
||||
(when (and wasm/context-initialized? (not @wasm/context-lost?))
|
||||
(perf/begin-measure "render-finish")
|
||||
(h/call wasm/internal-module "_set_view_end")
|
||||
(perf/end-measure "render-finish")
|
||||
;; Use async _render: visible tiles render synchronously
|
||||
;; (no yield), interest-area tiles render progressively
|
||||
;; via rAF. _set_view_end already rebuilt the tile
|
||||
;; index. For pan, most tiles are cached so the render
|
||||
;; completes in the first frame. For zoom, interest-
|
||||
;; area tiles (~3 tile margin) don't block the main
|
||||
;; thread.
|
||||
(h/call wasm/internal-module "_render" 0)))]
|
||||
(fns/debounce do-render DEBOUNCE_DELAY_MS)))
|
||||
(let [debounced (fns/debounce do-render-finish! DEBOUNCE_DELAY_MS)]
|
||||
(fn []
|
||||
;; While a gesture is active, suppress the debounced fire. The
|
||||
;; gesture-end watcher below will trigger do-render-finish! exactly
|
||||
;; once when the gesture ends, eliminating the mid-gesture feedback
|
||||
;; loop entirely.
|
||||
(when-not (gesture-active?)
|
||||
(debounced)))))
|
||||
|
||||
;; Fire the deferred full render the moment the gesture ends. We watch the
|
||||
;; store rather than coupling viewport.cljs to api.cljs (which would risk a
|
||||
;; circular dep) — render-wasm already depends on app.main.store.
|
||||
(defonce ^:private _gesture-end-watcher
|
||||
(add-watch st/state
|
||||
::gesture-end-watcher
|
||||
(fn [_ _ old-state new-state]
|
||||
(let [old-ws (:workspace-local old-state)
|
||||
new-ws (:workspace-local new-state)
|
||||
was-active? (or (:panning old-ws)
|
||||
(:zooming old-ws)
|
||||
(some? (:transform old-ws)))
|
||||
now-active? (or (:panning new-ws)
|
||||
(:zooming new-ws)
|
||||
(some? (:transform new-ws)))]
|
||||
(when (and was-active? (not now-active?))
|
||||
;; FIXME(pan-end-debug): remove with rest of pan-end debug instrumentation
|
||||
(js/console.log "[pan-end-debug] gesture ended -> flushing render")
|
||||
(do-render-finish!))))))
|
||||
|
||||
(defn set-view-box
|
||||
[zoom vbox]
|
||||
;; FIXME(pan-end-debug): remove with rest of pan-end debug instrumentation
|
||||
(js/console.log "[pan-end-debug] set-view-box CALLED (vbox:" (:x vbox) (:y vbox) ")")
|
||||
(perf/begin-measure "set-view-box")
|
||||
(h/call wasm/internal-module "_set_view_start")
|
||||
(h/call wasm/internal-module "_set_view" zoom (- (:x vbox)) (- (:y vbox)))
|
||||
|
||||
@ -443,6 +443,9 @@ pub extern "C" fn set_view_start() -> Result<()> {
|
||||
pub extern "C" fn set_view_end() -> Result<()> {
|
||||
with_state_mut!(state, {
|
||||
performance::begin_measure!("set_view_end");
|
||||
// FIXME(pan-end-debug): remove with rest of pan-end debug instrumentation
|
||||
let _start_set_view_end = performance::get_time();
|
||||
println!("[pan-end-debug] set_view_end: enter");
|
||||
let render_state = get_render_state();
|
||||
render_state.options.set_fast_mode(false);
|
||||
render_state.cancel_animation_frame();
|
||||
@ -471,6 +474,11 @@ pub extern "C" fn set_view_end() -> Result<()> {
|
||||
render_state.rebuild_tile_index(&state.shapes);
|
||||
}
|
||||
performance::end_measure!("set_view_end");
|
||||
// FIXME(pan-end-debug): remove with rest of pan-end debug instrumentation
|
||||
println!(
|
||||
"[pan-end-debug] set_view_end: {}ms",
|
||||
performance::get_time() - _start_set_view_end
|
||||
);
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -1689,6 +1689,14 @@ impl RenderState {
|
||||
non_overlapping.push((*id, *bounds, *selrect));
|
||||
}
|
||||
|
||||
// FIXME(pan-end-debug): remove with rest of pan-end debug instrumentation
|
||||
println!(
|
||||
"[pan-end-debug] rebuild candidates={} non_overlapping={}",
|
||||
candidates.len(),
|
||||
non_overlapping.len()
|
||||
);
|
||||
let _start_snapshots = performance::get_time();
|
||||
|
||||
// Snapshot from Backbuffer for each accepted shape.
|
||||
let scale = self.get_scale();
|
||||
let vb_left = self.viewbox.area.left;
|
||||
@ -1733,6 +1741,15 @@ impl RenderState {
|
||||
},
|
||||
);
|
||||
}
|
||||
// FIXME(pan-end-debug): remove with rest of pan-end debug instrumentation
|
||||
println!(
|
||||
"[pan-end-debug] rebuild_crop_snapshots_total: {}ms",
|
||||
performance::get_time() - _start_snapshots
|
||||
);
|
||||
println!(
|
||||
"[pan-end-debug] rebuild cached={}",
|
||||
self.backbuffer_crop_cache.len()
|
||||
);
|
||||
}
|
||||
|
||||
pub fn render_from_cache(&mut self, shapes: ShapesPoolRef) {
|
||||
@ -2103,8 +2120,32 @@ impl RenderState {
|
||||
// A full-quality frame is now complete. Refresh Backbuffer and regenerate
|
||||
// the per-shape crop cache so interactive drags can reuse pixels.
|
||||
if !self.options.is_fast_mode() && !self.options.is_interactive_transform() {
|
||||
// FIXME(pan-end-debug): remove with rest of pan-end debug instrumentation
|
||||
println!("[pan-end-debug] post_full_render_block: enter");
|
||||
let _start_post_render = performance::get_time();
|
||||
|
||||
let _start_copy = performance::get_time();
|
||||
self.surfaces.copy_target_to_backbuffer();
|
||||
println!(
|
||||
"[pan-end-debug] copy_target_to_backbuffer: {}ms",
|
||||
performance::get_time() - _start_copy
|
||||
);
|
||||
|
||||
let _start_rebuild = performance::get_time();
|
||||
self.rebuild_backbuffer_crop_cache(tree);
|
||||
println!(
|
||||
"[pan-end-debug] rebuild_backbuffer_crop_cache: {}ms",
|
||||
performance::get_time() - _start_rebuild
|
||||
);
|
||||
println!(
|
||||
"[pan-end-debug] post_full_render_block: {}ms",
|
||||
performance::get_time() - _start_post_render
|
||||
);
|
||||
} else {
|
||||
// FIXME(pan-end-debug): remove with rest of pan-end debug instrumentation
|
||||
println!(
|
||||
"[pan-end-debug] post_full_render_block: SKIPPED (fast_mode or interactive_transform still active)"
|
||||
);
|
||||
}
|
||||
wapi::notify_tiles_render_complete!();
|
||||
performance::end_measure!("render");
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user