diff --git a/frontend/src/app/main/streams.cljs b/frontend/src/app/main/streams.cljs index b8a8e952a..3529d2776 100644 --- a/frontend/src/app/main/streams.cljs +++ b/frontend/src/app/main/streams.cljs @@ -15,7 +15,7 @@ ;; --- User Events -(defrecord KeyboardEvent [type key shift ctrl alt]) +(defrecord KeyboardEvent [type key shift ctrl alt meta]) (defn keyboard-event? [v] @@ -120,6 +120,21 @@ (rx/map (constantly false)))) (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/map :ctrl)) + ;; Fix a situation caused by using `ctrl+alt` kind of shortcuts, + ;; that makes keyboard-alt stream registring the key pressed but + ;; on bluring the window (unfocus) the key down is never arrived. + (->> window-blur + (rx/map (constantly false)))) + (rx/dedupe))] + (rx/subscribe-with ob sub) sub)) (defn mouse-position-deltas diff --git a/frontend/src/app/main/ui/workspace/effects.cljs b/frontend/src/app/main/ui/workspace/effects.cljs index a0ae1fd3c..84a69c299 100644 --- a/frontend/src/app/main/ui/workspace/effects.cljs +++ b/frontend/src/app/main/ui/workspace/effects.cljs @@ -52,11 +52,9 @@ drawing? @refs/selected-drawing-tool button (.-which (.-nativeEvent event)) shift? (kbd/shift? event) - ctrl? (or (kbd/ctrl? event) (kbd/meta? event)) allow-click? (and (not blocked) (not drawing?) - (not ctrl?) (not edition))] (when (and (= button 1) allow-click?) diff --git a/frontend/src/app/main/ui/workspace/selection.cljs b/frontend/src/app/main/ui/workspace/selection.cljs index ddfb7b0b1..6a395336d 100644 --- a/frontend/src/app/main/ui/workspace/selection.cljs +++ b/frontend/src/app/main/ui/workspace/selection.cljs @@ -10,29 +10,30 @@ (ns app.main.ui.workspace.selection "Selection handlers component." (:require - [beicon.core :as rx] - [cuerdas.core :as str] - [potok.core :as ptk] - [rumext.alpha :as mf] - [rumext.util :refer [map->obj]] + [app.common.geom.matrix :as gmt] + [app.common.geom.point :as gpt] + [app.common.geom.shapes :as geom] + [app.common.math :as mth] [app.common.uuid :as uuid] - [app.util.data :as d] [app.main.data.workspace :as dw] [app.main.data.workspace.common :as dwc] [app.main.refs :as refs] [app.main.store :as st] [app.main.streams :as ms] [app.main.ui.cursors :as cur] - [app.common.math :as mth] + [app.main.ui.hooks :as hooks] + [app.main.ui.measurements :as msr] + [app.main.ui.workspace.shapes.outline :refer [outline]] + [app.main.ui.workspace.shapes.path.editor :refer [path-editor]] + [app.util.data :as d] + [app.util.debug :refer [debug?]] [app.util.dom :as dom] [app.util.object :as obj] - [app.common.geom.shapes :as geom] - [app.common.geom.point :as gpt] - [app.common.geom.matrix :as gmt] - [app.util.debug :refer [debug?]] - [app.main.ui.workspace.shapes.outline :refer [outline]] - [app.main.ui.measurements :as msr] - [app.main.ui.workspace.shapes.path.editor :refer [path-editor]])) + [beicon.core :as rx] + [cuerdas.core :as str] + [potok.core :as ptk] + [rumext.alpha :as mf] + [rumext.util :refer [map->obj]])) (def rotation-handler-size 20) (def resize-point-radius 4) @@ -235,19 +236,22 @@ (mf/defc controls {::mf/wrap-props false} [props] - (let [{:keys [overflow-text] :as shape} (obj/get props "shape") + (let [{:keys [overflow-text type] :as shape} (obj/get props "shape") zoom (obj/get props "zoom") color (obj/get props "color") on-resize (obj/get props "on-resize") on-rotate (obj/get props "on-rotate") current-transform (mf/deref refs/current-transform) + hide? (mf/use-state false) selrect (-> (:selrect shape) minimum-selrect) transform (geom/transform-matrix shape {:no-flip true})] + (hooks/use-stream ms/keyboard-ctrl #(when (= type :group) (reset! hide? %))) + (when (not (#{:move :rotate} current-transform)) - [:g.controls + [:g.controls {:style {:display (when @hide? "none")}} ;; Selection rect [:& selection-rect {:rect selrect diff --git a/frontend/src/app/main/ui/workspace/shapes/group.cljs b/frontend/src/app/main/ui/workspace/shapes/group.cljs index f3f4cc3c5..bb8ac5c04 100644 --- a/frontend/src/app/main/ui/workspace/shapes/group.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/group.cljs @@ -9,17 +9,18 @@ (ns app.main.ui.workspace.shapes.group (:require + [app.common.geom.shapes :as gsh] [app.main.data.workspace :as dw] [app.main.refs :as refs] [app.main.store :as st] [app.main.streams :as ms] + [app.main.ui.hooks :as hooks] [app.main.ui.shapes.group :as group] [app.main.ui.shapes.shape :refer [shape-container]] [app.main.ui.workspace.effects :as we] + [app.util.debug :refer [debug?]] [app.util.dom :as dom] - [rumext.alpha :as mf] - [app.common.geom.shapes :as gsh] - [app.util.debug :refer [debug?]])) + [rumext.alpha :as mf])) (defn use-double-click [{:keys [id]}] (mf/use-callback @@ -40,8 +41,10 @@ frame (unchecked-get props "frame") {:keys [id x y width height]} shape - transform (gsh/transform-matrix shape) + transform (gsh/transform-matrix shape) + + ctrl? (mf/use-state false) childs-ref (mf/use-memo (mf/deps shape) #(refs/objects-by-id (:shapes shape))) childs (mf/deref childs-ref) @@ -59,33 +62,38 @@ is-mask-selected? (mf/deref is-mask-selected-ref) + expand-mask? is-child-selected? + group-interactions? (not (or @ctrl? is-child-selected?)) + handle-mouse-down (we/use-mouse-down shape) handle-context-menu (we/use-context-menu shape) handle-pointer-enter (we/use-pointer-enter shape) handle-pointer-leave (we/use-pointer-leave shape) handle-double-click (use-double-click shape)] + (hooks/use-stream ms/keyboard-ctrl #(reset! ctrl? %)) + [:> shape-container {:shape shape} [:g.group-shape [:& group-shape {:frame frame :shape shape :childs childs - :expand-mask is-mask-selected? - :pointer-events (when (not is-child-selected?) "none")}] + :expand-mask expand-mask? + :pointer-events (when group-interactions? "none")}] - (when-not is-child-selected? - [:rect.group-actions - {:x x - :y y - :fill (if (debug? :group) "red" "transparent") - :opacity 0.5 - :transform transform - :width width - :height height - :on-mouse-down handle-mouse-down - :on-context-menu handle-context-menu - :on-pointer-over handle-pointer-enter - :on-pointer-out handle-pointer-leave - :on-double-click handle-double-click}])]])))) + [:rect.group-actions + {:x x + :y y + :width width + :height height + :transform transform + :style {:pointer-events (when-not group-interactions? "none") + :fill (if (debug? :group) "red" "transparent") + :opacity 0.5} + :on-mouse-down handle-mouse-down + :on-context-menu handle-context-menu + :on-pointer-over handle-pointer-enter + :on-pointer-out handle-pointer-leave + :on-double-click handle-double-click}]]])))) diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index 0d7f2d6af..b5d79a0db 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -219,11 +219,17 @@ (not (:blocked shape)) (not= edition (:id shape)) (outline? (:id shape)))) - shapes (->> (vals objects) (filter show-outline?)) + + remove-groups? (mf/use-state false) + + shapes (cond->> (vals objects) + show-outline? (filter show-outline?) + @remove-groups? (remove #(= :group (:type %)))) transform (mf/deref refs/current-transform) color (if (or (> (count shapes) 1) (nil? (:shape-ref (first shapes)))) "#31EFB8" "#00E0FF")] + (hooks/use-stream ms/keyboard-ctrl #(reset! remove-groups? %)) (when (nil? transform) [:g.outlines (for [shape shapes] @@ -424,16 +430,16 @@ (mf/use-callback (fn [event] (let [target (dom/get-target event)] - ; Capture mouse pointer to detect the movements even if cursor - ; leaves the viewport or the browser itself - ; https://developer.mozilla.org/en-US/docs/Web/API/Element/setPointerCapture + ; Capture mouse pointer to detect the movements even if cursor + ; leaves the viewport or the browser itself + ; https://developer.mozilla.org/en-US/docs/Web/API/Element/setPointerCapture (.setPointerCapture target (.-pointerId event))))) on-pointer-up (mf/use-callback (fn [event] (let [target (dom/get-target event)] - ; Release pointer on mouse up + ; Release pointer on mouse up (.releasePointerCapture target (.-pointerId event))))) on-click @@ -442,9 +448,7 @@ (let [ctrl? (kbd/ctrl? event) shift? (kbd/shift? event) alt? (kbd/alt? event)] - (if ctrl? - (st/emit! (dw/select-last-layer @ms/mouse-position)) - (st/emit! (ms/->MouseEvent :click ctrl? shift? alt?)))))) + (st/emit! (ms/->MouseEvent :click ctrl? shift? alt?))))) on-double-click (mf/use-callback @@ -460,14 +464,15 @@ (mf/use-callback (fn [event] (let [bevent (.getBrowserEvent ^js event) - key (.-keyCode ^js event) - ctrl? (kbd/ctrl? event) + key (.-keyCode ^js event) + ctrl? (kbd/ctrl? event) shift? (kbd/shift? event) - alt? (kbd/alt? event) + alt? (kbd/alt? event) + meta? (kbd/meta? event) target (dom/get-target event)] (when-not (.-repeat bevent) - (st/emit! (ms/->KeyboardEvent :down key ctrl? shift? alt?)) + (st/emit! (ms/->KeyboardEvent :down key shift? ctrl? alt? meta?)) (when (and (kbd/space? event) (not= "rich-text" (obj/get target "className")) (not= "INPUT" (obj/get target "tagName")) @@ -477,13 +482,14 @@ on-key-up (mf/use-callback (fn [event] - (let [key (.-keyCode event) - ctrl? (kbd/ctrl? event) + (let [key (.-keyCode event) + ctrl? (kbd/ctrl? event) shift? (kbd/shift? event) - alt? (kbd/alt? event)] + alt? (kbd/alt? event) + meta? (kbd/meta? event)] (when (kbd/space? event) (st/emit! dw/finish-pan ::finish-positioning)) - (st/emit! (ms/->KeyboardEvent :up key ctrl? shift? alt?))))) + (st/emit! (ms/->KeyboardEvent :up key shift? ctrl? alt? meta?))))) translate-point-to-viewport (mf/use-callback