diff --git a/frontend/src/app/main/data/workspace/selection.cljs b/frontend/src/app/main/data/workspace/selection.cljs index 05742a14b..7c6aecea5 100644 --- a/frontend/src/app/main/data/workspace/selection.cljs +++ b/frontend/src/app/main/data/workspace/selection.cljs @@ -59,7 +59,7 @@ (assoc-in state [:workspace-local :selrect] selrect)))) (defn handle-area-selection - [preserve? ignore-groups?] + [preserve?] (ptk/reify ::handle-area-selection ptk/WatchEvent (watch [_ state stream] @@ -114,7 +114,20 @@ (rx/buffer-time 100) (rx/map last) (rx/pipe (rxo/distinct-contiguous)) - (rx/map #(select-shapes-by-current-selrect preserve? ignore-groups?)))) + (rx/with-latest-from ms/keyboard-mod ms/keyboard-shift) + (rx/map + (fn [[_ mod? shift?]] + (select-shapes-by-current-selrect shift? mod?)))) + + ;; The last "tick" from the mouse cannot be buffered so we are sure + ;; a selection is returned. Without this we can have empty selections on + ;; very fast movement + (->> selrect-stream + (rx/last) + (rx/with-latest-from ms/keyboard-mod ms/keyboard-shift) + (rx/map + (fn [[_ mod? shift?]] + (select-shapes-by-current-selrect shift? mod? false))))) (->> (rx/of (update-selrect nil)) ;; We need the async so the current event finishes before updating the selrect @@ -307,34 +320,39 @@ ;; --- Select Shapes (By selrect) (defn select-shapes-by-current-selrect - [preserve? ignore-groups?] - (ptk/reify ::select-shapes-by-current-selrect - ptk/WatchEvent - (watch [_ state _] - (let [page-id (:current-page-id state) - objects (wsh/lookup-page-objects state) - selected (wsh/lookup-selected state) - initial-set (if preserve? - selected - lks/empty-linked-set) - selrect (dm/get-in state [:workspace-local :selrect]) - blocked? (fn [id] (dm/get-in objects [id :blocked] false))] + ([preserve? ignore-groups?] + (select-shapes-by-current-selrect preserve? ignore-groups? true)) + ([preserve? ignore-groups? buffered?] + (ptk/reify ::select-shapes-by-current-selrect + ptk/WatchEvent + (watch [_ state _] + (let [page-id (:current-page-id state) + objects (wsh/lookup-page-objects state) + selected (wsh/lookup-selected state) + initial-set (if preserve? + selected + lks/empty-linked-set) + selrect (dm/get-in state [:workspace-local :selrect]) + blocked? (fn [id] (dm/get-in objects [id :blocked] false)) - (when selrect - (rx/empty) - (->> (uw/ask-buffered! - {:cmd :selection/query - :page-id page-id - :rect selrect - :include-frames? true - :ignore-groups? ignore-groups? - :full-frame? true - :using-selrect? true}) - (rx/map #(cfh/clean-loops objects %)) - (rx/map #(into initial-set (comp - (filter (complement blocked?)) - (remove (partial cfh/hidden-parent? objects))) %)) - (rx/map select-shapes))))))) + ask-worker (if buffered? uw/ask-buffered! uw/ask!)] + + (if (some? selrect) + (->> (ask-worker + {:cmd :selection/query + :page-id page-id + :rect selrect + :include-frames? true + :ignore-groups? ignore-groups? + :full-frame? true + :using-selrect? true}) + (rx/filter some?) + (rx/map #(cfh/clean-loops objects %)) + (rx/map #(into initial-set (comp + (filter (complement blocked?)) + (remove (partial cfh/hidden-parent? objects))) %)) + (rx/map select-shapes)) + (rx/empty))))))) (defn select-inside-group [group-id position] diff --git a/frontend/src/app/main/streams.cljs b/frontend/src/app/main/streams.cljs index 536e98b66..4fc3d970d 100644 --- a/frontend/src/app/main/streams.cljs +++ b/frontend/src/app/main/streams.cljs @@ -112,6 +112,20 @@ (rx/sub! ob sub) sub)) +(defonce keyboard-shift + (let [sub (rx/behavior-subject nil) + ob (->> keyboard + (rx/filter kbd/shift-key?) + (rx/map kbd/key-down-event?) + ;; Fix a situation caused by using `ctrl+alt` kind of + ;; shortcuts, that makes keyboard-alt stream + ;; registering the key pressed but on blurring the + ;; window (unfocus) the key down is never arrived. + (rx/merge window-blur) + (rx/pipe (rxo/distinct-contiguous)))] + (rx/sub! ob sub) + sub)) + (defonce keyboard-meta (let [sub (rx/behavior-subject nil) ob (->> keyboard diff --git a/frontend/src/app/main/ui/workspace/viewport/actions.cljs b/frontend/src/app/main/ui/workspace/viewport/actions.cljs index 3a9174a9d..30fa24840 100644 --- a/frontend/src/app/main/ui/workspace/viewport/actions.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/actions.cljs @@ -106,7 +106,7 @@ (st/emit! (dd/start-drawing drawing-tool))) (or (not id) mod?) - (st/emit! (dw/handle-area-selection shift? mod?)) + (st/emit! (dw/handle-area-selection shift?)) (not drawing-tool) (when-not workspace-read-only? diff --git a/frontend/src/app/worker/selection.cljs b/frontend/src/app/worker/selection.cljs index ea940b883..fa73fe356 100644 --- a/frontend/src/app/worker/selection.cljs +++ b/frontend/src/app/worker/selection.cljs @@ -127,7 +127,7 @@ match-criteria? (fn [shape] (and (not (:hidden shape)) - (or (= :frame (:type shape)) ;; We return frames even if blocked + (or (cfh/frame-shape? shape) ;; We return frames even if blocked (not (:blocked shape))) (or (not frame-id) (= frame-id (:frame-id shape))) (case (:type shape) @@ -135,8 +135,11 @@ (:bool :group) (not ignore-groups?) true) + ;; This condition controls when to check for overlapping. Otherwise the + ;; shape needs to be fully contained. (or (not full-frame?) - (not= :frame (:type shape)) + (and (not ignore-groups?) (contains? shape :component-id)) + (and (not ignore-groups?) (not (cfh/root-frame? shape))) (and (d/not-empty? (:shapes shape)) (gsh/rect-contains-shape? rect shape)) (and (empty? (:shapes shape))