Merge pull request #3863 from penpot/audriu-Ctrl-Plus-to-zoom-into-Canvas

 Override browser zoom with penpot zoom
This commit is contained in:
Alejandro 2023-11-29 09:28:01 +01:00 committed by GitHub
commit 3246c196d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 393 additions and 251 deletions

View File

@ -7,6 +7,7 @@
### :sparkles: New features ### :sparkles: New features
- Select through stroke only rectangle [Taiga #5484](https://tree.taiga.io/project/penpot/issue/5484) - 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 ### :bug: Bugs fixed

View File

@ -20,6 +20,7 @@
[app.main.data.workspace.viewport :as dwv] [app.main.data.workspace.viewport :as dwv]
[app.main.repo :as rp] [app.main.repo :as rp]
[app.main.streams :as ms] [app.main.streams :as ms]
[app.util.mouse :as mse]
[app.util.router :as rt] [app.util.router :as rt]
[beicon.core :as rx] [beicon.core :as rx]
[potok.core :as ptk])) [potok.core :as ptk]))
@ -37,7 +38,8 @@
(rx/merge (rx/merge
(rx/of (dcm/retrieve-comment-threads file-id)) (rx/of (dcm/retrieve-comment-threads file-id))
(->> stream (->> 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/switch-map #(rx/take 1 ms/mouse-position))
(rx/with-latest-from ms/keyboard-space) (rx/with-latest-from ms/keyboard-space)
(rx/filter (fn [[_ space]] (not space)) ) (rx/filter (fn [[_ space]] (not space)) )

View File

@ -24,6 +24,7 @@
[app.main.data.workspace.state-helpers :as wsh] [app.main.data.workspace.state-helpers :as wsh]
[app.main.snap :as snap] [app.main.snap :as snap]
[app.main.streams :as ms] [app.main.streams :as ms]
[app.util.mouse :as mse]
[beicon.core :as rx] [beicon.core :as rx]
[potok.core :as ptk])) [potok.core :as ptk]))
@ -76,7 +77,13 @@
(ptk/reify ::handle-drawing (ptk/reify ::handle-drawing
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (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) layout (get state :workspace-layout)
zoom (dm/get-in state [:workspace-local :zoom] 1) zoom (dm/get-in state [:workspace-local :zoom] 1)

View File

@ -21,6 +21,7 @@
[app.main.data.workspace.drawing.common :as common] [app.main.data.workspace.drawing.common :as common]
[app.main.data.workspace.state-helpers :as wsh] [app.main.data.workspace.state-helpers :as wsh]
[app.main.streams :as ms] [app.main.streams :as ms]
[app.util.mouse :as mse]
[app.util.path.simplify-curve :as ups] [app.util.path.simplify-curve :as ups]
[beicon.core :as rx] [beicon.core :as rx]
[potok.core :as ptk])) [potok.core :as ptk]))
@ -29,7 +30,8 @@
(defn stoper-event? (defn stoper-event?
[{:keys [type] :as event}] [{:keys [type] :as event}]
(ms/mouse-event? event) (= type :up)) (and (mse/mouse-event? event)
(= type :up)))
(defn- insert-point (defn- insert-point
[point] [point]

View File

@ -19,6 +19,7 @@
[app.main.data.workspace.state-helpers :as wsh] [app.main.data.workspace.state-helpers :as wsh]
[app.main.data.workspace.undo :as dwu] [app.main.data.workspace.undo :as dwu]
[app.main.streams :as ms] [app.main.streams :as ms]
[app.util.mouse :as mse]
[beicon.core :as rx] [beicon.core :as rx]
[potok.core :as ptk])) [potok.core :as ptk]))
@ -187,7 +188,9 @@
(watch [_ state stream] (watch [_ state stream]
(let [initial-pos @ms/mouse-position (let [initial-pos @ms/mouse-position
selected (wsh/lookup-selected state) 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)) (when (= 1 (count selected))
(rx/concat (rx/concat
(->> ms/mouse-position (->> ms/mouse-position
@ -295,7 +298,9 @@
(watch [_ state stream] (watch [_ state stream]
(let [initial-pos @ms/mouse-position (let [initial-pos @ms/mouse-position
selected (wsh/lookup-selected state) 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)) (when (= 1 (count selected))
(let [page-id (:current-page-id state) (let [page-id (:current-page-id state)
objects (wsh/lookup-page-objects state page-id) objects (wsh/lookup-page-objects state page-id)

View File

@ -16,8 +16,8 @@
[app.main.data.workspace.changes :as dch] [app.main.data.workspace.changes :as dch]
[app.main.data.workspace.libraries :as dwl] [app.main.data.workspace.libraries :as dwl]
[app.main.data.workspace.persistence :as dwp] [app.main.data.workspace.persistence :as dwp]
[app.main.streams :as ms]
[app.util.globals :refer [global]] [app.util.globals :refer [global]]
[app.util.mouse :as mse]
[app.util.object :as obj] [app.util.object :as obj]
[app.util.time :as dt] [app.util.time :as dt]
[beicon.core :as rx] [beicon.core :as rx]
@ -81,7 +81,7 @@
;; Emit to all other connected users the current pointer ;; Emit to all other connected users the current pointer
;; position changes. ;; position changes.
(->> stream (->> stream
(rx/filter ms/pointer-event?) (rx/filter mse/pointer-event?)
(rx/sample 50) (rx/sample 50)
(rx/map #(handle-pointer-send file-id (:pt %))))) (rx/map #(handle-pointer-send file-id (:pt %)))))

View File

@ -26,7 +26,7 @@
[app.main.data.workspace.path.streams :as streams] [app.main.data.workspace.path.streams :as streams]
[app.main.data.workspace.path.undo :as undo] [app.main.data.workspace.path.undo :as undo]
[app.main.data.workspace.state-helpers :as wsh] [app.main.data.workspace.state-helpers :as wsh]
[app.main.streams :as ms] [app.util.mouse :as mse]
[beicon.core :as rx] [beicon.core :as rx]
[potok.core :as ptk])) [potok.core :as ptk]))
@ -122,16 +122,12 @@
(declare close-path-drag-end) (declare close-path-drag-end)
(defn close-path-drag-start [position] (defn close-path-drag-start
[position]
(ptk/reify ::close-path-drag-start (ptk/reify ::close-path-drag-start
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (watch [_ state stream]
(let [stop-stream (let [content (st/get-path state :content)
(->> stream (rx/filter #(or (helpers/end-path-event? %)
(ms/mouse-up? %))))
content (st/get-path state :content)
handlers (-> (upc/content->handlers content) handlers (-> (upc/content->handlers content)
(get position)) (get position))
@ -140,8 +136,14 @@
drag-events-stream drag-events-stream
(->> (streams/position-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/concat
(rx/of (add-node position)) (rx/of (add-node position))
@ -163,12 +165,16 @@
(ptk/reify ::start-path-from-point (ptk/reify ::start-path-from-point
ptk/WatchEvent ptk/WatchEvent
(watch [_ _ stream] (watch [_ _ stream]
(let [mouse-up (->> stream (rx/filter #(or (helpers/end-path-event? %) (let [stoper (rx/merge
(ms/mouse-up? %)))) (->> stream
drag-events (->> (streams/position-stream) (rx/filter mse/mouse-event?)
(rx/take-until mouse-up) (rx/filter mse/mouse-up-event?))
(rx/map #(drag-handler %)))] (->> stream
(rx/filter helpers/end-path-event?)))
drag-events (->> (streams/position-stream)
(rx/map #(drag-handler %))
(rx/take-until stoper))]
(rx/concat (rx/concat
(rx/of (add-node position)) (rx/of (add-node position))
(streams/drag-stream (streams/drag-stream
@ -185,13 +191,16 @@
(defn make-drag-stream (defn make-drag-stream
[stream down-event] [stream down-event]
(let [mouse-up (->> stream (rx/filter #(or (helpers/end-path-event? %) (let [stoper (rx/merge
(ms/mouse-up? %)))) (->> stream
(rx/filter mse/mouse-event?)
(rx/filter mse/mouse-up-event?))
(->> stream
(rx/filter helpers/end-path-event?)))
drag-events (->> (streams/position-stream) 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/concat
(rx/of (add-node down-event)) (rx/of (add-node down-event))
(streams/drag-stream (streams/drag-stream
@ -209,8 +218,11 @@
ptk/WatchEvent ptk/WatchEvent
(watch [_ _ stream] (watch [_ _ stream]
(let [mouse-down (->> stream (rx/filter ms/mouse-down?)) (let [mouse-down (->> stream
end-path-events (->> stream (rx/filter helpers/end-path-event?)) (rx/filter mse/mouse-event?)
(rx/filter mse/mouse-down-event?))
end-path-events (->> stream
(rx/filter helpers/end-path-event?))
;; Mouse move preview ;; Mouse move preview
mousemove-events mousemove-events

View File

@ -26,6 +26,7 @@
[app.main.data.workspace.path.undo :as undo] [app.main.data.workspace.path.undo :as undo]
[app.main.data.workspace.state-helpers :as wsh] [app.main.data.workspace.state-helpers :as wsh]
[app.main.streams :as ms] [app.main.streams :as ms]
[app.util.mouse :as mse]
[app.util.path.tools :as upt] [app.util.path.tools :as upt]
[beicon.core :as rx] [beicon.core :as rx]
[potok.core :as ptk])) [potok.core :as ptk]))
@ -150,7 +151,10 @@
(ptk/reify ::drag-selected-points (ptk/reify ::drag-selected-points
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (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]) id (dm/get-in state [:workspace-local :edition])
selected-points (dm/get-in state [:workspace-local :edit-path id :selected-points] #{}) selected-points (dm/get-in state [:workspace-local :edit-path id :selected-points] #{})
@ -263,8 +267,6 @@
(rx/concat (rx/concat
(rx/of (dch/update-shapes [id] upsp/convert-to-path)) (rx/of (dch/update-shapes [id] upsp/convert-to-path))
(->> (streams/move-handler-stream handler point handler opposite points) (->> (streams/move-handler-stream handler point handler opposite points)
(rx/take-until (->> stream (rx/filter #(or (ms/mouse-up? %)
(streams/finish-edition? %)))))
(rx/map (rx/map
(fn [{:keys [x y alt? shift?]}] (fn [{:keys [x y alt? shift?]}]
(let [pos (cond-> (gpt/point x y) (let [pos (cond-> (gpt/point x y)
@ -275,7 +277,15 @@
prefix prefix
(+ start-delta-x (- (:x pos) (:x handler))) (+ start-delta-x (- (:x pos) (:x handler)))
(+ start-delta-y (- (:y pos) (:y 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))))))))) (rx/concat (rx/of (apply-content-modifiers)))))))))
(declare stop-path-edit) (declare stop-path-edit)

View File

@ -14,16 +14,19 @@
[app.common.svg.path.command :as upc] [app.common.svg.path.command :as upc]
[app.common.svg.path.subpath :as ups] [app.common.svg.path.subpath :as ups]
[app.main.data.workspace.path.common :as common] [app.main.data.workspace.path.common :as common]
[app.main.streams :as ms] [app.util.mouse :as mse]
[potok.core :as ptk])) [potok.core :as ptk]))
(defn end-path-event? [event] (defn end-path-event?
(or (= (ptk/type event) ::common/finish-path) [event]
(= (ptk/type event) :app.main.data.workspace.path.shortcuts/esc-pressed) (let [type (ptk/type event)]
(= :app.main.data.workspace.common/clear-edition-mode (ptk/type event)) (or (= type ::common/finish-path)
(= :app.main.data.workspace/finalize-page (ptk/type event)) (= type :app.main.data.workspace.path.shortcuts/esc-pressed)
(= event :interrupt) ;; ESC (= type :app.main.data.workspace.common/clear-edition-mode)
(ms/mouse-double-click? event))) (= 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 (defn content-center
[content] [content]

View File

@ -13,6 +13,7 @@
[app.main.data.workspace.common :as dwc] [app.main.data.workspace.common :as dwc]
[app.main.data.workspace.path.state :as st] [app.main.data.workspace.path.state :as st]
[app.main.streams :as ms] [app.main.streams :as ms]
[app.util.mouse :as mse]
[beicon.core :as rx] [beicon.core :as rx]
[potok.core :as ptk])) [potok.core :as ptk]))
@ -118,16 +119,21 @@
(ptk/reify ::handle-area-selection (ptk/reify ::handle-area-selection
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (watch [_ state stream]
(let [zoom (get-in state [:workspace-local :zoom] 1) (let [zoom (get-in state [:workspace-local :zoom] 1)
stop? (fn [event] (or (dwc/interrupt? event) (ms/mouse-up? event))) stoper (rx/merge
stoper (->> stream (rx/filter stop?)) (->> stream
(rx/filter mse/mouse-event?)
(rx/filter mse/mouse-up-event?))
(->> stream
(rx/filter dwc/interrupt?)))
from-p @ms/mouse-position] from-p @ms/mouse-position]
(rx/concat (rx/concat
(->> ms/mouse-position (->> ms/mouse-position
(rx/take-until stoper)
(rx/map #(grc/points->rect [from-p %])) (rx/map #(grc/points->rect [from-p %]))
(rx/filter (partial valid-rect? zoom)) (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?) (rx/of (select-node-area shift?)
(clear-area-selection)))))))) (clear-area-selection))))))))

View File

@ -14,6 +14,7 @@
[app.main.snap :as snap] [app.main.snap :as snap]
[app.main.store :as st] [app.main.store :as st]
[app.main.streams :as ms] [app.main.streams :as ms]
[app.util.mouse :as mse]
[beicon.core :as rx] [beicon.core :as rx]
[okulary.core :as l] [okulary.core :as l]
[potok.core :as ptk])) [potok.core :as ptk]))
@ -50,16 +51,20 @@
(let [zoom (get-in @st/state [:workspace-local :zoom] 1) (let [zoom (get-in @st/state [:workspace-local :zoom] 1)
start (-> @ms/mouse-position to-pixel-snap) start (-> @ms/mouse-position to-pixel-snap)
mouse-up (->> st/stream
(rx/filter #(or (finish-edition? %) stoper (rx/merge
(ms/mouse-up? %)))) (->> st/stream
(rx/filter mse/mouse-event?)
(rx/filter mse/mouse-up-event?))
(->> st/stream
(rx/filter finish-edition?)))
position-stream position-stream
(->> ms/mouse-position (->> ms/mouse-position
(rx/take-until mouse-up)
(rx/map to-pixel-snap) (rx/map to-pixel-snap)
(rx/filter (dragging? start zoom)) (rx/filter (dragging? start zoom))
(rx/take 1))] (rx/take 1)
(rx/take-until stoper))]
(rx/merge (rx/merge
(->> position-stream (->> position-stream

View File

@ -35,12 +35,15 @@
[app.main.refs :as refs] [app.main.refs :as refs]
[app.main.streams :as ms] [app.main.streams :as ms]
[app.main.worker :as uw] [app.main.worker :as uw]
[app.util.mouse :as mse]
[beicon.core :as rx] [beicon.core :as rx]
[clojure.set :as set] [clojure.set :as set]
[linked.set :as lks] [linked.set :as lks]
[potok.core :as ptk])) [potok.core :as ptk]))
(defn interrupt? [e] (= e :interrupt)) (defn interrupt?
[e]
(= e :interrupt))
;; --- Selection Rect ;; --- Selection Rect
@ -60,8 +63,12 @@
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (watch [_ state stream]
(let [zoom (dm/get-in state [:workspace-local :zoom] 1) (let [zoom (dm/get-in state [:workspace-local :zoom] 1)
stop? (fn [event] (or (interrupt? event) (ms/mouse-up? event))) stoper (rx/merge
stoper (rx/filter stop? stream) (->> stream
(rx/filter mse/mouse-event?)
(rx/filter mse/mouse-up-event?))
(->> stream
(rx/filter interrupt?)))
init-position @ms/mouse-position init-position @ms/mouse-position

View File

@ -33,6 +33,8 @@
[app.main.snap :as snap] [app.main.snap :as snap]
[app.main.streams :as ms] [app.main.streams :as ms]
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.keyboard :as kbd]
[app.util.mouse :as mse]
[beicon.core :as rx] [beicon.core :as rx]
[potok.core :as ptk])) [potok.core :as ptk]))
@ -214,7 +216,9 @@
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (watch [_ state stream]
(let [initial-position @ms/mouse-position (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) layout (:workspace-layout state)
page-id (:current-page-id state) page-id (:current-page-id state)
focus (:workspace-focus-selected state) focus (:workspace-focus-selected state)
@ -305,7 +309,10 @@
ptk/WatchEvent ptk/WatchEvent
(watch [_ _ stream] (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 (gsh/shapes->rect shapes)
group-center (grc/rect->center group) group-center (grc/rect->center group)
initial-angle (gpt/angle @ms/mouse-position group-center) initial-angle (gpt/angle @ms/mouse-position group-center)
@ -369,7 +376,10 @@
(watch [_ state stream] (watch [_ state stream]
(let [initial (deref ms/mouse-position) (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) zoom (get-in state [:workspace-local :zoom] 1)
;; We toggle the selection so we don't have to wait for the event ;; We toggle the selection so we don't have to wait for the event
@ -443,7 +453,11 @@
ids (if (nil? ids) selected ids) ids (if (nil? ids) selected ids)
shapes (mapv #(get objects %) ids) shapes (mapv #(get objects %) ids)
duplicate-move-started? (get-in state [:workspace-local :duplicate-move-started?] false) 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) layout (get state :workspace-layout)
zoom (get-in state [:workspace-local :zoom] 1) zoom (get-in state [:workspace-local :zoom] 1)
focus (:workspace-focus-selected state) focus (:workspace-focus-selected state)
@ -673,7 +687,8 @@
(rx/switch-map #(rx/merge (rx/switch-map #(rx/merge
(rx/timer 1000) (rx/timer 1000)
(->> stream (->> stream
(rx/filter ms/key-up?) (rx/filter kbd/keyboard-event?)
(rx/filter kbd/key-up-event?)
(rx/delay 250)))) (rx/delay 250))))
(rx/take 1)) (rx/take 1))

View File

@ -15,7 +15,7 @@
[app.common.geom.shapes :as gsh] [app.common.geom.shapes :as gsh]
[app.common.math :as mth] [app.common.math :as mth]
[app.main.data.workspace.state-helpers :as wsh] [app.main.data.workspace.state-helpers :as wsh]
[app.main.streams :as ms] [app.util.mouse :as mse]
[beicon.core :as rx] [beicon.core :as rx]
[potok.core :as ptk])) [potok.core :as ptk]))
@ -155,7 +155,7 @@
(rx/concat (rx/concat
(rx/of #(-> % (assoc-in [:workspace-local :panning] true))) (rx/of #(-> % (assoc-in [:workspace-local :panning] true)))
(->> stream (->> stream
(rx/filter ms/pointer-event?) (rx/filter mse/pointer-event?)
(rx/filter #(= :delta (:source %))) (rx/filter #(= :delta (:source %)))
(rx/map :pt) (rx/map :pt)
(rx/take-until stopper) (rx/take-until stopper)

View File

@ -14,6 +14,7 @@
[app.common.geom.shapes :as gsh] [app.common.geom.shapes :as gsh]
[app.main.data.workspace.state-helpers :as wsh] [app.main.data.workspace.state-helpers :as wsh]
[app.main.streams :as ms] [app.main.streams :as ms]
[app.util.mouse :as mse]
[beicon.core :as rx] [beicon.core :as rx]
[potok.core :as ptk])) [potok.core :as ptk]))
@ -118,7 +119,7 @@
(rx/concat (rx/concat
(rx/of #(-> % (assoc-in [:workspace-local :zooming] true))) (rx/of #(-> % (assoc-in [:workspace-local :zooming] true)))
(->> stream (->> stream
(rx/filter ms/pointer-event?) (rx/filter mse/pointer-event?)
(rx/filter #(= :delta (:source %))) (rx/filter #(= :delta (:source %)))
(rx/map :pt) (rx/map :pt)
(rx/take-until stopper) (rx/take-until stopper)

View File

@ -11,94 +11,43 @@
[app.main.store :as st] [app.main.store :as st]
[app.util.globals :as globals] [app.util.globals :as globals]
[app.util.keyboard :as kbd] [app.util.keyboard :as kbd]
[app.util.mouse :as mse]
[beicon.core :as rx])) [beicon.core :as rx]))
;; --- User Events ;; --- 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? (defn interaction-event?
[event] [event]
(or (keyboard-event? event) (or ^boolean (kbd/keyboard-event? event)
(mouse-event? event))) ^boolean (mse/mouse-event? event)))
;; --- Derived streams ;; --- Derived streams
(defonce ^:private pointer
(->> st/stream
(rx/filter mse/pointer-event?)
(rx/share)))
(defonce mouse-position (defonce mouse-position
(let [sub (rx/behavior-subject nil) (let [sub (rx/behavior-subject nil)
ob (->> st/stream ob (->> pointer
(rx/filter pointer-event?) (rx/filter #(= :viewport (mse/get-pointer-source %)))
(rx/filter #(= :viewport (:source %))) (rx/map mse/get-pointer-position))]
(rx/map :pt))]
(rx/subscribe-with ob sub) (rx/subscribe-with ob sub)
sub)) sub))
(defonce mouse-position-ctrl (defonce mouse-position-ctrl
(let [sub (rx/behavior-subject nil) (let [sub (rx/behavior-subject nil)
ob (->> st/stream ob (->> pointer
(rx/filter pointer-event?) (rx/map mse/get-pointer-ctrl-mod)
(rx/map :ctrl)
(rx/dedupe))] (rx/dedupe))]
(rx/subscribe-with ob sub) (rx/subscribe-with ob sub)
sub)) sub))
(defonce mouse-position-meta (defonce mouse-position-meta
(let [sub (rx/behavior-subject nil) (let [sub (rx/behavior-subject nil)
ob (->> st/stream ob (->> pointer
(rx/filter pointer-event?) (rx/map mse/get-pointer-meta-mod)
(rx/map :meta)
(rx/dedupe))] (rx/dedupe))]
(rx/subscribe-with ob sub) (rx/subscribe-with ob sub)
sub)) sub))
@ -110,71 +59,68 @@
(defonce mouse-position-shift (defonce mouse-position-shift
(let [sub (rx/behavior-subject nil) (let [sub (rx/behavior-subject nil)
ob (->> st/stream ob (->> pointer
(rx/filter pointer-event?) (rx/map mse/get-pointer-shift-mod)
(rx/map :shift)
(rx/dedupe))] (rx/dedupe))]
(rx/subscribe-with ob sub) (rx/subscribe-with ob sub)
sub)) sub))
(defonce mouse-position-alt (defonce mouse-position-alt
(let [sub (rx/behavior-subject nil) (let [sub (rx/behavior-subject nil)
ob (->> st/stream ob (->> pointer
(rx/filter pointer-event?) (rx/map mse/get-pointer-alt-mod)
(rx/map :alt)
(rx/dedupe))] (rx/dedupe))]
(rx/subscribe-with ob sub) (rx/subscribe-with ob sub)
sub)) sub))
(defonce ^:private window-blur
(defonce window-blur
(->> (rx/from-event globals/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))) (rx/share)))
(defonce keyboard-alt (defonce keyboard-alt
(let [sub (rx/behavior-subject nil) (let [sub (rx/behavior-subject nil)
ob (->> (rx/merge ob (->> keyboard
(->> st/stream (rx/filter kbd/alt-key?)
(rx/filter keyboard-event?) (rx/map kbd/key-down-event?)
(rx/filter kbd/alt-key?) ;; Fix a situation caused by using `ctrl+alt` kind of
(rx/map #(= :down (:type %)))) ;; shortcuts, that makes keyboard-alt stream
;; Fix a situation caused by using `ctrl+alt` kind of shortcuts, ;; registering the key pressed but on blurring the
;; that makes keyboard-alt stream registering the key pressed but ;; window (unfocus) the key down is never arrived.
;; on blurring the window (unfocus) the key down is never arrived. (rx/merge window-blur)
(->> window-blur
(rx/map (constantly false))))
(rx/dedupe))] (rx/dedupe))]
(rx/subscribe-with ob sub) (rx/subscribe-with ob sub)
sub)) sub))
(defonce keyboard-ctrl (defonce keyboard-ctrl
(let [sub (rx/behavior-subject nil) (let [sub (rx/behavior-subject nil)
ob (->> (rx/merge ob (->> keyboard
(->> st/stream (rx/filter kbd/ctrl-key?)
(rx/filter keyboard-event?) (rx/map kbd/key-down-event?)
(rx/filter kbd/ctrl-key?) ;; Fix a situation caused by using `ctrl+alt` kind of
(rx/map #(= :down (:type %)))) ;; shortcuts, that makes keyboard-alt stream
;; Fix a situation caused by using `ctrl+alt` kind of shortcuts, ;; registering the key pressed but on blurring the
;; that makes keyboard-alt stream registering the key pressed but ;; window (unfocus) the key down is never arrived.
;; on blurring the window (unfocus) the key down is never arrived. (rx/merge window-blur)
(->> window-blur
(rx/map (constantly false))))
(rx/dedupe))] (rx/dedupe))]
(rx/subscribe-with ob sub) (rx/subscribe-with ob sub)
sub)) sub))
(defonce keyboard-meta (defonce keyboard-meta
(let [sub (rx/behavior-subject nil) (let [sub (rx/behavior-subject nil)
ob (->> (rx/merge ob (->> keyboard
(->> st/stream (rx/filter kbd/meta-key?)
(rx/filter keyboard-event?) (rx/map kbd/key-down-event?)
(rx/filter kbd/meta-key?) ;; Fix a situation caused by using `ctrl+alt` kind of
(rx/map #(= :down (:type %)))) ;; shortcuts, that makes keyboard-alt stream
;; Fix a situation caused by using `ctrl+alt` kind of shortcuts, ;; registering the key pressed but on blurring the
;; that makes keyboard-alt stream registering the key pressed but ;; window (unfocus) the key down is never arrived.
;; on blurring the window (unfocus) the key down is never arrived. (rx/merge window-blur)
(->> window-blur
(rx/map (constantly false))))
(rx/dedupe))] (rx/dedupe))]
(rx/subscribe-with ob sub) (rx/subscribe-with ob sub)
sub)) sub))
@ -186,33 +132,10 @@
(defonce keyboard-space (defonce keyboard-space
(let [sub (rx/behavior-subject nil) (let [sub (rx/behavior-subject nil)
ob (->> st/stream ob (->> keyboard
(rx/filter keyboard-event?)
(rx/filter kbd/space?) (rx/filter kbd/space?)
(rx/filter (comp not kbd/editing?)) (rx/filter (complement kbd/editing-event?))
(rx/map #(= :down (:type %))) (rx/map kbd/key-down-event?)
(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/dedupe))] (rx/dedupe))]
(rx/subscribe-with ob sub) (rx/subscribe-with ob sub)
sub)) sub))

View File

@ -348,34 +348,36 @@
state)) state))
(defn use-dynamic-grid-item-width (defn use-dynamic-grid-item-width
([] ([] (use-dynamic-grid-item-width nil))
(use-dynamic-grid-item-width nil))
([itemsize] ([itemsize]
(let [width (mf/use-state (:items-width @storage)) (let [;; NOTE: we pass a function to use-state for avoid repeatedly
rowref (mf/use-ref) ;; lookup `:items-width` on storage on each render
width* (mf/use-state #(:items-width @storage))
width (deref width*)
itemsize (cond rowref (mf/use-ref)
(some? itemsize) itemsize
(>= @width 1030) 280
:else 230)
ratio (if (some? @width) (/ @width itemsize) 0) itemsize (cond
nitems (mth/floor ratio) (some? itemsize) itemsize
limit (min 10 nitems) (>= width 1030) 280
limit (max 1 limit)] :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) (let [node (mf/ref-val rowref)
mnt? (volatile! true) mnt? (volatile! true)
sub (->> (wapi/observe-resize node) sub (->> (wapi/observe-resize node)
(rx/observe-on :af) (rx/observe-on :af)
(rx/subs (fn [entries] (rx/subs (fn [entries]
(let [row (first entries) (let [row (first entries)
row-rect (.-contentRect ^js row) row-rect (.-contentRect ^js row)
row-width (.-width ^js row-rect)] row-width (.-width ^js row-rect)]
(when @mnt? (when @mnt?
(reset! width row-width) (reset! width* row-width)
(swap! storage assoc :items-width row-width))))))] (swap! storage assoc :items-width row-width))))))]
(fn [] (fn []
(vreset! mnt? false) (vreset! mnt? false)

View File

@ -21,12 +21,12 @@
[app.main.data.workspace.specialized-panel :as-alias dwsp] [app.main.data.workspace.specialized-panel :as-alias dwsp]
[app.main.refs :as refs] [app.main.refs :as refs]
[app.main.store :as st] [app.main.store :as st]
[app.main.streams :as ms]
[app.main.ui.workspace.viewport.viewport-ref :as uwvv] [app.main.ui.workspace.viewport.viewport-ref :as uwvv]
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.dom.dnd :as dnd] [app.util.dom.dnd :as dnd]
[app.util.dom.normalize-wheel :as nw] [app.util.dom.normalize-wheel :as nw]
[app.util.keyboard :as kbd] [app.util.keyboard :as kbd]
[app.util.mouse :as mse]
[app.util.object :as obj] [app.util.object :as obj]
[app.util.timers :as timers] [app.util.timers :as timers]
[app.util.webapi :as wapi] [app.util.webapi :as wapi]
@ -85,7 +85,7 @@
left-click? left-click?
(do (do
(st/emit! (ms/->MouseEvent :down ctrl? shift? alt? meta?) (st/emit! (mse/->MouseEvent :down ctrl? shift? alt? meta?)
::dwsp/interrupt) ::dwsp/interrupt)
(when (and (not= edition id) (or text-editing? grid-editing?)) (when (and (not= edition id) (or text-editing? grid-editing?))
@ -173,7 +173,7 @@
hovering? (some? @hover) hovering? (some? @hover)
raw-pt (dom/get-client-position event) raw-pt (dom/get-client-position event)
pt (uwvv/point->viewport raw-pt)] 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? (when (and hovering?
(not @space?) (not @space?)
@ -213,7 +213,7 @@
grid-layout-id (->> @hover-ids reverse (d/seek (partial ctl/grid-layout? objects)))] 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 ;; Emit asynchronously so the double click to exit shapes won't break
(timers/schedule (timers/schedule
@ -283,7 +283,7 @@
middle-click? (= 2 (.-which event))] middle-click? (= 2 (.-which event))]
(when left-click? (when left-click?
(st/emit! (ms/->MouseEvent :up ctrl? shift? alt? meta?))) (st/emit! (mse/->MouseEvent :up ctrl? shift? alt? meta?)))
(when middle-click? (when middle-click?
(dom/prevent-default event) (dom/prevent-default event)
@ -314,29 +314,33 @@
shift? (kbd/shift? event) shift? (kbd/shift? event)
alt? (kbd/alt? event) alt? (kbd/alt? event)
meta? (kbd/meta? event) meta? (kbd/meta? event)
mod? (kbd/mod? event)
target (dom/get-target event) target (dom/get-target event)
editing? (or (some? (.closest ^js target ".public-DraftEditor-content")) editing? (or (some? (.closest ^js target ".public-DraftEditor-content"))
(= "rich-text" (obj/get target "className")) (= "rich-text" (obj/get target "className"))
(= "INPUT" (obj/get target "tagName")) (= "INPUT" (obj/get target "tagName"))
(= "TEXTAREA" (obj/get target "tagName")))] (= "TEXTAREA" (obj/get target "tagName")))]
(when-not (.-repeat bevent) (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 [] (defn on-key-up []
(mf/use-callback (mf/use-callback
(fn [event] (fn [event]
(let [key (.-key event) (let [key (.-key event)
ctrl? (kbd/ctrl? event) ctrl? (kbd/ctrl? event)
shift? (kbd/shift? event) shift? (kbd/shift? event)
alt? (kbd/alt? event) alt? (kbd/alt? event)
meta? (kbd/meta? event) meta? (kbd/meta? event)
mod? (kbd/mod? event)
target (dom/get-target event) target (dom/get-target event)
editing? (or (some? (.closest ^js target ".public-DraftEditor-content")) editing? (or (some? (.closest ^js target ".public-DraftEditor-content"))
(= "rich-text" (obj/get target "className")) (= "rich-text" (obj/get target "className"))
(= "INPUT" (obj/get target "tagName")) (= "INPUT" (obj/get target "tagName"))
(= "TEXTAREA" (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] (defn on-pointer-move [move-stream]
(let [last-position (mf/use-var nil)] (let [last-position (mf/use-var nil)]
@ -353,16 +357,16 @@
(rx/push! move-stream pt) (rx/push! move-stream pt)
(reset! last-position raw-pt) (reset! last-position raw-pt)
(st/emit! (ms/->PointerEvent :delta delta (st/emit! (mse/->PointerEvent :delta delta
(kbd/ctrl? event) (kbd/ctrl? event)
(kbd/shift? event) (kbd/shift? event)
(kbd/alt? event) (kbd/alt? event)
(kbd/meta? event))) (kbd/meta? event)))
(st/emit! (ms/->PointerEvent :viewport pt (st/emit! (mse/->PointerEvent :viewport pt
(kbd/ctrl? event) (kbd/ctrl? event)
(kbd/shift? event) (kbd/shift? event)
(kbd/alt? event) (kbd/alt? event)
(kbd/meta? event)))))))) (kbd/meta? event))))))))
(defn on-mouse-wheel [zoom] (defn on-mouse-wheel [zoom]
(mf/use-callback (mf/use-callback

View File

@ -15,8 +15,8 @@
[app.main.data.workspace.colors :as dc] [app.main.data.workspace.colors :as dc]
[app.main.refs :as refs] [app.main.refs :as refs]
[app.main.store :as st] [app.main.store :as st]
[app.main.streams :as ms]
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.mouse :as mse]
[beicon.core :as rx] [beicon.core :as rx]
[cuerdas.core :as str] [cuerdas.core :as str]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
@ -154,14 +154,14 @@
(mf/deps @moving-point from-p to-p width-p) (mf/deps @moving-point from-p to-p width-p)
(fn [] (fn []
(let [subs (->> st/stream (let [subs (->> st/stream
(rx/filter ms/pointer-event?) (rx/filter mse/pointer-event?)
(rx/filter #(= :viewport (:source %))) (rx/filter #(= :viewport (mse/get-pointer-source %)))
(rx/map :pt) (rx/map mse/get-pointer-position)
(rx/subs (rx/subs
(fn [pt] (fn [pt]
(case @moving-point (case @moving-point
:from-p (when on-change-start (on-change-start pt)) :from-p (when on-change-start (on-change-start pt))
:to-p (when on-change-finish (on-change-finish pt)) :to-p (when on-change-finish (on-change-finish pt))
:width-p (when on-change-width :width-p (when on-change-width
(let [width-v (gpt/unit (gpt/to-vec from-p width-p)) (let [width-v (gpt/unit (gpt/to-vec from-p width-p))
distance (gpt/point-line-distance pt from-p to-p) distance (gpt/point-line-distance pt from-p to-p)

View File

@ -31,6 +31,7 @@
[app.util.debug :as dbg] [app.util.debug :as dbg]
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.globals :as globals] [app.util.globals :as globals]
[app.util.keyboard :as kbd]
[beicon.core :as rx] [beicon.core :as rx]
[goog.events :as events] [goog.events :as events]
[rumext.v2 :as mf]) [rumext.v2 :as mf])
@ -99,14 +100,57 @@
(when (not= @cursor new-cursor) (when (not= @cursor new-cursor)
(reset! cursor new-cursor)))))) (reset! cursor new-cursor))))))
(defn setup-keyboard [alt? mod? space? z? shift?] (defn setup-keyboard
(hooks/use-stream ms/keyboard-alt #(reset! alt? %)) [alt* mod* space* z* shift*]
(hooks/use-stream ms/keyboard-mod #(do (let [kbd-zoom-s
(reset! mod? %) (mf/with-memo []
(when-not % (reset! z? false)))) ;; In mac after command+z there is no event for the release of the z key (->> ms/keyboard
(hooks/use-stream ms/keyboard-space #(reset! space? %)) (rx/filter kbd/key-down-event?)
(hooks/use-stream ms/keyboard-z #(reset! z? %)) (rx/filter kbd/mod-event?)
(hooks/use-stream ms/keyboard-shift #(reset! shift? %))) (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? (defn group-empty-space?
"Given a group `group-id` check if `hover-ids` contains any of its children. If it doesn't means "Given a group `group-id` check if `hover-ids` contains any of its children. If it doesn't means

View File

@ -9,15 +9,44 @@
[app.config :as cfg] [app.config :as cfg]
[cuerdas.core :as str])) [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? (defn is-key?
[^string key] [^string key]
(fn [^js e] (fn [^KeyboardEvent e]
(= (.-key e) key))) (= (.-key e) key)))
(defn is-key-ignore-case? (defn is-key-ignore-case?
[^string key] [^string key]
(fn [^js e] (let [key (str/upper key)]
(= (str/upper (.-key e)) (str/upper key)))) (fn [^KeyboardEvent e]
(= (str/upper (.-key e)) key))))
(defn ^boolean alt? (defn ^boolean alt?
[^js event] [^js event]
@ -45,6 +74,10 @@
(def enter? (is-key? "Enter")) (def enter? (is-key? "Enter"))
(def space? (is-key? " ")) (def space? (is-key? " "))
(def z? (is-key-ignore-case? "z")) (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 up-arrow? (is-key? "ArrowUp"))
(def down-arrow? (is-key? "ArrowDown")) (def down-arrow? (is-key? "ArrowDown"))
(def left-arrow? (is-key? "ArrowLeft")) (def left-arrow? (is-key? "ArrowLeft"))
@ -58,6 +91,3 @@
(def home? (is-key? "Home")) (def home? (is-key? "Home"))
(def tab? (is-key? "Tab")) (def tab? (is-key? "Tab"))
(defn editing? [e]
(.-editing ^js e))

View File

@ -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))