diff --git a/CHANGES.md b/CHANGES.md index 5adb6f627..f9b5f8d72 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -7,6 +7,7 @@ ### :sparkles: New features - Select through stroke only rectangle [Taiga #5484](https://tree.taiga.io/project/penpot/issue/5484) +- Override browser Ctrl+ and Ctrl- zoom with Penpot Zoom [Taiga #3200](https://tree.taiga.io/project/penpot/us/3200) ### :bug: Bugs fixed diff --git a/frontend/src/app/main/data/workspace/comments.cljs b/frontend/src/app/main/data/workspace/comments.cljs index 46919efa4..3c3f71f74 100644 --- a/frontend/src/app/main/data/workspace/comments.cljs +++ b/frontend/src/app/main/data/workspace/comments.cljs @@ -20,6 +20,7 @@ [app.main.data.workspace.viewport :as dwv] [app.main.repo :as rp] [app.main.streams :as ms] + [app.util.mouse :as mse] [app.util.router :as rt] [beicon.core :as rx] [potok.core :as ptk])) @@ -37,7 +38,8 @@ (rx/merge (rx/of (dcm/retrieve-comment-threads file-id)) (->> stream - (rx/filter ms/mouse-click?) + (rx/filter mse/mouse-event?) + (rx/filter mse/mouse-click-event?) (rx/switch-map #(rx/take 1 ms/mouse-position)) (rx/with-latest-from ms/keyboard-space) (rx/filter (fn [[_ space]] (not space)) ) diff --git a/frontend/src/app/main/data/workspace/drawing/box.cljs b/frontend/src/app/main/data/workspace/drawing/box.cljs index 7df56e1f0..d8ccdf4c9 100644 --- a/frontend/src/app/main/data/workspace/drawing/box.cljs +++ b/frontend/src/app/main/data/workspace/drawing/box.cljs @@ -24,6 +24,7 @@ [app.main.data.workspace.state-helpers :as wsh] [app.main.snap :as snap] [app.main.streams :as ms] + [app.util.mouse :as mse] [beicon.core :as rx] [potok.core :as ptk])) @@ -76,7 +77,13 @@ (ptk/reify ::handle-drawing ptk/WatchEvent (watch [_ state stream] - (let [stoper (rx/filter #(or (ms/mouse-up? %) (= % :interrupt)) stream) + (let [stoper (rx/merge + (->> stream + (rx/filter mse/mouse-event?) + (rx/filter mse/mouse-up-event?)) + (->> stream + (rx/filter #(= % :interrupt)))) + layout (get state :workspace-layout) zoom (dm/get-in state [:workspace-local :zoom] 1) diff --git a/frontend/src/app/main/data/workspace/drawing/curve.cljs b/frontend/src/app/main/data/workspace/drawing/curve.cljs index d0b1aed44..f77078281 100644 --- a/frontend/src/app/main/data/workspace/drawing/curve.cljs +++ b/frontend/src/app/main/data/workspace/drawing/curve.cljs @@ -21,6 +21,7 @@ [app.main.data.workspace.drawing.common :as common] [app.main.data.workspace.state-helpers :as wsh] [app.main.streams :as ms] + [app.util.mouse :as mse] [app.util.path.simplify-curve :as ups] [beicon.core :as rx] [potok.core :as ptk])) @@ -29,7 +30,8 @@ (defn stoper-event? [{:keys [type] :as event}] - (ms/mouse-event? event) (= type :up)) + (and (mse/mouse-event? event) + (= type :up))) (defn- insert-point [point] diff --git a/frontend/src/app/main/data/workspace/interactions.cljs b/frontend/src/app/main/data/workspace/interactions.cljs index 622eeb70b..a7610d2e5 100644 --- a/frontend/src/app/main/data/workspace/interactions.cljs +++ b/frontend/src/app/main/data/workspace/interactions.cljs @@ -19,6 +19,7 @@ [app.main.data.workspace.state-helpers :as wsh] [app.main.data.workspace.undo :as dwu] [app.main.streams :as ms] + [app.util.mouse :as mse] [beicon.core :as rx] [potok.core :as ptk])) @@ -187,7 +188,9 @@ (watch [_ state stream] (let [initial-pos @ms/mouse-position selected (wsh/lookup-selected state) - stopper (rx/filter ms/mouse-up? stream)] + stopper (->> stream + (rx/filter mse/mouse-event?) + (rx/filter mse/mouse-up-event?))] (when (= 1 (count selected)) (rx/concat (->> ms/mouse-position @@ -295,7 +298,9 @@ (watch [_ state stream] (let [initial-pos @ms/mouse-position selected (wsh/lookup-selected state) - stopper (rx/filter ms/mouse-up? stream)] + stopper (->> stream + (rx/filter mse/mouse-event?) + (rx/filter mse/mouse-up-event?))] (when (= 1 (count selected)) (let [page-id (:current-page-id state) objects (wsh/lookup-page-objects state page-id) diff --git a/frontend/src/app/main/data/workspace/notifications.cljs b/frontend/src/app/main/data/workspace/notifications.cljs index 4b5916055..8a88ddf70 100644 --- a/frontend/src/app/main/data/workspace/notifications.cljs +++ b/frontend/src/app/main/data/workspace/notifications.cljs @@ -16,8 +16,8 @@ [app.main.data.workspace.changes :as dch] [app.main.data.workspace.libraries :as dwl] [app.main.data.workspace.persistence :as dwp] - [app.main.streams :as ms] [app.util.globals :refer [global]] + [app.util.mouse :as mse] [app.util.object :as obj] [app.util.time :as dt] [beicon.core :as rx] @@ -81,7 +81,7 @@ ;; Emit to all other connected users the current pointer ;; position changes. (->> stream - (rx/filter ms/pointer-event?) + (rx/filter mse/pointer-event?) (rx/sample 50) (rx/map #(handle-pointer-send file-id (:pt %))))) diff --git a/frontend/src/app/main/data/workspace/path/drawing.cljs b/frontend/src/app/main/data/workspace/path/drawing.cljs index a0dd03249..912a90718 100644 --- a/frontend/src/app/main/data/workspace/path/drawing.cljs +++ b/frontend/src/app/main/data/workspace/path/drawing.cljs @@ -26,7 +26,7 @@ [app.main.data.workspace.path.streams :as streams] [app.main.data.workspace.path.undo :as undo] [app.main.data.workspace.state-helpers :as wsh] - [app.main.streams :as ms] + [app.util.mouse :as mse] [beicon.core :as rx] [potok.core :as ptk])) @@ -122,16 +122,12 @@ (declare close-path-drag-end) -(defn close-path-drag-start [position] +(defn close-path-drag-start + [position] (ptk/reify ::close-path-drag-start ptk/WatchEvent (watch [_ state stream] - (let [stop-stream - (->> stream (rx/filter #(or (helpers/end-path-event? %) - (ms/mouse-up? %)))) - - content (st/get-path state :content) - + (let [content (st/get-path state :content) handlers (-> (upc/content->handlers content) (get position)) @@ -140,8 +136,14 @@ drag-events-stream (->> (streams/position-stream) - (rx/take-until stop-stream) - (rx/map #(drag-handler position idx prefix %)))] + (rx/map #(drag-handler position idx prefix %)) + (rx/take-until + (rx/merge + (->> stream + (rx/filter mse/mouse-event?) + (rx/filter mse/mouse-up-event?)) + (->> stream + (rx/filter helpers/end-path-event?)))))] (rx/concat (rx/of (add-node position)) @@ -163,12 +165,16 @@ (ptk/reify ::start-path-from-point ptk/WatchEvent (watch [_ _ stream] - (let [mouse-up (->> stream (rx/filter #(or (helpers/end-path-event? %) - (ms/mouse-up? %)))) - drag-events (->> (streams/position-stream) - (rx/take-until mouse-up) - (rx/map #(drag-handler %)))] + (let [stoper (rx/merge + (->> stream + (rx/filter mse/mouse-event?) + (rx/filter mse/mouse-up-event?)) + (->> stream + (rx/filter helpers/end-path-event?))) + drag-events (->> (streams/position-stream) + (rx/map #(drag-handler %)) + (rx/take-until stoper))] (rx/concat (rx/of (add-node position)) (streams/drag-stream @@ -185,13 +191,16 @@ (defn make-drag-stream [stream down-event] - (let [mouse-up (->> stream (rx/filter #(or (helpers/end-path-event? %) - (ms/mouse-up? %)))) + (let [stoper (rx/merge + (->> stream + (rx/filter mse/mouse-event?) + (rx/filter mse/mouse-up-event?)) + (->> stream + (rx/filter helpers/end-path-event?))) drag-events (->> (streams/position-stream) - (rx/take-until mouse-up) - (rx/map #(drag-handler %)))] - + (rx/map #(drag-handler %)) + (rx/take-until stoper))] (rx/concat (rx/of (add-node down-event)) (streams/drag-stream @@ -209,8 +218,11 @@ ptk/WatchEvent (watch [_ _ stream] - (let [mouse-down (->> stream (rx/filter ms/mouse-down?)) - end-path-events (->> stream (rx/filter helpers/end-path-event?)) + (let [mouse-down (->> stream + (rx/filter mse/mouse-event?) + (rx/filter mse/mouse-down-event?)) + end-path-events (->> stream + (rx/filter helpers/end-path-event?)) ;; Mouse move preview mousemove-events diff --git a/frontend/src/app/main/data/workspace/path/edition.cljs b/frontend/src/app/main/data/workspace/path/edition.cljs index f1a9c8bc9..31080603c 100644 --- a/frontend/src/app/main/data/workspace/path/edition.cljs +++ b/frontend/src/app/main/data/workspace/path/edition.cljs @@ -26,6 +26,7 @@ [app.main.data.workspace.path.undo :as undo] [app.main.data.workspace.state-helpers :as wsh] [app.main.streams :as ms] + [app.util.mouse :as mse] [app.util.path.tools :as upt] [beicon.core :as rx] [potok.core :as ptk])) @@ -150,7 +151,10 @@ (ptk/reify ::drag-selected-points ptk/WatchEvent (watch [_ state stream] - (let [stopper (->> stream (rx/filter ms/mouse-up?)) + (let [stopper (->> stream + (rx/filter mse/mouse-event?) + (rx/filter mse/mouse-up-event?)) + id (dm/get-in state [:workspace-local :edition]) selected-points (dm/get-in state [:workspace-local :edit-path id :selected-points] #{}) @@ -263,8 +267,6 @@ (rx/concat (rx/of (dch/update-shapes [id] upsp/convert-to-path)) (->> (streams/move-handler-stream handler point handler opposite points) - (rx/take-until (->> stream (rx/filter #(or (ms/mouse-up? %) - (streams/finish-edition? %))))) (rx/map (fn [{:keys [x y alt? shift?]}] (let [pos (cond-> (gpt/point x y) @@ -275,7 +277,15 @@ prefix (+ start-delta-x (- (:x pos) (:x handler))) (+ start-delta-y (- (:y pos) (:y handler))) - (not alt?)))))) + (not alt?))))) + (rx/take-until + (rx/merge + (->> stream + (rx/filter mse/mouse-event?) + (rx/filter mse/mouse-up-event?)) + (->> stream + (rx/filter streams/finish-edition?))))) + (rx/concat (rx/of (apply-content-modifiers))))))))) (declare stop-path-edit) diff --git a/frontend/src/app/main/data/workspace/path/helpers.cljs b/frontend/src/app/main/data/workspace/path/helpers.cljs index 5792a69f8..4249c23fe 100644 --- a/frontend/src/app/main/data/workspace/path/helpers.cljs +++ b/frontend/src/app/main/data/workspace/path/helpers.cljs @@ -14,16 +14,19 @@ [app.common.svg.path.command :as upc] [app.common.svg.path.subpath :as ups] [app.main.data.workspace.path.common :as common] - [app.main.streams :as ms] + [app.util.mouse :as mse] [potok.core :as ptk])) -(defn end-path-event? [event] - (or (= (ptk/type event) ::common/finish-path) - (= (ptk/type event) :app.main.data.workspace.path.shortcuts/esc-pressed) - (= :app.main.data.workspace.common/clear-edition-mode (ptk/type event)) - (= :app.main.data.workspace/finalize-page (ptk/type event)) - (= event :interrupt) ;; ESC - (ms/mouse-double-click? event))) +(defn end-path-event? + [event] + (let [type (ptk/type event)] + (or (= type ::common/finish-path) + (= type :app.main.data.workspace.path.shortcuts/esc-pressed) + (= type :app.main.data.workspace.common/clear-edition-mode) + (= type :app.main.data.workspace/finalize-page) + (= event :interrupt) ;; ESC + (and ^boolean (mse/mouse-event? event) + ^boolean (mse/mouse-double-click-event? event))))) (defn content-center [content] diff --git a/frontend/src/app/main/data/workspace/path/selection.cljs b/frontend/src/app/main/data/workspace/path/selection.cljs index 4d517eaf6..24b7b0bf4 100644 --- a/frontend/src/app/main/data/workspace/path/selection.cljs +++ b/frontend/src/app/main/data/workspace/path/selection.cljs @@ -13,6 +13,7 @@ [app.main.data.workspace.common :as dwc] [app.main.data.workspace.path.state :as st] [app.main.streams :as ms] + [app.util.mouse :as mse] [beicon.core :as rx] [potok.core :as ptk])) @@ -118,16 +119,21 @@ (ptk/reify ::handle-area-selection ptk/WatchEvent (watch [_ state stream] - (let [zoom (get-in state [:workspace-local :zoom] 1) - stop? (fn [event] (or (dwc/interrupt? event) (ms/mouse-up? event))) - stoper (->> stream (rx/filter stop?)) + (let [zoom (get-in state [:workspace-local :zoom] 1) + stoper (rx/merge + (->> stream + (rx/filter mse/mouse-event?) + (rx/filter mse/mouse-up-event?)) + (->> stream + (rx/filter dwc/interrupt?))) + from-p @ms/mouse-position] (rx/concat (->> ms/mouse-position - (rx/take-until stoper) (rx/map #(grc/points->rect [from-p %])) (rx/filter (partial valid-rect? zoom)) - (rx/map update-area-selection)) + (rx/map update-area-selection) + (rx/take-until stoper)) (rx/of (select-node-area shift?) (clear-area-selection)))))))) diff --git a/frontend/src/app/main/data/workspace/path/streams.cljs b/frontend/src/app/main/data/workspace/path/streams.cljs index 071e6ab12..264a145a1 100644 --- a/frontend/src/app/main/data/workspace/path/streams.cljs +++ b/frontend/src/app/main/data/workspace/path/streams.cljs @@ -14,6 +14,7 @@ [app.main.snap :as snap] [app.main.store :as st] [app.main.streams :as ms] + [app.util.mouse :as mse] [beicon.core :as rx] [okulary.core :as l] [potok.core :as ptk])) @@ -50,16 +51,20 @@ (let [zoom (get-in @st/state [:workspace-local :zoom] 1) start (-> @ms/mouse-position to-pixel-snap) - mouse-up (->> st/stream - (rx/filter #(or (finish-edition? %) - (ms/mouse-up? %)))) + + stoper (rx/merge + (->> st/stream + (rx/filter mse/mouse-event?) + (rx/filter mse/mouse-up-event?)) + (->> st/stream + (rx/filter finish-edition?))) position-stream (->> ms/mouse-position - (rx/take-until mouse-up) (rx/map to-pixel-snap) (rx/filter (dragging? start zoom)) - (rx/take 1))] + (rx/take 1) + (rx/take-until stoper))] (rx/merge (->> position-stream diff --git a/frontend/src/app/main/data/workspace/selection.cljs b/frontend/src/app/main/data/workspace/selection.cljs index fdaaaeb99..aac220262 100644 --- a/frontend/src/app/main/data/workspace/selection.cljs +++ b/frontend/src/app/main/data/workspace/selection.cljs @@ -35,12 +35,15 @@ [app.main.refs :as refs] [app.main.streams :as ms] [app.main.worker :as uw] + [app.util.mouse :as mse] [beicon.core :as rx] [clojure.set :as set] [linked.set :as lks] [potok.core :as ptk])) -(defn interrupt? [e] (= e :interrupt)) +(defn interrupt? + [e] + (= e :interrupt)) ;; --- Selection Rect @@ -60,8 +63,12 @@ ptk/WatchEvent (watch [_ state stream] (let [zoom (dm/get-in state [:workspace-local :zoom] 1) - stop? (fn [event] (or (interrupt? event) (ms/mouse-up? event))) - stoper (rx/filter stop? stream) + stoper (rx/merge + (->> stream + (rx/filter mse/mouse-event?) + (rx/filter mse/mouse-up-event?)) + (->> stream + (rx/filter interrupt?))) init-position @ms/mouse-position diff --git a/frontend/src/app/main/data/workspace/transforms.cljs b/frontend/src/app/main/data/workspace/transforms.cljs index 951962aa3..9f3434396 100644 --- a/frontend/src/app/main/data/workspace/transforms.cljs +++ b/frontend/src/app/main/data/workspace/transforms.cljs @@ -33,6 +33,8 @@ [app.main.snap :as snap] [app.main.streams :as ms] [app.util.dom :as dom] + [app.util.keyboard :as kbd] + [app.util.mouse :as mse] [beicon.core :as rx] [potok.core :as ptk])) @@ -214,7 +216,9 @@ ptk/WatchEvent (watch [_ state stream] (let [initial-position @ms/mouse-position - stopper (rx/filter ms/mouse-up? stream) + stopper (->> stream + (rx/filter mse/mouse-event?) + (rx/filter mse/mouse-up-event?)) layout (:workspace-layout state) page-id (:current-page-id state) focus (:workspace-focus-selected state) @@ -305,7 +309,10 @@ ptk/WatchEvent (watch [_ _ stream] - (let [stoper (rx/filter ms/mouse-up? stream) + (let [stoper (->> stream + (rx/filter mse/mouse-event?) + (rx/filter mse/mouse-up-event?)) + group (gsh/shapes->rect shapes) group-center (grc/rect->center group) initial-angle (gpt/angle @ms/mouse-position group-center) @@ -369,7 +376,10 @@ (watch [_ state stream] (let [initial (deref ms/mouse-position) - stopper (rx/filter ms/mouse-up? stream) + stopper (->> stream + (rx/filter mse/mouse-event?) + (rx/filter mse/mouse-up-event?)) + zoom (get-in state [:workspace-local :zoom] 1) ;; We toggle the selection so we don't have to wait for the event @@ -443,7 +453,11 @@ ids (if (nil? ids) selected ids) shapes (mapv #(get objects %) ids) duplicate-move-started? (get-in state [:workspace-local :duplicate-move-started?] false) - stopper (rx/filter ms/mouse-up? stream) + + stopper (->> stream + (rx/filter mse/mouse-event?) + (rx/filter mse/mouse-up-event?)) + layout (get state :workspace-layout) zoom (get-in state [:workspace-local :zoom] 1) focus (:workspace-focus-selected state) @@ -673,7 +687,8 @@ (rx/switch-map #(rx/merge (rx/timer 1000) (->> stream - (rx/filter ms/key-up?) + (rx/filter kbd/keyboard-event?) + (rx/filter kbd/key-up-event?) (rx/delay 250)))) (rx/take 1)) diff --git a/frontend/src/app/main/data/workspace/viewport.cljs b/frontend/src/app/main/data/workspace/viewport.cljs index dacc3ec22..1bbb48ccf 100644 --- a/frontend/src/app/main/data/workspace/viewport.cljs +++ b/frontend/src/app/main/data/workspace/viewport.cljs @@ -15,7 +15,7 @@ [app.common.geom.shapes :as gsh] [app.common.math :as mth] [app.main.data.workspace.state-helpers :as wsh] - [app.main.streams :as ms] + [app.util.mouse :as mse] [beicon.core :as rx] [potok.core :as ptk])) @@ -155,7 +155,7 @@ (rx/concat (rx/of #(-> % (assoc-in [:workspace-local :panning] true))) (->> stream - (rx/filter ms/pointer-event?) + (rx/filter mse/pointer-event?) (rx/filter #(= :delta (:source %))) (rx/map :pt) (rx/take-until stopper) diff --git a/frontend/src/app/main/data/workspace/zoom.cljs b/frontend/src/app/main/data/workspace/zoom.cljs index 0117631fa..8aba81a0b 100644 --- a/frontend/src/app/main/data/workspace/zoom.cljs +++ b/frontend/src/app/main/data/workspace/zoom.cljs @@ -14,6 +14,7 @@ [app.common.geom.shapes :as gsh] [app.main.data.workspace.state-helpers :as wsh] [app.main.streams :as ms] + [app.util.mouse :as mse] [beicon.core :as rx] [potok.core :as ptk])) @@ -118,7 +119,7 @@ (rx/concat (rx/of #(-> % (assoc-in [:workspace-local :zooming] true))) (->> stream - (rx/filter ms/pointer-event?) + (rx/filter mse/pointer-event?) (rx/filter #(= :delta (:source %))) (rx/map :pt) (rx/take-until stopper) diff --git a/frontend/src/app/main/streams.cljs b/frontend/src/app/main/streams.cljs index a4dc59d69..b17f5808d 100644 --- a/frontend/src/app/main/streams.cljs +++ b/frontend/src/app/main/streams.cljs @@ -11,94 +11,43 @@ [app.main.store :as st] [app.util.globals :as globals] [app.util.keyboard :as kbd] + [app.util.mouse :as mse] [beicon.core :as rx])) ;; --- User Events -(defrecord KeyboardEvent [type key shift ctrl alt meta editing]) - -(defn keyboard-event? - [v] - (instance? KeyboardEvent v)) - -(defn key-up? - [v] - (and (keyboard-event? v) - (= :up (:type v)))) - -(defn key-down? - [v] - (and (keyboard-event? v) - (= :down (:type v)))) - -(defrecord MouseEvent [type ctrl shift alt meta]) - -(defn mouse-event? - [v] - (instance? MouseEvent v)) - -(defn mouse-down? - [v] - (and (mouse-event? v) - (= :down (:type v)))) - -(defn mouse-up? - [v] - (and (mouse-event? v) - (= :up (:type v)))) - -(defn mouse-click? - [v] - (and (mouse-event? v) - (= :click (:type v)))) - -(defn mouse-double-click? - [v] - (and (mouse-event? v) - (= :double-click (:type v)))) - -(defrecord PointerEvent [source pt ctrl shift alt meta]) - -(defn pointer-event? - [v] - (instance? PointerEvent v)) - -(defrecord ScrollEvent [point]) - -(defn scroll-event? - [v] - (instance? ScrollEvent v)) - (defn interaction-event? [event] - (or (keyboard-event? event) - (mouse-event? event))) + (or ^boolean (kbd/keyboard-event? event) + ^boolean (mse/mouse-event? event))) ;; --- Derived streams +(defonce ^:private pointer + (->> st/stream + (rx/filter mse/pointer-event?) + (rx/share))) + (defonce mouse-position (let [sub (rx/behavior-subject nil) - ob (->> st/stream - (rx/filter pointer-event?) - (rx/filter #(= :viewport (:source %))) - (rx/map :pt))] + ob (->> pointer + (rx/filter #(= :viewport (mse/get-pointer-source %))) + (rx/map mse/get-pointer-position))] (rx/subscribe-with ob sub) sub)) (defonce mouse-position-ctrl (let [sub (rx/behavior-subject nil) - ob (->> st/stream - (rx/filter pointer-event?) - (rx/map :ctrl) + ob (->> pointer + (rx/map mse/get-pointer-ctrl-mod) (rx/dedupe))] (rx/subscribe-with ob sub) sub)) (defonce mouse-position-meta (let [sub (rx/behavior-subject nil) - ob (->> st/stream - (rx/filter pointer-event?) - (rx/map :meta) + ob (->> pointer + (rx/map mse/get-pointer-meta-mod) (rx/dedupe))] (rx/subscribe-with ob sub) sub)) @@ -110,71 +59,68 @@ (defonce mouse-position-shift (let [sub (rx/behavior-subject nil) - ob (->> st/stream - (rx/filter pointer-event?) - (rx/map :shift) + ob (->> pointer + (rx/map mse/get-pointer-shift-mod) (rx/dedupe))] (rx/subscribe-with ob sub) sub)) (defonce mouse-position-alt (let [sub (rx/behavior-subject nil) - ob (->> st/stream - (rx/filter pointer-event?) - (rx/map :alt) + ob (->> pointer + (rx/map mse/get-pointer-alt-mod) (rx/dedupe))] (rx/subscribe-with ob sub) sub)) - -(defonce window-blur +(defonce ^:private window-blur (->> (rx/from-event globals/window "blur") + (rx/map (constantly false)) + (rx/share))) + +(defonce keyboard + (->> st/stream + (rx/filter kbd/keyboard-event?) (rx/share))) (defonce keyboard-alt (let [sub (rx/behavior-subject nil) - ob (->> (rx/merge - (->> st/stream - (rx/filter keyboard-event?) - (rx/filter kbd/alt-key?) - (rx/map #(= :down (:type %)))) - ;; 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. - (->> window-blur - (rx/map (constantly false)))) + ob (->> keyboard + (rx/filter kbd/alt-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/dedupe))] (rx/subscribe-with ob sub) sub)) (defonce keyboard-ctrl (let [sub (rx/behavior-subject nil) - ob (->> (rx/merge - (->> st/stream - (rx/filter keyboard-event?) - (rx/filter kbd/ctrl-key?) - (rx/map #(= :down (:type %)))) - ;; 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. - (->> window-blur - (rx/map (constantly false)))) + ob (->> keyboard + (rx/filter kbd/ctrl-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/dedupe))] (rx/subscribe-with ob sub) sub)) (defonce keyboard-meta (let [sub (rx/behavior-subject nil) - ob (->> (rx/merge - (->> st/stream - (rx/filter keyboard-event?) - (rx/filter kbd/meta-key?) - (rx/map #(= :down (:type %)))) - ;; 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. - (->> window-blur - (rx/map (constantly false)))) + ob (->> keyboard + (rx/filter kbd/meta-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/dedupe))] (rx/subscribe-with ob sub) sub)) @@ -186,33 +132,10 @@ (defonce keyboard-space (let [sub (rx/behavior-subject nil) - ob (->> st/stream - (rx/filter keyboard-event?) + ob (->> keyboard (rx/filter kbd/space?) - (rx/filter (comp not kbd/editing?)) - (rx/map #(= :down (:type %))) - (rx/dedupe))] - (rx/subscribe-with ob sub) - sub)) - -(defonce keyboard-z - (let [sub (rx/behavior-subject nil) - ob (->> st/stream - (rx/filter keyboard-event?) - (rx/filter kbd/z?) - (rx/filter (comp not kbd/editing?)) - (rx/map #(= :down (:type %))) - (rx/dedupe))] - (rx/subscribe-with ob sub) - sub)) - -(defonce keyboard-shift - (let [sub (rx/behavior-subject nil) - ob (->> st/stream - (rx/filter keyboard-event?) - (rx/filter kbd/shift-key?) - (rx/filter (comp not kbd/editing?)) - (rx/map #(= :down (:type %))) + (rx/filter (complement kbd/editing-event?)) + (rx/map kbd/key-down-event?) (rx/dedupe))] (rx/subscribe-with ob sub) sub)) diff --git a/frontend/src/app/main/ui/hooks.cljs b/frontend/src/app/main/ui/hooks.cljs index dd0babf9f..c4a653320 100644 --- a/frontend/src/app/main/ui/hooks.cljs +++ b/frontend/src/app/main/ui/hooks.cljs @@ -348,34 +348,36 @@ state)) (defn use-dynamic-grid-item-width - ([] - (use-dynamic-grid-item-width nil)) - + ([] (use-dynamic-grid-item-width nil)) ([itemsize] - (let [width (mf/use-state (:items-width @storage)) - rowref (mf/use-ref) + (let [;; NOTE: we pass a function to use-state for avoid repeatedly + ;; lookup `:items-width` on storage on each render + width* (mf/use-state #(:items-width @storage)) + width (deref width*) - itemsize (cond - (some? itemsize) itemsize - (>= @width 1030) 280 - :else 230) + rowref (mf/use-ref) - ratio (if (some? @width) (/ @width itemsize) 0) - nitems (mth/floor ratio) - limit (min 10 nitems) - limit (max 1 limit)] + itemsize (cond + (some? itemsize) itemsize + (>= width 1030) 280 + :else 230) - (mf/with-effect + ratio (if (some? width) (/ width itemsize) 0) + nitems (mth/floor ratio) + limit (mth/min 10 nitems) + limit (mth/max 1 limit)] + + (mf/with-effect [] (let [node (mf/ref-val rowref) mnt? (volatile! true) sub (->> (wapi/observe-resize node) (rx/observe-on :af) (rx/subs (fn [entries] - (let [row (first entries) - row-rect (.-contentRect ^js row) + (let [row (first entries) + row-rect (.-contentRect ^js row) row-width (.-width ^js row-rect)] (when @mnt? - (reset! width row-width) + (reset! width* row-width) (swap! storage assoc :items-width row-width))))))] (fn [] (vreset! mnt? false) diff --git a/frontend/src/app/main/ui/workspace/viewport/actions.cljs b/frontend/src/app/main/ui/workspace/viewport/actions.cljs index c9a699db3..82b983c0b 100644 --- a/frontend/src/app/main/ui/workspace/viewport/actions.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/actions.cljs @@ -21,12 +21,12 @@ [app.main.data.workspace.specialized-panel :as-alias dwsp] [app.main.refs :as refs] [app.main.store :as st] - [app.main.streams :as ms] [app.main.ui.workspace.viewport.viewport-ref :as uwvv] [app.util.dom :as dom] [app.util.dom.dnd :as dnd] [app.util.dom.normalize-wheel :as nw] [app.util.keyboard :as kbd] + [app.util.mouse :as mse] [app.util.object :as obj] [app.util.timers :as timers] [app.util.webapi :as wapi] @@ -85,7 +85,7 @@ left-click? (do - (st/emit! (ms/->MouseEvent :down ctrl? shift? alt? meta?) + (st/emit! (mse/->MouseEvent :down ctrl? shift? alt? meta?) ::dwsp/interrupt) (when (and (not= edition id) (or text-editing? grid-editing?)) @@ -173,7 +173,7 @@ hovering? (some? @hover) raw-pt (dom/get-client-position event) pt (uwvv/point->viewport raw-pt)] - (st/emit! (ms/->MouseEvent :click ctrl? shift? alt? meta?)) + (st/emit! (mse/->MouseEvent :click ctrl? shift? alt? meta?)) (when (and hovering? (not @space?) @@ -213,7 +213,7 @@ grid-layout-id (->> @hover-ids reverse (d/seek (partial ctl/grid-layout? objects)))] - (st/emit! (ms/->MouseEvent :double-click ctrl? shift? alt? meta?)) + (st/emit! (mse/->MouseEvent :double-click ctrl? shift? alt? meta?)) ;; Emit asynchronously so the double click to exit shapes won't break (timers/schedule @@ -283,7 +283,7 @@ middle-click? (= 2 (.-which event))] (when left-click? - (st/emit! (ms/->MouseEvent :up ctrl? shift? alt? meta?))) + (st/emit! (mse/->MouseEvent :up ctrl? shift? alt? meta?))) (when middle-click? (dom/prevent-default event) @@ -314,29 +314,33 @@ shift? (kbd/shift? event) alt? (kbd/alt? event) meta? (kbd/meta? event) + mod? (kbd/mod? event) target (dom/get-target event) + editing? (or (some? (.closest ^js target ".public-DraftEditor-content")) (= "rich-text" (obj/get target "className")) (= "INPUT" (obj/get target "tagName")) (= "TEXTAREA" (obj/get target "tagName")))] (when-not (.-repeat bevent) - (st/emit! (ms/->KeyboardEvent :down key shift? ctrl? alt? meta? editing?))))))) + (st/emit! (kbd/->KeyboardEvent :down key shift? ctrl? alt? meta? mod? editing? event))))))) (defn on-key-up [] (mf/use-callback (fn [event] - (let [key (.-key event) - ctrl? (kbd/ctrl? event) - shift? (kbd/shift? event) - alt? (kbd/alt? event) - meta? (kbd/meta? event) + (let [key (.-key event) + ctrl? (kbd/ctrl? event) + shift? (kbd/shift? event) + alt? (kbd/alt? event) + meta? (kbd/meta? event) + mod? (kbd/mod? event) target (dom/get-target event) + editing? (or (some? (.closest ^js target ".public-DraftEditor-content")) (= "rich-text" (obj/get target "className")) (= "INPUT" (obj/get target "tagName")) (= "TEXTAREA" (obj/get target "tagName")))] - (st/emit! (ms/->KeyboardEvent :up key shift? ctrl? alt? meta? editing?)))))) + (st/emit! (kbd/->KeyboardEvent :up key shift? ctrl? alt? meta? mod? editing? event)))))) (defn on-pointer-move [move-stream] (let [last-position (mf/use-var nil)] @@ -353,16 +357,16 @@ (rx/push! move-stream pt) (reset! last-position raw-pt) - (st/emit! (ms/->PointerEvent :delta delta - (kbd/ctrl? event) - (kbd/shift? event) - (kbd/alt? event) - (kbd/meta? event))) - (st/emit! (ms/->PointerEvent :viewport pt - (kbd/ctrl? event) - (kbd/shift? event) - (kbd/alt? event) - (kbd/meta? event)))))))) + (st/emit! (mse/->PointerEvent :delta delta + (kbd/ctrl? event) + (kbd/shift? event) + (kbd/alt? event) + (kbd/meta? event))) + (st/emit! (mse/->PointerEvent :viewport pt + (kbd/ctrl? event) + (kbd/shift? event) + (kbd/alt? event) + (kbd/meta? event)))))))) (defn on-mouse-wheel [zoom] (mf/use-callback diff --git a/frontend/src/app/main/ui/workspace/viewport/gradients.cljs b/frontend/src/app/main/ui/workspace/viewport/gradients.cljs index f90343331..6fd05d586 100644 --- a/frontend/src/app/main/ui/workspace/viewport/gradients.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/gradients.cljs @@ -15,8 +15,8 @@ [app.main.data.workspace.colors :as dc] [app.main.refs :as refs] [app.main.store :as st] - [app.main.streams :as ms] [app.util.dom :as dom] + [app.util.mouse :as mse] [beicon.core :as rx] [cuerdas.core :as str] [rumext.v2 :as mf])) @@ -154,14 +154,14 @@ (mf/deps @moving-point from-p to-p width-p) (fn [] (let [subs (->> st/stream - (rx/filter ms/pointer-event?) - (rx/filter #(= :viewport (:source %))) - (rx/map :pt) + (rx/filter mse/pointer-event?) + (rx/filter #(= :viewport (mse/get-pointer-source %))) + (rx/map mse/get-pointer-position) (rx/subs (fn [pt] (case @moving-point - :from-p (when on-change-start (on-change-start pt)) - :to-p (when on-change-finish (on-change-finish pt)) + :from-p (when on-change-start (on-change-start pt)) + :to-p (when on-change-finish (on-change-finish pt)) :width-p (when on-change-width (let [width-v (gpt/unit (gpt/to-vec from-p width-p)) distance (gpt/point-line-distance pt from-p to-p) diff --git a/frontend/src/app/main/ui/workspace/viewport/hooks.cljs b/frontend/src/app/main/ui/workspace/viewport/hooks.cljs index 3f863cd25..1e6758d80 100644 --- a/frontend/src/app/main/ui/workspace/viewport/hooks.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/hooks.cljs @@ -31,6 +31,7 @@ [app.util.debug :as dbg] [app.util.dom :as dom] [app.util.globals :as globals] + [app.util.keyboard :as kbd] [beicon.core :as rx] [goog.events :as events] [rumext.v2 :as mf]) @@ -99,14 +100,57 @@ (when (not= @cursor new-cursor) (reset! cursor new-cursor)))))) -(defn setup-keyboard [alt? mod? space? z? shift?] - (hooks/use-stream ms/keyboard-alt #(reset! alt? %)) - (hooks/use-stream ms/keyboard-mod #(do - (reset! mod? %) - (when-not % (reset! z? false)))) ;; In mac after command+z there is no event for the release of the z key - (hooks/use-stream ms/keyboard-space #(reset! space? %)) - (hooks/use-stream ms/keyboard-z #(reset! z? %)) - (hooks/use-stream ms/keyboard-shift #(reset! shift? %))) +(defn setup-keyboard + [alt* mod* space* z* shift*] + (let [kbd-zoom-s + (mf/with-memo [] + (->> ms/keyboard + (rx/filter kbd/key-down-event?) + (rx/filter kbd/mod-event?) + (rx/filter (fn [kevent] + (or ^boolean (kbd/minus? kevent) + ^boolean (kbd/underscore? kevent) + ^boolean (kbd/equals? kevent) + ^boolean (kbd/plus? kevent)))) + (rx/dedupe))) + + kbd-shift-s + (mf/with-memo [] + (->> ms/keyboard + (rx/filter kbd/shift-key?) + (rx/filter (complement kbd/editing-event?)) + (rx/map kbd/key-down-event?) + (rx/dedupe))) + + kbd-z-s + (mf/with-memo [] + (->> ms/keyboard + (rx/filter kbd/z?) + (rx/filter (complement kbd/editing-event?)) + (rx/map kbd/key-down-event?) + (rx/dedupe)))] + + (hooks/use-stream ms/keyboard-alt (partial reset! alt*)) + (hooks/use-stream ms/keyboard-space (partial reset! space*)) + (hooks/use-stream kbd-z-s (partial reset! z*)) + (hooks/use-stream kbd-shift-s (partial reset! shift*)) + (hooks/use-stream ms/keyboard-mod + (fn [value] + (reset! mod* value) + ;; In mac after command+z there is no event + ;; for the release of the z key + (when-not ^boolean value + (reset! z* false)))) + + (hooks/use-stream kbd-zoom-s + (fn [kevent] + (dom/prevent-default kevent) + (st/emit! + (if (or ^boolean (kbd/minus? kevent) + ^boolean (kbd/underscore? kevent)) + (dw/decrease-zoom) + (dw/increase-zoom))))))) + (defn group-empty-space? "Given a group `group-id` check if `hover-ids` contains any of its children. If it doesn't means diff --git a/frontend/src/app/util/keyboard.cljs b/frontend/src/app/util/keyboard.cljs index 7d4ecf95a..5151b2f50 100644 --- a/frontend/src/app/util/keyboard.cljs +++ b/frontend/src/app/util/keyboard.cljs @@ -9,15 +9,44 @@ [app.config :as cfg] [cuerdas.core :as str])) +(defrecord KeyboardEvent [type key shift ctrl alt meta mod editing native-event] + Object + (preventDefault [_] + (.preventDefault native-event)) + + (stopPropagation [_] + (.stopPropagation native-event))) + +(defn keyboard-event? + [o] + (instance? KeyboardEvent o)) + +(defn key-up-event? + [^KeyboardEvent event] + (= :up (.-type event))) + +(defn key-down-event? + [^KeyboardEvent event] + (= :down (.-type event))) + +(defn mod-event? + [^KeyboardEvent event] + (true? (.-mod event))) + +(defn editing-event? + [^KeyboardEvent event] + (true? (.-editing event))) + (defn is-key? [^string key] - (fn [^js e] + (fn [^KeyboardEvent e] (= (.-key e) key))) (defn is-key-ignore-case? [^string key] - (fn [^js e] - (= (str/upper (.-key e)) (str/upper key)))) + (let [key (str/upper key)] + (fn [^KeyboardEvent e] + (= (str/upper (.-key e)) key)))) (defn ^boolean alt? [^js event] @@ -45,6 +74,10 @@ (def enter? (is-key? "Enter")) (def space? (is-key? " ")) (def z? (is-key-ignore-case? "z")) +(def equals? (is-key? "=")) +(def plus? (is-key? "+")) +(def minus? (is-key? "-")) +(def underscore? (is-key? "_")) (def up-arrow? (is-key? "ArrowUp")) (def down-arrow? (is-key? "ArrowDown")) (def left-arrow? (is-key? "ArrowLeft")) @@ -58,6 +91,3 @@ (def home? (is-key? "Home")) (def tab? (is-key? "Tab")) -(defn editing? [e] - (.-editing ^js e)) - diff --git a/frontend/src/app/util/mouse.cljs b/frontend/src/app/util/mouse.cljs new file mode 100644 index 000000000..6102fecfa --- /dev/null +++ b/frontend/src/app/util/mouse.cljs @@ -0,0 +1,63 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS INC + +(ns app.util.mouse) + +(defrecord MouseEvent [type ctrl shift alt meta]) +(defrecord PointerEvent [source pt ctrl shift alt meta]) +(defrecord ScrollEvent [point]) + +(defn mouse-event? + [v] + (instance? MouseEvent v)) + +(defn pointer-event? + [v] + (instance? PointerEvent v)) + +(defn scroll-event? + [v] + (instance? ScrollEvent v)) + +(defn mouse-down-event? + [^MouseEvent v] + (= :down (.-type v))) + +(defn mouse-up-event? + [^MouseEvent v] + (= :up (.-type v))) + +(defn mouse-click-event? + [^MouseEvent v] + (= :click (.-type v))) + +(defn mouse-double-click-event? + [^MouseEvent v] + (= :double-click (.-type v))) + +(defn get-pointer-source + [^PointerEvent ev] + (.-source ev)) + +(defn get-pointer-position + [^PointerEvent ev] + (.-pt ev)) + +(defn get-pointer-ctrl-mod + [^PointerEvent ev] + (.-ctrl ev)) + +(defn get-pointer-meta-mod + [^PointerEvent ev] + (.-meta ev)) + +(defn get-pointer-alt-mod + [^PointerEvent ev] + (.-meta ev)) + +(defn get-pointer-shift-mod + [^PointerEvent ev] + (.-meta ev))