From 14475fdc672925b03490dcb27ef8bdad328e7738 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Mon, 26 Apr 2021 18:30:56 +0200 Subject: [PATCH] :sparkles: Add move path points with keyboard --- .../src/app/main/data/workspace/path.cljs | 1 + .../app/main/data/workspace/path/edition.cljs | 152 ++++++++++++++---- .../main/data/workspace/path/shortcuts.cljs | 120 +++++++++----- 3 files changed, 195 insertions(+), 78 deletions(-) diff --git a/frontend/src/app/main/data/workspace/path.cljs b/frontend/src/app/main/data/workspace/path.cljs index ed3d3c8ee..33a5c2b52 100644 --- a/frontend/src/app/main/data/workspace/path.cljs +++ b/frontend/src/app/main/data/workspace/path.cljs @@ -24,6 +24,7 @@ (d/export edition/start-move-path-point) (d/export edition/start-path-edit) (d/export edition/create-node-at-position) +(d/export edition/move-selected) ;; Selection (d/export selection/handle-selection) diff --git a/frontend/src/app/main/data/workspace/path/edition.cljs b/frontend/src/app/main/data/workspace/path/edition.cljs index 6ccb29a9c..cace090c2 100644 --- a/frontend/src/app/main/data/workspace/path/edition.cljs +++ b/frontend/src/app/main/data/workspace/path/edition.cljs @@ -64,45 +64,61 @@ (selection/update-selection point-change) (fn [state] (update-in state [:workspace-local :edit-path id] dissoc :content-modifiers :moving-nodes :moving-handler))))))) +(defn modify-content-point + [content {dx :x dy :y} modifiers point] + (let [point-indices (upc/point-indices content point) ;; [indices] + handler-indices (upc/handler-indices content point) ;; [[index prefix]] + + modify-point + (fn [modifiers index] + (-> modifiers + (update index assoc :x dx :y dy))) + + modify-handler + (fn [modifiers [index prefix]] + (let [cx (d/prefix-keyword prefix :x) + cy (d/prefix-keyword prefix :y)] + (-> modifiers + (update index assoc cx dx cy dy))))] + + (as-> modifiers $ + (reduce modify-point $ point-indices) + (reduce modify-handler $ handler-indices)))) + +(defn set-move-modifier + [points move-modifier] + (ptk/reify ::set-modifiers + ptk/UpdateEvent + (update [_ state] + (let [id (st/get-path-id state) + content (get-in state (st/get-path state :content)) + modifiers-reducer (partial modify-content-point content move-modifier) + content-modifiers (get-in state [:workspace-local :edit-path id :content-modifiers] {}) + content-modifiers (->> points + (reduce modifiers-reducer content-modifiers))] + + (-> state + (assoc-in [:workspace-local :edit-path id :content-modifiers] content-modifiers)))))) + (defn move-selected-path-point [from-point to-point] - (letfn [(modify-content-point [content {dx :x dy :y} modifiers point] - (let [point-indices (upc/point-indices content point) ;; [indices] - handler-indices (upc/handler-indices content point) ;; [[index prefix]] + (ptk/reify ::move-point + ptk/UpdateEvent + (update [_ state] + (let [id (st/get-path-id state) + content (get-in state (st/get-path state :content)) + delta (gpt/subtract to-point from-point) - modify-point - (fn [modifiers index] - (-> modifiers - (update index assoc :x dx :y dy))) + modifiers-reducer (partial modify-content-point content delta) - modify-handler - (fn [modifiers [index prefix]] - (let [cx (d/prefix-keyword prefix :x) - cy (d/prefix-keyword prefix :y)] - (-> modifiers - (update index assoc cx dx cy dy))))] + points (get-in state [:workspace-local :edit-path id :selected-points] #{}) - (as-> modifiers $ - (reduce modify-point $ point-indices) - (reduce modify-handler $ handler-indices))))] + modifiers (get-in state [:workspace-local :edit-path id :content-modifiers] {}) + modifiers (->> points + (reduce modifiers-reducer modifiers))] - (ptk/reify ::move-point - ptk/UpdateEvent - (update [_ state] - (let [id (st/get-path-id state) - content (get-in state (st/get-path state :content)) - delta (gpt/subtract to-point from-point) - - modifiers-reducer (partial modify-content-point content delta) - - points (get-in state [:workspace-local :edit-path id :selected-points] #{}) - - modifiers (get-in state [:workspace-local :edit-path id :content-modifiers] {}) - modifiers (->> points - (reduce modifiers-reducer {}))] - - (-> state - (assoc-in [:workspace-local :edit-path id :moving-nodes] true) - (assoc-in [:workspace-local :edit-path id :content-modifiers] modifiers))))))) + (-> state + (assoc-in [:workspace-local :edit-path id :moving-nodes] true) + (assoc-in [:workspace-local :edit-path id :content-modifiers] modifiers)))))) (declare drag-selected-points) @@ -126,7 +142,6 @@ ptk/WatchEvent (watch [_ state stream] (let [stopper (->> stream (rx/filter ms/mouse-up?)) - zoom (get-in state [:workspace-local :zoom]) id (get-in state [:workspace-local :edition]) snap-toggled (get-in state [:workspace-local :edit-path id :snap-toggled]) @@ -143,6 +158,73 @@ (rx/map #(move-selected-path-point start-position %))) (rx/of (apply-content-modifiers))))))) +(defn- get-displacement + "Retrieve the correct displacement delta point for the + provided direction speed and distances thresholds." + [direction] + (case direction + :up (gpt/point 0 (- 1)) + :down (gpt/point 0 1) + :left (gpt/point (- 1) 0) + :right (gpt/point 1 0))) + +(defn finish-move-selected [] + (ptk/reify ::move-selected + ptk/UpdateEvent + (update [_ state] + (let [id (get-in state [:workspace-local :edition])] + (-> state + (update-in [:workspace-local :edit-path id] dissoc :current-move)))))) + +(defn move-selected + [direction shift?] + + (let [same-event (js/Symbol "same-event")] + (ptk/reify ::move-selected + IDeref + (-deref [_] direction) + + ptk/UpdateEvent + (update [_ state] + (let [id (get-in state [:workspace-local :edition]) + current-move (get-in state [:workspace-local :edit-path id :current-move])] + (if (nil? current-move) + (-> state + (assoc-in [:workspace-local :edit-path id :moving-nodes] true) + (assoc-in [:workspace-local :edit-path id :current-move] same-event)) + state))) + + ptk/WatchEvent + (watch [_ state stream] + (let [id (get-in state [:workspace-local :edition]) + current-move (get-in state [:workspace-local :edit-path id :current-move])] + (if (= same-event current-move) + (let [points (get-in state [:workspace-local :edit-path id :selected-points] #{}) + + move-events (->> stream + (rx/filter (ptk/type? ::move-selected)) + (rx/filter #(= direction (deref %)))) + + stopper (->> move-events (rx/debounce 100) (rx/take 1)) + + scale (if shift? (gpt/point 10) (gpt/point 1)) + + mov-vec (gpt/multiply (get-displacement direction) scale)] + + (rx/concat + (rx/merge + (->> move-events + (rx/take-until stopper) + (rx/scan #(gpt/add %1 mov-vec) (gpt/point 0 0)) + (rx/map #(set-move-modifier points %))) + + ;; First event is not read by the stream so we need to send it again + (rx/of (move-selected direction shift?))) + + (rx/of (apply-content-modifiers) + (finish-move-selected)))) + (rx/empty))))))) + (defn start-move-handler [index prefix] (ptk/reify ::start-move-handler diff --git a/frontend/src/app/main/data/workspace/path/shortcuts.cljs b/frontend/src/app/main/data/workspace/path/shortcuts.cljs index 7c28926f6..2470f2bb3 100644 --- a/frontend/src/app/main/data/workspace/path/shortcuts.cljs +++ b/frontend/src/app/main/data/workspace/path/shortcuts.cljs @@ -32,61 +32,95 @@ (rx/empty)))))) (def shortcuts - {:move-nodes {:tooltip "V" - :command "v" - :fn #(st/emit! (drp/change-edit-mode :move))} + {:move-nodes {:tooltip "V" + :command "v" + :fn #(st/emit! (drp/change-edit-mode :move))} - :draw-nodes {:tooltip "P" - :command "p" - :fn #(st/emit! (drp/change-edit-mode :draw))} + :draw-nodes {:tooltip "P" + :command "p" + :fn #(st/emit! (drp/change-edit-mode :draw))} - :add-node {:tooltip "+" - :command "+" - :fn #(st/emit! (drp/add-node))} + :add-node {:tooltip "+" + :command "+" + :fn #(st/emit! (drp/add-node))} - :delete-node {:tooltip (ds/supr) - :command ["del" "backspace"] - :fn #(st/emit! (drp/remove-node))} + :delete-node {:tooltip (ds/supr) + :command ["del" "backspace"] + :fn #(st/emit! (drp/remove-node))} - :merge-nodes {:tooltip (ds/meta "J") - :command (ds/c-mod "j") - :fn #(st/emit! (drp/merge-nodes))} + :merge-nodes {:tooltip (ds/meta "J") + :command (ds/c-mod "j") + :fn #(st/emit! (drp/merge-nodes))} - :join-nodes {:tooltip "J" - :command "j" - :fn #(st/emit! (drp/join-nodes))} + :join-nodes {:tooltip "J" + :command "j" + :fn #(st/emit! (drp/join-nodes))} - :separate-nodes {:tooltip "K" - :command "k" - :fn #(st/emit! (drp/separate-nodes))} + :separate-nodes {:tooltip "K" + :command "k" + :fn #(st/emit! (drp/separate-nodes))} - :make-corner {:tooltip "B" - :command "b" - :fn #(st/emit! (drp/make-corner))} + :make-corner {:tooltip "B" + :command "b" + :fn #(st/emit! (drp/make-corner))} - :make-curve {:tooltip (ds/meta "B") - :command (ds/c-mod "b") - :fn #(st/emit! (drp/make-curve))} + :make-curve {:tooltip (ds/meta "B") + :command (ds/c-mod "b") + :fn #(st/emit! (drp/make-curve))} - :snap-nodes {:tooltip (ds/meta "'") - :command (ds/c-mod "'") - :fn #(st/emit! (drp/toggle-snap))} - - :escape {:tooltip (ds/esc) - :command "escape" - :fn #(st/emit! (esc-pressed))} + :snap-nodes {:tooltip (ds/meta "'") + :command (ds/c-mod "'") + :fn #(st/emit! (drp/toggle-snap))} - :start-editing {:tooltip (ds/enter) - :command "enter" - :fn #(st/emit! (dw/start-editing-selected))} + :escape {:tooltip (ds/esc) + :command "escape" + :fn #(st/emit! (esc-pressed))} - :undo {:tooltip (ds/meta "Z") - :command (ds/c-mod "z") - :fn #(st/emit! (drp/undo-path))} + :start-editing {:tooltip (ds/enter) + :command "enter" + :fn #(st/emit! (dw/start-editing-selected))} + + :undo {:tooltip (ds/meta "Z") + :command (ds/c-mod "z") + :fn #(st/emit! (drp/undo-path))} + + :redo {:tooltip (ds/meta "Y") + :command [(ds/c-mod "shift+z") (ds/c-mod "y")] + :fn #(st/emit! (drp/redo-path))} + + ;; Arrow movement + :move-fast-up {:tooltip (ds/shift ds/up-arrow) + :command "shift+up" + :fn #(st/emit! (drp/move-selected :up true))} + + :move-fast-down {:tooltip (ds/shift ds/down-arrow) + :command "shift+down" + :fn #(st/emit! (drp/move-selected :down true))} + + :move-fast-right {:tooltip (ds/shift ds/right-arrow) + :command "shift+right" + :fn #(st/emit! (drp/move-selected :right true))} + + :move-fast-left {:tooltip (ds/shift ds/left-arrow) + :command "shift+left" + :fn #(st/emit! (drp/move-selected :left true))} + + :move-unit-up {:tooltip ds/up-arrow + :command "up" + :fn #(st/emit! (drp/move-selected :up false))} + + :move-unit-down {:tooltip ds/down-arrow + :command "down" + :fn #(st/emit! (drp/move-selected :down false))} + + :move-unit-left {:tooltip ds/right-arrow + :command "right" + :fn #(st/emit! (drp/move-selected :right false))} + + :move-unit-right {:tooltip ds/left-arrow + :command "left" + :fn #(st/emit! (drp/move-selected :left false))} - :redo {:tooltip (ds/meta "Y") - :command [(ds/c-mod "shift+z") (ds/c-mod "y")] - :fn #(st/emit! (drp/redo-path))} }) (defn get-tooltip [shortcut]