diff --git a/frontend/src/app/main/data/workspace/path/selection.cljs b/frontend/src/app/main/data/workspace/path/selection.cljs index b2256b3c9..81210ab9f 100644 --- a/frontend/src/app/main/data/workspace/path/selection.cljs +++ b/frontend/src/app/main/data/workspace/path/selection.cljs @@ -14,6 +14,7 @@ [app.main.streams :as ms] [app.util.mouse :as mse] [beicon.v2.core :as rx] + [beicon.v2.operators :as rxo] [potok.v2.core :as ptk])) (defn path-pointer-enter [position] @@ -45,7 +46,7 @@ (update-in state [:workspace-local :edit-path id :hover-handlers] disj [index prefix]))))) (defn select-node-area - [shift?] + [initial-set remove?] (ptk/reify ::select-node-area ptk/UpdateEvent (update [_ state] @@ -57,13 +58,12 @@ (partial gsh/has-point-rect? selrect) (constantly false)) - selected-points (dm/get-in state [:workspace-local :edit-path id :selected-points]) - selected-points (or selected-points #{}) - xform (comp (filter #(not (= (:command %) :close-path))) (map (comp gpt/point :params)) (filter selected-point?)) - positions (into (if shift? selected-points #{}) xform content)] + positions (if remove? + (apply disj initial-set (into #{} xform content)) + (into initial-set xform content))] (cond-> state (some? id) @@ -73,8 +73,8 @@ (ptk/reify ::select-node ptk/UpdateEvent (update [_ state] - (let [id (get-in state [:workspace-local :edition]) - selected-points (or (get-in state [:workspace-local :edit-path id :selected-points]) #{}) + (let [id (dm/get-in state [:workspace-local :edition]) + selected-points (dm/get-in state [:workspace-local :edit-path id :selected-points] #{}) selected-points (cond (and shift? (contains? selected-points position)) (disj selected-points position) @@ -111,25 +111,42 @@ (update state :workspace-local dissoc :selrect)))) (defn handle-area-selection - [shift?] + [append? remove?] (letfn [(valid-rect? [zoom {width :width height :height}] (or (> width (/ 10 zoom)) (> height (/ 10 zoom))))] (ptk/reify ::handle-area-selection ptk/WatchEvent (watch [_ state stream] - (let [zoom (get-in state [:workspace-local :zoom] 1) + (let [id (dm/get-in state [:workspace-local :edition]) + zoom (dm/get-in state [:workspace-local :zoom] 1) stopper (mse/drag-stopper stream) - from-p @ms/mouse-position] - (rx/concat - (->> ms/mouse-position - (rx/map #(grc/points->rect [from-p %])) - (rx/filter (partial valid-rect? zoom)) - (rx/map update-area-selection) - (rx/take-until stopper)) + from-p @ms/mouse-position - (rx/of (select-node-area shift?) - (clear-area-selection)))))))) + initial-set + (if (or append? remove?) + (dm/get-in state [:workspace-local :edit-path id :selected-points] #{}) + #{}) + + selrect-stream + (->> ms/mouse-position + (rx/map #(grc/points->rect [from-p %])) + (rx/filter (partial valid-rect? zoom)) + (rx/take-until stopper))] + + (rx/concat + (if (or append? remove?) + (rx/empty) + (rx/of (deselect-all))) + (rx/merge + (->> selrect-stream + (rx/map update-area-selection)) + (->> selrect-stream + (rx/buffer-time 100) + (rx/map last) + (rx/pipe (rxo/distinct-contiguous)) + (rx/map #(select-node-area initial-set remove?)))) + (rx/of (clear-area-selection)))))))) (defn update-selection [point-change] @@ -137,7 +154,7 @@ ptk/UpdateEvent (update [_ state] (let [id (st/get-path-id state) - selected-points (get-in state [:workspace-local :edit-path id :selected-points] #{}) + selected-points (dm/get-in state [:workspace-local :edit-path id :selected-points] #{}) selected-points (into #{} (map point-change) selected-points)] (-> state (assoc-in [:workspace-local :edit-path id :selected-points] selected-points)))))) diff --git a/frontend/src/app/main/data/workspace/selection.cljs b/frontend/src/app/main/data/workspace/selection.cljs index 870a050a6..ad37cfeec 100644 --- a/frontend/src/app/main/data/workspace/selection.cljs +++ b/frontend/src/app/main/data/workspace/selection.cljs @@ -54,7 +54,7 @@ (assoc-in state [:workspace-local :selrect] selrect)))) (defn handle-area-selection - [preserve?] + [append? remove? ignore-groups?] (ptk/reify ::handle-area-selection ptk/WatchEvent (watch [_ state stream] @@ -62,6 +62,10 @@ stopper (mse/drag-stopper stream) init-position @ms/mouse-position + initial-set (if (or append? remove?) + (dsh/lookup-selected state) + lks/empty-linked-set) + init-selrect (grc/make-rect (dm/get-prop init-position :x) (dm/get-prop init-position :y) @@ -91,7 +95,7 @@ (rx/take-until stopper))] (rx/concat - (if preserve? + (if (or append? remove?) (rx/empty) (rx/of (deselect-all))) @@ -103,20 +107,14 @@ (rx/buffer-time 100) (rx/map last) (rx/pipe (rxo/distinct-contiguous)) - (rx/with-latest-from ms/keyboard-mod ms/keyboard-shift) - (rx/map - (fn [[_ mod? shift?]] - (select-shapes-by-current-selrect shift? mod?)))) + (rx/map #(select-shapes-by-current-selrect initial-set remove? ignore-groups?))) ;; 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/map #(select-shapes-by-current-selrect initial-set remove? ignore-groups? false)))) (->> (rx/of (update-selrect nil)) ;; We need the async so the current event finishes before updating the selrect @@ -325,22 +323,23 @@ ;; --- Select Shapes (By selrect) (defn select-shapes-by-current-selrect - ([preserve? ignore-groups?] - (select-shapes-by-current-selrect preserve? ignore-groups? true)) - ([preserve? ignore-groups? buffered?] + "Sends the current selection rectangle to the worker to compute the selection, + and sends its result to select-shapes for storage in the state." + ([initial-set remove? ignore-groups?] + (select-shapes-by-current-selrect initial-set remove? ignore-groups? true)) + + ([initial-set remove? ignore-groups? buffered?] (ptk/reify ::select-shapes-by-current-selrect ptk/WatchEvent (watch [_ state _] - (let [page-id (:current-page-id state) - objects (dsh/lookup-page-objects state) - selected (dsh/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)) - - ask-worker (if buffered? mw/ask-buffered! mw/ask!)] + (let [page-id (:current-page-id state) + objects (dsh/lookup-page-objects state) + selrect (dm/get-in state [:workspace-local :selrect]) + blocked? (fn [id] (dm/get-in objects [id :blocked] false)) + ask-worker (if buffered? mw/ask-buffered! mw/ask!) + filter-objs (comp + (filter (complement blocked?)) + (remove (partial cfh/hidden-parent? objects)))] (if (some? selrect) (->> (ask-worker @@ -353,9 +352,9 @@ :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 (if remove? + #(apply disj initial-set %) + #(into initial-set filter-objs %))) (rx/map select-shapes)) (rx/empty))))))) diff --git a/frontend/src/app/main/ui/workspace/viewport/actions.cljs b/frontend/src/app/main/ui/workspace/viewport/actions.cljs index 6ff1e381e..56c70d174 100644 --- a/frontend/src/app/main/ui/workspace/viewport/actions.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/actions.cljs @@ -102,14 +102,14 @@ node-editing? ;; Handle path node area selection (when-not read-only? - (st/emit! (dwdp/handle-area-selection shift?))) + (st/emit! (dwdp/handle-area-selection shift? (and shift? mod?)))) drawing-tool (when-not read-only? (st/emit! (dd/start-drawing drawing-tool))) (or (not id) mod?) - (st/emit! (dw/handle-area-selection shift?)) + (st/emit! (dw/handle-area-selection shift? (and shift? mod?) mod?)) (not drawing-tool) (when-not read-only?