diff --git a/frontend/src/app/main/data/workspace/drawing.cljs b/frontend/src/app/main/data/workspace/drawing.cljs index 1b1d96beee..a3b77f82ed 100644 --- a/frontend/src/app/main/data/workspace/drawing.cljs +++ b/frontend/src/app/main/data/workspace/drawing.cljs @@ -96,7 +96,7 @@ (watch [_ _ _] (rx/of (case type - :path (path/handle-new-shape) + :path (path/handle-drawing) :curve (curve/handle-drawing) (box/handle-drawing type)))))) diff --git a/frontend/src/app/main/data/workspace/path.cljs b/frontend/src/app/main/data/workspace/path.cljs index 13009db9c3..0fedbd5b57 100644 --- a/frontend/src/app/main/data/workspace/path.cljs +++ b/frontend/src/app/main/data/workspace/path.cljs @@ -14,7 +14,7 @@ [app.main.data.workspace.path.undo :as undo])) ;; Drawing -(dm/export drawing/handle-new-shape) +(dm/export drawing/handle-drawing) (dm/export drawing/start-path-from-point) (dm/export drawing/close-path-drag-start) (dm/export drawing/change-edit-mode) diff --git a/frontend/src/app/main/data/workspace/path/common.cljs b/frontend/src/app/main/data/workspace/path/common.cljs index 3bb60ed721..b90762790b 100644 --- a/frontend/src/app/main/data/workspace/path/common.cljs +++ b/frontend/src/app/main/data/workspace/path/common.cljs @@ -11,7 +11,7 @@ [potok.v2.core :as ptk])) (defn init-path [] - (ptk/reify ::init-path)) + (ptk/data-event ::init-path {})) (defn clean-edit-state [state] diff --git a/frontend/src/app/main/data/workspace/path/drawing.cljs b/frontend/src/app/main/data/workspace/path/drawing.cljs index 7d9eb3cdb1..b85ac2edd7 100644 --- a/frontend/src/app/main/data/workspace/path/drawing.cljs +++ b/frontend/src/app/main/data/workspace/path/drawing.cljs @@ -6,6 +6,7 @@ (ns app.main.data.workspace.path.drawing (:require + [app.common.data :as d] [app.common.data.macros :as dm] [app.common.geom.point :as gpt] [app.common.geom.shapes.flex-layout :as gsl] @@ -32,19 +33,23 @@ (declare change-edit-mode) -(defn preview-next-point [{:keys [x y shift?]}] +(defn preview-next-point + [{:keys [x y shift?]}] (ptk/reify ::preview-next-point ptk/UpdateEvent (update [_ state] - (let [id (st/get-path-id state) + (let [id (st/get-path-id state) fix-angle? shift? last-point (get-in state [:workspace-local :edit-path id :last-point]) - position (cond-> (gpt/point x y) - fix-angle? (path.helpers/position-fixed-angle last-point)) - shape (st/get-path state) - {:keys [last-point prev-handler]} (get-in state [:workspace-local :edit-path id]) - command (path.segment/next-node shape position last-point prev-handler)] - (assoc-in state [:workspace-local :edit-path id :preview] command))))) + position (cond-> (gpt/point x y) + fix-angle? (path.helpers/position-fixed-angle last-point)) + shape (st/get-path state) + + {:keys [last-point prev-handler]} + (get-in state [:workspace-local :edit-path id]) + + segment (path.segment/next-node shape position last-point prev-handler)] + (assoc-in state [:workspace-local :edit-path id :preview] segment))))) (defn add-node [{:keys [x y shift?]}] @@ -189,18 +194,18 @@ (defn make-drag-stream [state stream down-event] - (dm/assert! - "should be a pointer" - (gpt/point? down-event)) + (assert (gpt/point? down-event) + "should be a point instance") (let [stopper (rx/merge (mse/drag-stopper stream) (->> stream (rx/filter helpers/end-path-event?))) - drag-events (->> (streams/position-stream state) - (rx/map #(drag-handler %)) - (rx/take-until stopper))] + drag-events + (->> (streams/position-stream state) + (rx/map #(drag-handler %)) + (rx/take-until stopper))] (rx/concat (rx/of (add-node down-event)) (streams/drag-stream @@ -208,9 +213,9 @@ drag-events (rx/of (finish-drag))))))) -(defn handle-drawing +(defn- start-edition [_id] - (ptk/reify ::handle-drawing + (ptk/reify ::start-edition ptk/UpdateEvent (update [_ state] (let [id (st/get-path-id state)] @@ -218,36 +223,49 @@ ptk/WatchEvent (watch [_ state stream] - (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?)) + (let [mouse-down + (->> stream + (rx/filter mse/mouse-event?) + (rx/filter mse/mouse-down-event?)) + + end-stream + (->> stream + (rx/filter helpers/end-path-event?) + (rx/share)) + + stoper-stream + (->> stream + (rx/filter (ptk/type? ::start-edition)) + (rx/merge end-stream)) ;; Mouse move preview mousemove-events (->> (streams/position-stream state) - (rx/take-until end-path-events) - (rx/map #(preview-next-point %))) + (rx/map #(preview-next-point %)) + (rx/take-until end-stream)) ;; From mouse down we can have: click, drag and double click mousedown-events (->> mouse-down - (rx/take-until end-path-events) ;; We just ignore the mouse event and stream down the ;; last position event (rx/with-latest-from #(-> %2) (streams/position-stream state)) ;; We change to the stream that emits the first event (rx/switch-map #(rx/race (make-node-events-stream stream) - (make-drag-stream state stream %))))] + (make-drag-stream state stream %))) + (rx/take-until end-stream))] (rx/concat (rx/of (undo/start-path-undo)) (rx/of (common/init-path)) - (rx/merge mousemove-events - mousedown-events) - (rx/of (common/finish-path))))))) + (->> (rx/merge mousemove-events + mousedown-events) + (rx/take-until stoper-stream)) + + ;; This step implicitly finish path + (rx/of (common/finish-path) + (change-edit-mode :draw))))))) (defn setup-frame [] (ptk/reify ::setup-frame @@ -275,9 +293,9 @@ (cond-> (some? drop-index) (with-meta {:index drop-index}))))))))) -(defn handle-new-shape-result +(defn- handle-drawing-end [shape-id] - (ptk/reify ::handle-new-shape-result + (ptk/reify ::handle-drawing-end ptk/UpdateEvent (update [_ state] (let [content (dm/get-in state [:workspace-drawing :object :content])] @@ -299,8 +317,8 @@ (change-edit-mode :draw)) (rx/of (dwdc/handle-finish-drawing))))))) -(defn handle-new-shape - "Creates a new path shape" +(defn handle-drawing + "Hanndle the start of drawing new path shape" [] (ptk/reify ::handle-new-shape ptk/UpdateEvent @@ -312,12 +330,12 @@ (watch [_ state stream] (let [shape-id (dm/get-in state [:workspace-drawing :object :id])] (rx/concat - (rx/of (handle-drawing shape-id)) + (rx/of (start-edition shape-id)) (->> stream (rx/filter (ptk/type? ::common/finish-path)) (rx/take 1) (rx/observe-on :async) - (rx/map #(handle-new-shape-result shape-id)))))))) + (rx/map (partial handle-drawing-end shape-id)))))))) (declare check-changed-content) @@ -339,7 +357,7 @@ (if (= :draw edit-mode) (rx/concat (rx/of (dwsh/update-shapes [id] path/convert-to-path)) - (rx/of (handle-drawing id)) + (rx/of (start-edition id)) (->> stream (rx/filter (ptk/type? ::common/finish-path)) (rx/take 1) @@ -367,17 +385,18 @@ (ptk/reify ::change-edit-mode ptk/UpdateEvent (update [_ state] - (let [id (get-in state [:workspace-local :edition])] - (cond-> state - id (assoc-in [:workspace-local :edit-path id :edit-mode] mode)))) + (if-let [id (dm/get-in state [:workspace-local :edition])] + (d/update-in-when state [:workspace-local :edit-path id] assoc :edit-mode mode) + state)) ptk/WatchEvent (watch [_ state _] - (let [id (st/get-path-id state)] - (cond - (and id (= :move mode)) (rx/of (common/finish-path)) - (and id (= :draw mode)) (rx/of (start-draw-mode)) - :else (rx/empty)))))) + (when-let [id (dm/get-in state [:workspace-local :edition])] + (let [mode (dm/get-in state [:workspace-local :edit-path id :edit-mode])] + (case mode + :move (rx/of (common/finish-path)) + :draw (rx/of (start-draw-mode)) + (rx/empty))))))) (defn reset-last-handler [] @@ -385,6 +404,5 @@ ptk/UpdateEvent (update [_ state] (let [id (st/get-path-id state)] - (-> state - (assoc-in [:workspace-local :edit-path id :prev-handler] nil)))))) + (assoc-in state [:workspace-local :edit-path id :prev-handler] nil))))) diff --git a/frontend/src/app/main/data/workspace/path/state.cljs b/frontend/src/app/main/data/workspace/path/state.cljs index efe34a0044..6a89ed127a 100644 --- a/frontend/src/app/main/data/workspace/path/state.cljs +++ b/frontend/src/app/main/data/workspace/path/state.cljs @@ -7,42 +7,8 @@ (ns app.main.data.workspace.path.state (:require [app.common.data.macros :as dm] - [app.common.files.helpers :as cph] [app.common.types.path.shape-to-path :as stp])) -(defn path-editing? - "Returns true if we're editing a path or creating a new one." - [{local :workspace-local - drawing :workspace-drawing}] - (let [selected (:selected local) - edition (:edition local) - - drawing-obj (:object drawing) - drawing-tool (:tool drawing) - - edit-path? (dm/get-in local [:edit-path edition]) - - shape (or drawing-obj (first selected)) - shape-id (:id shape) - - single? (= (count selected) 1) - editing? (and (some? shape-id) - (some? edition) - (= shape-id edition)) - - ;; we need to check if we're drawing a new object but we're - ;; not using the pencil tool. - draw-path? (and (some? drawing-obj) - (cph/path-shape? drawing-obj) - (not= :curve drawing-tool))] - - (or (and ^boolean single? - ^boolean editing? - (and (not (cph/text-shape? shape)) - (not (cph/frame-shape? shape)))) - draw-path? - edit-path?))) - (defn get-path-id "Retrieves the currently editing path id" [state] diff --git a/frontend/src/app/main/data/workspace/viewport.cljs b/frontend/src/app/main/data/workspace/viewport.cljs index 97e88bc29a..d9f523003a 100644 --- a/frontend/src/app/main/data/workspace/viewport.cljs +++ b/frontend/src/app/main/data/workspace/viewport.cljs @@ -22,9 +22,8 @@ (defn initialize-viewport [{:keys [width height] :as size}] - (dm/assert! - "expected `size` to be a rect instance" - (gpr/rect? size)) + (assert (gpr/rect? size) + "expected `size` to be a rect instance") (letfn [(update* [{:keys [vport] :as local}] (let [wprop (/ (:width vport) width) diff --git a/frontend/src/app/main/ui/workspace/shapes/path/editor.cljs b/frontend/src/app/main/ui/workspace/shapes/path/editor.cljs index dede7d3315..f6d308183b 100644 --- a/frontend/src/app/main/ui/workspace/shapes/path/editor.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/path/editor.cljs @@ -8,13 +8,11 @@ (:require [app.common.data :as d] [app.common.data.macros :as dm] - [app.common.files.helpers :as cfh] [app.common.geom.point :as gpt] [app.common.types.path :as path] [app.common.types.path.helpers :as path.helpers] [app.common.types.path.segment :as path.segment] [app.main.data.workspace.path :as drp] - [app.main.refs :as refs] [app.main.snap :as snap] [app.main.store :as st] [app.main.streams :as ms] @@ -24,7 +22,6 @@ [app.util.keyboard :as kbd] [clojure.set :refer [map-invert]] [goog.events :as events] - [okulary.core :as l] [rumext.v2 :as mf])) (def point-radius 5) @@ -261,18 +258,9 @@ angle (gpt/angle-with-other v1 v2)] (<= (- 180 angle) 0.1)))) -(defn- make-edit-path-ref [id] - (let [get-fn #(dm/get-in % [:edit-path id])] - (l/derived get-fn refs/workspace-local))) - (mf/defc path-editor* - [{:keys [shape zoom]}] - - (let [shape-id (dm/get-prop shape :id) - edit-path-ref (mf/with-memo [shape-id] - (make-edit-path-ref shape-id)) - - hover-point (mf/use-state nil) + [{:keys [shape zoom state]}] + (let [hover-point (mf/use-state nil) editor-ref (mf/use-ref nil) {:keys [edit-mode @@ -286,19 +274,12 @@ moving-handler hover-handlers hover-points - snap-toggled] - :as edit-path} - (mf/deref edit-path-ref) + snap-toggled]} + state selected-points (or selected-points #{}) - shape - (mf/with-memo [shape] - (cond-> shape - (not (cfh/path-shape? shape)) - (path/convert-to-path))) - base-content (get shape :content) diff --git a/frontend/src/app/main/ui/workspace/top_toolbar.cljs b/frontend/src/app/main/ui/workspace/top_toolbar.cljs index a904761f69..5d8abacdad 100644 --- a/frontend/src/app/main/ui/workspace/top_toolbar.cljs +++ b/frontend/src/app/main/ui/workspace/top_toolbar.cljs @@ -7,14 +7,12 @@ (ns app.main.ui.workspace.top-toolbar (:require-macros [app.main.style :as stl]) (:require - [app.common.data.macros :as dm] [app.common.geom.point :as gpt] [app.main.data.event :as ev] [app.main.data.modal :as modal] [app.main.data.workspace :as dw] [app.main.data.workspace.common :as dwc] [app.main.data.workspace.media :as dwm] - [app.main.data.workspace.path.state :as pst] [app.main.data.workspace.shortcuts :as sc] [app.main.features :as features] [app.main.refs :as refs] @@ -68,26 +66,31 @@ :ref ref :on-selected on-selected}]]])) -(def toolbar-hidden - (l/derived - (fn [state] - (let [visibility (dm/get-in state [:workspace-local :hide-toolbar]) - editing? (pst/path-editing? state) - hidden? (if editing? true visibility)] - hidden?)) - st/state)) +(def ^:private toolbar-hidden-ref + (l/derived (fn [state] + (let [visibility (get state :hide-toolbar) + path-edit-state (get state :edit-path) -(mf/defc top-toolbar - {::mf/wrap [mf/memo] - ::mf/wrap-props false} + selected (get state :selected) + edition (get state :edition) + single? (= (count selected) 1) + + path-editing? (and single? (some? (get path-edit-state edition)))] + (if path-editing? true visibility))) + refs/workspace-local)) + +(mf/defc top-toolbar* + {::mf/memo true} [{:keys [layout]}] - (let [selected-drawtool (mf/deref refs/selected-drawing-tool) - edition (mf/deref refs/selected-edition) + (let [drawtool (mf/deref refs/selected-drawing-tool) + edition (mf/deref refs/selected-edition) - read-only? (mf/use-ctx ctx/workspace-read-only?) + profile (mf/deref refs/profile) + props (get profile :props) - rulers? (mf/deref refs/rulers?) - hide-toolbar? (mf/deref toolbar-hidden) + read-only? (mf/use-ctx ctx/workspace-read-only?) + rulers? (mf/deref refs/rulers?) + hide-toolbar? (mf/deref toolbar-hidden-ref) interrupt (mf/use-fn #(st/emit! :interrupt (dw/clear-edition-mode))) @@ -121,8 +124,6 @@ (dom/blur! (dom/get-target event)) (st/emit! (dwc/toggle-toolbar-visibility)))) - profile (mf/deref refs/profile) - props (get profile :props) test-tooltip-board-text (if (not (:workspace-visited props)) (tr "workspace.toolbar.frame-first-time" (sc/get-tooltip :draw-frame)) @@ -139,7 +140,7 @@ {:title (tr "workspace.toolbar.move" (sc/get-tooltip :move)) :aria-label (tr "workspace.toolbar.move" (sc/get-tooltip :move)) :class (stl/css-case :main-toolbar-options-button true - :selected (and (nil? selected-drawtool) + :selected (and (nil? drawtool) (not edition))) :on-click interrupt} i/move]] @@ -148,7 +149,7 @@ [:button {:title test-tooltip-board-text :aria-label (tr "workspace.toolbar.frame" (sc/get-tooltip :draw-frame)) - :class (stl/css-case :main-toolbar-options-button true :selected (= selected-drawtool :frame)) + :class (stl/css-case :main-toolbar-options-button true :selected (= drawtool :frame)) :on-click select-drawtool :data-tool "frame" :data-testid "artboard-btn"} @@ -157,7 +158,7 @@ [:button {:title (tr "workspace.toolbar.rect" (sc/get-tooltip :draw-rect)) :aria-label (tr "workspace.toolbar.rect" (sc/get-tooltip :draw-rect)) - :class (stl/css-case :main-toolbar-options-button true :selected (= selected-drawtool :rect)) + :class (stl/css-case :main-toolbar-options-button true :selected (= drawtool :rect)) :on-click select-drawtool :data-tool "rect" :data-testid "rect-btn"} @@ -166,7 +167,7 @@ [:button {:title (tr "workspace.toolbar.ellipse" (sc/get-tooltip :draw-ellipse)) :aria-label (tr "workspace.toolbar.ellipse" (sc/get-tooltip :draw-ellipse)) - :class (stl/css-case :main-toolbar-options-button true :selected (= selected-drawtool :circle)) + :class (stl/css-case :main-toolbar-options-button true :selected (= drawtool :circle)) :on-click select-drawtool :data-tool "circle" :data-testid "ellipse-btn"} @@ -175,7 +176,7 @@ [:button {:title (tr "workspace.toolbar.text" (sc/get-tooltip :draw-text)) :aria-label (tr "workspace.toolbar.text" (sc/get-tooltip :draw-text)) - :class (stl/css-case :main-toolbar-options-button true :selected (= selected-drawtool :text)) + :class (stl/css-case :main-toolbar-options-button true :selected (= drawtool :text)) :on-click select-drawtool :data-tool "text"} i/text]] @@ -186,7 +187,7 @@ [:button {:title (tr "workspace.toolbar.curve" (sc/get-tooltip :draw-curve)) :aria-label (tr "workspace.toolbar.curve" (sc/get-tooltip :draw-curve)) - :class (stl/css-case :main-toolbar-options-button true :selected (= selected-drawtool :curve)) + :class (stl/css-case :main-toolbar-options-button true :selected (= drawtool :curve)) :on-click select-drawtool :data-tool "curve" :data-testid "curve-btn"} @@ -195,7 +196,7 @@ [:button {:title (tr "workspace.toolbar.path" (sc/get-tooltip :draw-path)) :aria-label (tr "workspace.toolbar.path" (sc/get-tooltip :draw-path)) - :class (stl/css-case :main-toolbar-options-button true :selected (= selected-drawtool :path)) + :class (stl/css-case :main-toolbar-options-button true :selected (= drawtool :path)) :on-click select-drawtool :data-tool "path" :data-testid "path-btn"} diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index 0e225599d4..a9832ffbc4 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -12,6 +12,8 @@ [app.common.data.macros :as dm] [app.common.files.helpers :as cfh] [app.common.geom.shapes :as gsh] + [app.common.types.path :as path] + [app.common.types.shape :as cts] [app.common.types.shape-tree :as ctt] [app.common.types.shape.layout :as ctl] [app.main.data.workspace.modifiers :as dwm] @@ -29,6 +31,7 @@ [app.main.ui.workspace.shapes.text.text-edition-outline :refer [text-edition-outline]] [app.main.ui.workspace.shapes.text.v2-editor :as editor-v2] [app.main.ui.workspace.shapes.text.viewport-texts-html :as stvh] + [app.main.ui.workspace.top-toolbar :refer [top-toolbar*]] [app.main.ui.workspace.viewport-wasm :as viewport.wasm] [app.main.ui.workspace.viewport.actions :as actions] [app.main.ui.workspace.viewport.comments :as comments] @@ -48,7 +51,7 @@ [app.main.ui.workspace.viewport.selection :as selection] [app.main.ui.workspace.viewport.snap-distances :as snap-distances] [app.main.ui.workspace.viewport.snap-points :as snap-points] - [app.main.ui.workspace.viewport.top-bar :as top-bar] + [app.main.ui.workspace.viewport.top-bar :refer [edition-bar*]] [app.main.ui.workspace.viewport.utils :as utils] [app.main.ui.workspace.viewport.viewport-ref :refer [create-viewport-ref]] [app.main.ui.workspace.viewport.widgets :as widgets] @@ -88,12 +91,14 @@ vport zoom zoom-inverse - edition]} wlocal + edition]} + wlocal {:keys [options-mode tooltip show-distances? - picking-color?]} wglobal + picking-color?]} + wglobal vbox' (mf/use-debounce 100 vbox) @@ -122,23 +127,23 @@ (not-empty)) ;; STATE - alt? (mf/use-state false) - shift? (mf/use-state false) - mod? (mf/use-state false) - space? (mf/use-state false) - z? (mf/use-state false) - cursor (mf/use-state (utils/get-cursor :pointer-inner)) - hover-ids (mf/use-state nil) - hover (mf/use-state nil) - measure-hover (mf/use-state nil) - hover-disabled? (mf/use-state false) + alt? (mf/use-state false) + shift? (mf/use-state false) + mod? (mf/use-state false) + space? (mf/use-state false) + z? (mf/use-state false) + cursor (mf/use-state #(utils/get-cursor :pointer-inner)) + hover-ids (mf/use-state nil) + hover (mf/use-state nil) + measure-hover (mf/use-state nil) + hover-disabled? (mf/use-state false) hover-top-frame-id (mf/use-state nil) - frame-hover (mf/use-state nil) - active-frames (mf/use-state #{}) + frame-hover (mf/use-state nil) + active-frames (mf/use-state #{}) ;; REFS - [viewport-ref - on-viewport-ref] (create-viewport-ref) + [viewport-ref on-viewport-ref] + (create-viewport-ref) ;; VARS disable-paste (mf/use-var false) @@ -163,34 +168,43 @@ selected-frames (into #{} (map :frame-id) selected-shapes) ;; Only when we have all the selected shapes in one frame - selected-frame (when (= (count selected-frames) 1) (get base-objects (first selected-frames))) + selected-frame (when (= (count selected-frames) 1) + (get base-objects (first selected-frames))) - editing-shape (when edition (get base-objects edition)) + edit-path-state (get edit-path edition) + edit-path-mode (get edit-path-state :edit-mode) - edit-path (get edit-path edition) - edit-path-mode (get edit-path :edit-mode) + path-editing? (some? edit-path-state) + path-drawing? (or (= edit-path-mode :draw) + (and (= :path (get drawing-obj :type)) + (not= :curve drawing-tool))) + + editing-shape (when edition + (get base-objects edition)) + + editing-shape (mf/with-memo [editing-shape path-editing? base-objects] + (if path-editing? + (path/convert-to-path editing-shape base-objects) + editing-shape)) create-comment? (= :comments drawing-tool) - drawing-path? (or (= edit-path-mode :draw) - (= :path (get drawing-obj :type))) - node-editing? (cfh/path-shape? editing-shape) text-editing? (cfh/text-shape? editing-shape) grid-editing? (and edition (ctl/grid-layout? base-objects edition)) mode-inspect? (= options-mode :inspect) - on-click (actions/on-click hover selected edition drawing-path? drawing-tool space? selrect z?) + on-click (actions/on-click hover selected edition path-drawing? drawing-tool space? selrect z?) on-context-menu (actions/on-context-menu hover hover-ids read-only?) - on-double-click (actions/on-double-click hover hover-ids hover-top-frame-id drawing-path? base-objects edition drawing-tool z? read-only?) + on-double-click (actions/on-double-click hover hover-ids hover-top-frame-id path-drawing? base-objects edition drawing-tool z? read-only?) comp-inst-ref (mf/use-ref false) on-drag-enter (actions/on-drag-enter comp-inst-ref) on-drag-over (actions/on-drag-over move-stream) on-drag-end (actions/on-drag-over comp-inst-ref) on-drop (actions/on-drop file comp-inst-ref) - on-pointer-down (actions/on-pointer-down @hover selected edition drawing-tool text-editing? node-editing? grid-editing? - drawing-path? create-comment? space? panning z? read-only?) + on-pointer-down (actions/on-pointer-down @hover selected edition drawing-tool text-editing? path-editing? grid-editing? + path-drawing? create-comment? space? panning z? read-only?) on-pointer-up (actions/on-pointer-up disable-paste) @@ -236,14 +250,14 @@ (or drawing-obj transform)) show-selrect? (and selrect (empty? drawing) (not text-editing?)) show-measures? (and (not transform) - (not node-editing?) + (not path-editing?) (or show-distances? mode-inspect? read-only?)) show-artboard-names? (contains? layout :display-artboard-names) hide-ui? (contains? layout :hide-ui) show-rulers? (and (contains? layout :rulers) (not hide-ui?)) - disabled-guides? (or drawing-tool transform drawing-path? node-editing?) + disabled-guides? (or drawing-tool transform path-drawing? path-editing?) single-select? (= (count selected-shapes) 1) @@ -276,24 +290,30 @@ rule-area-size (/ rulers/ruler-area-size zoom)] - (hooks/setup-dom-events zoom disable-paste in-viewport? read-only? drawing-tool drawing-path?) + (hooks/setup-dom-events zoom disable-paste in-viewport? read-only? drawing-tool path-drawing?) (hooks/setup-viewport-size vport viewport-ref) - (hooks/setup-cursor cursor alt? mod? space? panning drawing-tool drawing-path? node-editing? z? read-only?) + (hooks/setup-cursor cursor alt? mod? space? panning drawing-tool path-drawing? path-editing? z? read-only?) (hooks/setup-keyboard alt? mod? space? z? shift?) (hooks/setup-hover-shapes page-id move-stream base-objects transform selected mod? hover measure-hover hover-ids hover-top-frame-id @hover-disabled? focus zoom show-measures?) (hooks/setup-viewport-modifiers modifiers base-objects) - (hooks/setup-shortcuts node-editing? drawing-path? text-editing? grid-editing?) + (hooks/setup-shortcuts path-editing? path-drawing? text-editing? grid-editing?) (hooks/setup-active-frames base-objects hover-ids selected active-frames zoom transform vbox) [:div {:class (stl/css :viewport) :style #js {"--zoom" zoom} :data-testid "viewport"} (when (:can-edit permissions) - [:> top-bar/top-bar* {:layout layout - :selected selected-shapes - :edit-path edit-path - :drawing drawing - :edition edition - :is-read-only read-only?}]) + [:* + (when-not hide-ui? + [:> top-toolbar* {:layout layout}]) + + (when single-select? + [:> edition-bar* {:shape editing-shape + :is-path-edition path-editing? + :is-grid-edition grid-editing? + :is-read-only read-only? + :edit-path-state edit-path-state + :layout layout}])]) + [:div {:class (stl/css :viewport-overlays)} ;; The behaviour inside a foreign object is a bit different that in plain HTML so we wrap ;; inside a foreign object "dummy" so this awkward behaviour is take into account @@ -512,7 +532,8 @@ :on-frame-leave on-frame-leave :on-frame-select on-frame-select}]) - (when show-draw-area? + (when (and ^boolean show-draw-area? + ^boolean (cts/shape? drawing-obj)) [:> drawarea/draw-area* {:shape drawing-obj :zoom zoom @@ -618,6 +639,7 @@ (when-not text-editing? (if editing-shape [:> path-editor* {:shape editing-shape + :state edit-path-state :zoom zoom}] (when selected-shapes [:> selection/handlers* diff --git a/frontend/src/app/main/ui/workspace/viewport/drawarea.cljs b/frontend/src/app/main/ui/workspace/viewport/drawarea.cljs index 725d2ec8e6..c7ccc2c72c 100644 --- a/frontend/src/app/main/ui/workspace/viewport/drawarea.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/drawarea.cljs @@ -7,13 +7,19 @@ (ns app.main.ui.workspace.viewport.drawarea "Drawing components." (:require + [app.common.data.macros :as dm] [app.common.math :as mth] - [app.common.types.shape :as cts] + [app.main.refs :as refs] [app.main.ui.shapes.path :refer [path-shape]] [app.main.ui.workspace.shapes :as shapes] [app.main.ui.workspace.shapes.path.editor :refer [path-editor*]] + [okulary.core :as l] [rumext.v2 :as mf])) +(defn- make-edit-path-ref [id] + (let [get-fn #(dm/get-in % [:edit-path id])] + (l/derived get-fn refs/workspace-local))) + (mf/defc generic-draw-area* {::mf/private true} [{:keys [shape zoom]}] @@ -29,17 +35,32 @@ :fill "none" :stroke-width (/ 1 zoom)}}]))) +(mf/defc path-draw-area* + {::mf/private true} + [{:keys [shape] :as props}] + (let [shape-id + (dm/get-prop shape :id) + + edit-path-ref + (mf/with-memo [shape-id] + (make-edit-path-ref shape-id)) + + edit-path-state + (mf/deref edit-path-ref) + + props + (mf/spread-props props {:state edit-path-state})] + + [:> path-editor* props])) + (mf/defc draw-area* [{:keys [shape zoom tool] :as props}] + [:g.draw-area + [:g {:style {:pointer-events "none"}} + [:& shapes/shape-wrapper {:shape shape}]] - ;; Prevent rendering something that it's not a shape. - (when (cts/shape? shape) - [:g.draw-area - [:g {:style {:pointer-events "none"}} - [:& shapes/shape-wrapper {:shape shape}]] - - (case tool - :path [:> path-editor* props] - :curve [:& path-shape {:shape shape :zoom zoom}] - #_:default [:> generic-draw-area* props])])) + (case tool + :path [:> path-draw-area* props] + :curve [:& path-shape {:shape shape :zoom zoom}] + #_:default [:> generic-draw-area* props])]) diff --git a/frontend/src/app/main/ui/workspace/viewport/path_actions.cljs b/frontend/src/app/main/ui/workspace/viewport/path_actions.cljs index 0f9553d652..5930c74c12 100644 --- a/frontend/src/app/main/ui/workspace/viewport/path_actions.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/path_actions.cljs @@ -67,8 +67,8 @@ :separate-nodes segments-selected?}))) (mf/defc path-actions* - [{:keys [shape edit-path]}] - (let [{:keys [edit-mode selected-points snap-toggled]} edit-path + [{:keys [shape state]}] + (let [{:keys [edit-mode selected-points snap-toggled]} state content (:content shape) diff --git a/frontend/src/app/main/ui/workspace/viewport/top_bar.cljs b/frontend/src/app/main/ui/workspace/viewport/top_bar.cljs index 570eaad0b0..1c91d3c656 100644 --- a/frontend/src/app/main/ui/workspace/viewport/top_bar.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/top_bar.cljs @@ -7,19 +7,20 @@ (ns app.main.ui.workspace.viewport.top-bar (:require-macros [app.main.style :as stl]) (:require - [app.common.data.macros :as dm] - [app.common.files.helpers :as cfh] - [app.common.types.shape.layout :as ctl] [app.main.data.workspace :as dw] [app.main.data.workspace.common :as dwc] [app.main.store :as st] - [app.main.ui.workspace.top-toolbar :refer [top-toolbar]] [app.main.ui.workspace.viewport.grid-layout-editor :refer [grid-edition-actions]] [app.main.ui.workspace.viewport.path-actions :refer [path-actions*]] [app.util.i18n :as i18n :refer [tr]] [rumext.v2 :as mf])) +;; FIXME: this namespace should be renamed and all translation files +;; should also be renamed. But this should be done on development +;; branch. + (mf/defc view-only-actions* + {::mf/private true} [] (let [handle-close-view-mode (mf/use-fn @@ -37,44 +38,22 @@ :on-click handle-close-view-mode} (tr "workspace.top-bar.read-only.done")]]])) -(mf/defc top-bar* - [{:keys [layout drawing is-read-only edition selected edit-path]}] - (let [rulers? (contains? layout :rulers) - hide-ui? (contains? layout :hide-ui) +(mf/defc edition-bar* + [{:keys [layout is-read-only edit-path-state is-grid-edition is-path-edition shape]}] + (cond + ^boolean + is-read-only + [:> view-only-actions*] - drawing-obj (get drawing :object) - shape (or drawing-obj (-> selected first)) - shape-id (dm/get-prop shape :id) + ^boolean + is-path-edition - single? (= (count selected) 1) - editing? (= shape-id edition) + (let [rulers? (contains? layout :rulers) + class (stl/css-case + :viewport-actions-path true + :viewport-actions-no-rulers (not rulers?))] + [:div {:class class} + [:> path-actions* {:shape shape :state edit-path-state}]]) - draw-path? (and (some? drawing-obj) - (cfh/path-shape? drawing-obj) - (not= :curve (:tool drawing))) - - is-path-edition - (or (and single? editing? - (and (not (cfh/text-shape? shape)) - (not (cfh/frame-shape? shape)))) - draw-path?) - - grid-edition? - (and single? editing? (ctl/grid-layout? shape))] - - [:* - (when-not ^boolean hide-ui? - [:& top-toolbar {:layout layout}]) - - (cond - ^boolean - is-read-only - [:> view-only-actions*] - - ^boolean - is-path-edition - [:div {:class (stl/css-case :viewport-actions-path true :viewport-actions-no-rulers (not rulers?))} - [:> path-actions* {:shape shape :edit-path edit-path}]] - - grid-edition? - [:& grid-edition-actions {:shape shape}])])) + is-grid-edition + [:& grid-edition-actions {:shape shape}])) diff --git a/frontend/src/app/main/ui/workspace/viewport_wasm.cljs b/frontend/src/app/main/ui/workspace/viewport_wasm.cljs index 0f49c833ee..91f0ab70bc 100644 --- a/frontend/src/app/main/ui/workspace/viewport_wasm.cljs +++ b/frontend/src/app/main/ui/workspace/viewport_wasm.cljs @@ -12,6 +12,7 @@ [app.common.data.macros :as dm] [app.common.files.helpers :as cfh] [app.common.geom.shapes :as gsh] + [app.common.types.path :as path] [app.common.types.shape :as cts] [app.common.types.shape-tree :as ctt] [app.common.types.shape.layout :as ctl] @@ -27,6 +28,7 @@ [app.main.ui.workspace.shapes.text.editor :as editor-v1] [app.main.ui.workspace.shapes.text.text-edition-outline :refer [text-edition-outline]] [app.main.ui.workspace.shapes.text.v2-editor :as editor-v2] + [app.main.ui.workspace.top-toolbar :refer [top-toolbar*]] [app.main.ui.workspace.viewport.actions :as actions] [app.main.ui.workspace.viewport.comments :as comments] [app.main.ui.workspace.viewport.debug :as wvd] @@ -45,7 +47,7 @@ [app.main.ui.workspace.viewport.selection :as selection] [app.main.ui.workspace.viewport.snap-distances :as snap-distances] [app.main.ui.workspace.viewport.snap-points :as snap-points] - [app.main.ui.workspace.viewport.top-bar :as top-bar] + [app.main.ui.workspace.viewport.top-bar :refer [edition-bar*]] [app.main.ui.workspace.viewport.utils :as utils] [app.main.ui.workspace.viewport.viewport-ref :refer [create-viewport-ref]] [app.main.ui.workspace.viewport.widgets :as widgets] @@ -162,38 +164,45 @@ drawing-tool (:tool drawing) drawing-obj (:object drawing) - selected-frames (into #{} (map :frame-id) selected-shapes) ;; Only when we have all the selected shapes in one frame selected-frame (when (= (count selected-frames) 1) (get base-objects (first selected-frames))) - editing-shape (when edition (get base-objects edition)) + edit-path-state (get edit-path edition) + edit-path-mode (get edit-path-state :edit-mode) - edit-path (get edit-path edition) - edit-path-mode (get edit-path :edit-mode) + path-editing? (some? edit-path-state) + path-drawing? (or (= edit-path-mode :draw) + (and (= :path (get drawing-obj :type)) + (not= :curve drawing-tool))) + + editing-shape (when edition + (get base-objects edition)) + + editing-shape (mf/with-memo [editing-shape path-editing? base-objects] + (if path-editing? + (path/convert-to-path editing-shape base-objects) + editing-shape)) create-comment? (= :comments drawing-tool) - drawing-path? (or (= edit-path-mode :draw) - (= :path (get drawing-obj :type))) - node-editing? (cfh/path-shape? editing-shape) text-editing? (cfh/text-shape? editing-shape) grid-editing? (and edition (ctl/grid-layout? base-objects edition)) mode-inspect? (= options-mode :inspect) - on-click (actions/on-click hover selected edition drawing-path? drawing-tool space? selrect z?) + on-click (actions/on-click hover selected edition path-drawing? drawing-tool space? selrect z?) on-context-menu (actions/on-context-menu hover hover-ids read-only?) - on-double-click (actions/on-double-click hover hover-ids hover-top-frame-id drawing-path? base-objects edition drawing-tool z? read-only?) + on-double-click (actions/on-double-click hover hover-ids hover-top-frame-id path-drawing? base-objects edition drawing-tool z? read-only?) comp-inst-ref (mf/use-ref false) on-drag-enter (actions/on-drag-enter comp-inst-ref) on-drag-over (actions/on-drag-over move-stream) on-drag-end (actions/on-drag-over comp-inst-ref) on-drop (actions/on-drop file comp-inst-ref) - on-pointer-down (actions/on-pointer-down @hover selected edition drawing-tool text-editing? node-editing? grid-editing? - drawing-path? create-comment? space? panning z? read-only?) + on-pointer-down (actions/on-pointer-down @hover selected edition drawing-tool text-editing? path-editing? grid-editing? + path-drawing? create-comment? space? panning z? read-only?) on-pointer-up (actions/on-pointer-up disable-paste) @@ -239,14 +248,14 @@ (or drawing-obj transform)) show-selrect? (and selrect (empty? drawing) (not text-editing?)) show-measures? (and (not transform) - (not node-editing?) + (not path-editing?) (or show-distances? mode-inspect?)) show-artboard-names? (contains? layout :display-artboard-names) hide-ui? (contains? layout :hide-ui) show-rulers? (and (contains? layout :rulers) (not hide-ui?)) - disabled-guides? (or drawing-tool transform drawing-path? node-editing?) + disabled-guides? (or drawing-tool transform path-drawing? path-editing?) single-select? (= (count selected-shapes) 1) @@ -329,23 +338,27 @@ (when (and @canvas-init? initialized?) (wasm.api/set-canvas-background background))) - (hooks/setup-dom-events zoom disable-paste in-viewport? read-only? drawing-tool drawing-path?) + (hooks/setup-dom-events zoom disable-paste in-viewport? read-only? drawing-tool path-drawing?) (hooks/setup-viewport-size vport viewport-ref) - (hooks/setup-cursor cursor alt? mod? space? panning drawing-tool drawing-path? node-editing? z? read-only?) + (hooks/setup-cursor cursor alt? mod? space? panning drawing-tool path-drawing? path-editing? z? read-only?) (hooks/setup-keyboard alt? mod? space? z? shift?) (hooks/setup-hover-shapes page-id move-stream base-objects transform selected mod? hover measure-hover hover-ids hover-top-frame-id @hover-disabled? focus zoom show-measures?) - (hooks/setup-shortcuts node-editing? drawing-path? text-editing? grid-editing?) + (hooks/setup-shortcuts path-editing? path-drawing? text-editing? grid-editing?) (hooks/setup-active-frames base-objects hover-ids selected active-frames zoom transform vbox) [:div {:class (stl/css :viewport) :style #js {"--zoom" zoom} :data-testid "viewport"} (when (:can-edit permissions) - [:> top-bar/top-bar* {:layout layout - :selected selected-shapes - :edit-path edit-path - :drawing drawing - :edition edition - :is-read-only read-only?}]) + [:* + (when-not hide-ui? + [:> top-toolbar* {:layout layout}]) + + [:> edition-bar* {:layout layout + :selected selected-shapes + :edit-path edit-path-state + :drawing drawing + :edition edition + :is-read-only read-only?}]]) [:div {:class (stl/css :viewport-overlays)} (when show-comments? [:> comments/comments-layer* {:vbox vbox @@ -509,7 +522,8 @@ :on-frame-leave on-frame-leave :on-frame-select on-frame-select}]) - (when show-draw-area? + (when (and ^boolean show-draw-area? + ^boolean (cts/shape? drawing-obj)) [:> drawarea/draw-area* {:shape drawing-obj :zoom zoom @@ -615,6 +629,7 @@ (when-not text-editing? (if editing-shape [:> path-editor* {:shape editing-shape + :state edit-path-state :zoom zoom}] (when selected-shapes [:> selection/handlers*