From e8ccddceaaa0a23da9b0bc84e63f260fae942c6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bel=C3=A9n=20Albeza?= Date: Thu, 11 Jun 2026 17:35:58 +0200 Subject: [PATCH] :tada: Make foreign guides not visible in focus mode --- .../main/ui/workspace/viewport/guides.cljs | 89 ++++++++++++------- .../app/main/ui/workspace/viewport_wasm.cljs | 21 +++-- 2 files changed, 71 insertions(+), 39 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/viewport/guides.cljs b/frontend/src/app/main/ui/workspace/viewport/guides.cljs index 1c3eff32d9..d62ca3cf6b 100644 --- a/frontend/src/app/main/ui/workspace/viewport/guides.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/guides.cljs @@ -62,12 +62,29 @@ (mth/round new-position) new-position))) -(defn- guide-draggable-in-focus? - [focus {:keys [frame-id]}] +(defn guide-visible-in-focus? + "When focus mode is active, only free guides and guides bound to a focused + board are visible and interactive." + [focus frame-id] (or (nil? frame-id) (empty? focus) (contains? focus frame-id))) +(defn wasm-visible-guides + "Guide map sent to the WASM renderer. Must be the same map used to resolve + `find-guide-at` indices (`guide-by-serialized-index`). Filters by + rulers/grids visibility, focus mode, and excludes the guide currently being + dragged (the SVG overlay draws it instead)." + [{:keys [guides visible? focused dragging-id]}] + (let [guides (if visible? (or guides {}) {}) + guides (if (seq focused) + (into {} (filter (fn [[_ guide]] + (guide-visible-in-focus? focused (:frame-id guide))) + guides)) + guides)] + (cond-> guides + dragging-id (dissoc dragging-id)))) + (defn use-guide "Hooks to support drag/drop for existing guides and new guides" [on-guide-change get-hover-frame zoom {:keys [id position axis frame-id]}] @@ -642,8 +659,9 @@ :hover-frame frame}])])) (defn- guide-by-serialized-index - "Maps a WASM guide index back to the guide map entry. The index matches the - serialization order used by `set-guides` / `write-guides`." + "Maps a WASM guide index back to the guide map entry. `guides` must be the + same map passed to `set-guides` (typically `wasm-visible-guides`); index + order matches `write-guides` / `(vec (vals guides))`." [guides index] (when (>= index 0) (nth (vec (vals guides)) index nil))) @@ -732,7 +750,7 @@ :mode :drag|:edit ...}` or nil) plus callbacks the overlay needs in edit mode: `commit-edit` (commits the parsed input value) and `cancel-edit` (drops the edit without committing)." - [{:keys [guides zoom wasm-guides? disabled-guides? on-guide-change + [{:keys [wasm-guides zoom wasm-guides? disabled-guides? on-guide-change on-guide-drag on-guide-hover get-hover-frame focus]}] (let [dragging-ref (mf/use-ref false) moved-ref (mf/use-ref false) @@ -861,7 +879,13 @@ guide-at-event (fn [event] (when-let [pt (uwvv/point->viewport (dom/get-client-position event))] - (guide-by-serialized-index guides (wasm.api/find-guide-at pt zoom)))) + (guide-by-serialized-index wasm-guides (wasm.api/find-guide-at pt zoom)))) + + visible-guide-at-event + (fn [event] + (when-let [guide (guide-at-event event)] + (when (guide-visible-in-focus? focus (:frame-id guide)) + guide))) guide-frame-offset (fn [guide] @@ -878,8 +902,7 @@ (when (and (not read-only?) (not (editing?)) (not (mf/ref-val dragging-ref))) - (let [guide (when-let [g (guide-at-event event)] - (when (guide-draggable-in-focus? focus g) g)) + (let [guide (visible-guide-at-event event) current-state @state current-hover-id (when (= :hover (:mode current-state)) (-> current-state :guide :id))] @@ -911,22 +934,23 @@ ;; via the input's blur handler. Don't initiate a drag on the ;; same pointerdown. (when (= 0 (.-button event)) - (let [position (dom/get-client-position event) - guide (guide-at-event event)] - (when (and guide (guide-draggable-in-focus? focus guide)) + (let [client-pos (dom/get-client-position event) + guide (visible-guide-at-event event) + {:keys [id axis position frame-id]} guide] + (when guide (when-let [viewport @uwvv/viewport-ref] (.setPointerCapture viewport (.-pointerId event))) (dom/stop-propagation event) - (emit-hover-axis (:axis guide)) - (emit-hover-guide-id (:id guide)) + (emit-hover-axis axis) + (emit-hover-guide-id id) (mf/set-ref-val! dragging-ref true) (mf/set-ref-val! moved-ref false) - (mf/set-ref-val! start-ref position) + (mf/set-ref-val! start-ref client-pos) (mf/set-ref-val! guide-ref guide) (mf/set-ref-val! pending-ref {:guide guide - :new-position (:position guide) - :new-frame-id (:frame-id guide) + :new-position position + :new-frame-id frame-id :mode :drag}) ;; Pointer capture (above) routes all subsequent pointer ;; events to the viewport, so we listen on the viewport @@ -944,20 +968,21 @@ double-click (fn [event] (when (and (not read-only?) (not (editing?))) - (let [guide (guide-at-event event)] - (when (and guide (guide-draggable-in-focus? focus guide)) + (let [guide (visible-guide-at-event event) + {:keys [id axis position frame-id]} guide] + (when guide (dom/prevent-default event) (dom/stop-propagation event) (when (some? on-guide-drag) - (on-guide-drag (:id guide))) + (on-guide-drag id)) (mf/set-ref-val! guide-ref guide) - (let [frame (some-> (:frame-id guide) refs/object-by-id deref) + (let [frame (some-> frame-id refs/object-by-id deref) offset (if frame - (if (= :x (:axis guide)) (:x frame) (:y frame)) + (if (= :x axis) (:x frame) (:y frame)) 0)] (reset! state {:guide guide - :new-position (:position guide) - :new-frame-id (:frame-id guide) + :new-position position + :new-frame-id frame-id :frame-offset offset :mode :edit})))))) @@ -978,7 +1003,7 @@ (reset-state)))] (mf/with-effect [wasm-guides? disabled-guides? read-only? - guides zoom focus snap-pixel? + wasm-guides zoom focus snap-pixel? on-guide-change on-guide-drag on-guide-hover get-hover-frame] (when (and wasm-guides? (not disabled-guides?) (not read-only?)) (when-let [viewport @uwvv/viewport-ref] @@ -1003,10 +1028,10 @@ (mf/defc wasm-guide-overlay-layer* "Owns WASM guide drag/edit state and overlay rendering so updates are not blocked by memoization on `viewport-guides*`." - [{:keys [guides zoom wasm-guides? disabled-guides? on-guide-change + [{:keys [wasm-guides zoom wasm-guides? disabled-guides? on-guide-change on-guide-drag on-guide-hover get-hover-frame focus vbox]}] (let [{:keys [state commit-edit cancel-edit]} - (use-wasm-guide-interaction {:guides guides + (use-wasm-guide-interaction {:wasm-guides wasm-guides :zoom zoom :wasm-guides? wasm-guides? :disabled-guides? disabled-guides? @@ -1031,8 +1056,8 @@ (mf/defc viewport-guides* {::mf/wrap [mf/memo]} - [{:keys [zoom vbox hover-frame disabled-guides modifiers guides wasm-guides? - on-guide-drag on-guide-hover]}] + [{:keys [zoom vbox hover-frame disabled-guides modifiers guides wasm-guides + wasm-guides? on-guide-drag on-guide-hover]}] (let [visible-guides (mf/with-memo [guides vbox] (->> (vals guides) @@ -1069,13 +1094,13 @@ ;; the render engine instead of per-guide SVG areas. on-wasm-context-menu (mf/use-fn - (mf/deps guides zoom disabled-guides) + (mf/deps wasm-guides zoom disabled-guides) (fn [event] (when-not disabled-guides (let [position (dom/get-client-position event) pt (uwvv/point->viewport position) index (when pt (wasm.api/find-guide-at pt zoom)) - guide (guide-by-serialized-index guides index)] + guide (guide-by-serialized-index wasm-guides index)] (when guide (dom/prevent-default event) (dom/stop-propagation event) @@ -1109,7 +1134,7 @@ :disabled-guides disabled-guides}] (when wasm-guides? - [:> wasm-guide-overlay-layer* {:guides guides + [:> wasm-guide-overlay-layer* {:wasm-guides wasm-guides :zoom zoom :wasm-guides? wasm-guides? :disabled-guides? disabled-guides @@ -1122,7 +1147,7 @@ (when-not wasm-guides? (for [{:keys [id frame-id] :as guide} visible-guides] - (when (guide-draggable-in-focus? focus guide) + (when (guide-visible-in-focus? focus frame-id) [:> guide* {:key (dm/str "guide-" id) :guide guide :vbox vbox diff --git a/frontend/src/app/main/ui/workspace/viewport_wasm.cljs b/frontend/src/app/main/ui/workspace/viewport_wasm.cljs index 693530be1e..3a75783a48 100644 --- a/frontend/src/app/main/ui/workspace/viewport_wasm.cljs +++ b/frontend/src/app/main/ui/workspace/viewport_wasm.cljs @@ -352,6 +352,14 @@ disabled-guides? (or drawing-tool transform path-drawing? path-editing? (contains? layout :lock-guides)) + wasm-guides + (mf/with-memo [guides focus show-rulers? show-grids? @dragging-guide-id*] + (guides/wasm-visible-guides + {:guides guides + :visible? (and show-rulers? show-grids?) + :focused focus + :dragging-id @dragging-guide-id*})) + single-select? (= (count selected-shapes) 1) first-shape (first selected-shapes) @@ -548,14 +556,12 @@ (when @canvas-init? (wasm.api/render-ui-only))) - ;; Ruler guides: push the page guides to the render engine whenever they - ;; change or their visibility toggles. When hidden we send an empty set. - ;; While dragging, exclude the active guide so the SVG preview is the only line. - (mf/with-effect [@canvas-init? guides objects show-rulers? show-grids? @dragging-guide-id*] + ;; Ruler guides: push the WASM-visible guide set to the render engine. + ;; `wasm-guides` is also passed to the SVG overlay for index-based hit + ;; testing — it must stay in sync with what we serialize here. + (mf/with-effect [@canvas-init? wasm-guides objects] (when @canvas-init? - (let [guides (if (and show-rulers? show-grids?) (or guides {}) {}) - guides (if-let [id @dragging-guide-id*] (dissoc guides id) guides)] - (wasm.api/set-guides guides objects)))) + (wasm.api/set-guides wasm-guides objects))) (hooks/setup-dom-events zoom disable-paste-ref in-viewport-ref read-only? drawing-tool path-drawing?) (hooks/setup-viewport-size vport viewport-ref) @@ -856,6 +862,7 @@ {:zoom zoom :vbox vbox :guides guides + :wasm-guides wasm-guides :wasm-guides? true :hover-frame guide-frame :disabled-guides disabled-guides?