Add minor performance enhacements to viewport top-bar

This commit is contained in:
Andrey Antukh 2025-04-29 11:19:53 +02:00
parent 202337b135
commit f1a557c372
10 changed files with 131 additions and 138 deletions

View file

@ -6,7 +6,6 @@
(ns app.main.data.workspace.edition
(:require
[app.common.data.macros :as dm]
[app.main.data.helpers :as dsh]
[app.main.data.workspace.path.common :as dwpc]
[beicon.v2.core :as rx]
@ -17,8 +16,10 @@
(declare clear-edition-mode)
(defn start-edition-mode
"Mark a shape in edition mode"
[id]
(dm/assert! (uuid? id))
(assert (uuid? id) "expected valid uuid for `id`")
(ptk/reify ::start-edition-mode
ptk/UpdateEvent
(update [_ state]
@ -26,8 +27,7 @@
;; Can only edit objects that exist
(if (contains? objects id)
(-> state
(assoc-in [:workspace-local :selected] #{id})
(assoc-in [:workspace-local :edition] id)
(update :workspace-local assoc :edition id)
(dissoc :workspace-grid-edition))
state)))

View file

@ -17,7 +17,6 @@
[app.main.data.helpers :as dsh]
[app.main.data.workspace.edition :as dwe]
[app.main.data.workspace.path.changes :as changes]
[app.main.data.workspace.path.drawing :as drawing]
[app.main.data.workspace.path.helpers :as helpers]
[app.main.data.workspace.path.selection :as selection]
[app.main.data.workspace.path.state :as st]
@ -309,23 +308,21 @@
(assoc-in [:workspace-local :edit-path id] {:edit-mode :move
:selected #{}
:snap-toggled false})
(and (some? edit-path)
(= :move (:edit-mode edit-path)))
(assoc-in [:workspace-local :edit-path id :edit-mode] :draw))))
ptk/WatchEvent
(watch [_ state stream]
(let [mode (dm/get-in state [:workspace-local :edit-path id :edit-mode])
stopper (->> stream
(rx/filter #(or
(= (ptk/type %) ::dwe/clear-edition-mode)
(= (ptk/type %) ::start-path-edit))))
interrupt (->> stream (rx/filter #(= % :interrupt)) (rx/take 1))]
(watch [_ _ stream]
(let [stopper (->> stream
(rx/filter #(let [type (ptk/type %)]
(= type ::dwe/clear-edition-mode)
(= type ::start-path-edit))))]
(rx/concat
(rx/of (undo/start-path-undo)
(drawing/change-edit-mode mode))
(->> interrupt
(rx/of (undo/start-path-undo))
(->> stream
(rx/filter #(= % :interrupt))
(rx/take 1)
(rx/map #(stop-path-edit id))
(rx/take-until stopper)))))))

View file

@ -12,9 +12,15 @@
[app.main.ui.shapes.path :as path]
[app.main.ui.shapes.shape :refer [shape-container]]
[app.main.ui.workspace.shapes.debug :as wsd]
[app.main.ui.workspace.shapes.path.common :as pc]
[okulary.core :as l]
[rumext.v2 :as mf]))
(defn- make-content-modifiers-ref
[id]
(l/derived (fn [local]
(dm/get-in local [:edit-path id :content-modifiers]))
refs/workspace-local))
(defn- apply-content-modifiers
[shape content-modifiers]
(let [shape (update shape :content types.path/apply-content-modifiers content-modifiers)]
@ -26,11 +32,13 @@
(let [shape-id (dm/get-prop shape :id)
content-modifiers-ref
(pc/make-content-modifiers-ref shape-id)
(mf/with-memo [shape-id]
(make-content-modifiers-ref shape-id))
content-modifiers
(mf/deref content-modifiers-ref)
;; FIXME: this should be provided by react context instead of using refs
editing-id
(mf/deref refs/selected-edition)

View file

@ -1,43 +0,0 @@
;; 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.main.ui.workspace.shapes.path.common
(:require
[app.common.data.macros :as dm]
[app.main.data.workspace.path.state :as pst]
[app.main.refs :as refs]
[app.main.store :as st]
[okulary.core :as l]
[rumext.v2 :as mf]))
(def accent-color "var(--color-accent-tertiary)")
(def secondary-color "var(--color-accent-quaternary)")
(def black-color "var(--app-black)")
(def white-color "var(--app-white)")
(def gray-color "var(--df-secondary)")
(def current-edit-path-ref
(l/derived
(fn [state]
(let [id (pst/get-path-id state)]
(dm/get-in state [:workspace-local :edit-path id])))
st/state))
(defn make-edit-path-ref [id]
(mf/use-memo
(mf/deps id)
(let [selfn #(get-in % [:edit-path id])]
#(l/derived selfn refs/workspace-local))))
(defn content-modifiers-ref
[id]
(l/derived #(get-in % [:edit-path id :content-modifiers]) refs/workspace-local))
(defn make-content-modifiers-ref [id]
(mf/use-memo
(mf/deps id)
#(content-modifiers-ref id)))

View file

@ -13,16 +13,17 @@
[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]
[app.main.ui.css-cursors :as cur]
[app.main.ui.hooks :as hooks]
[app.main.ui.workspace.shapes.path.common :as pc]
[app.util.dom :as dom]
[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)
@ -36,6 +37,12 @@
(def path-preview-dasharray 4)
(def path-snap-stroke-width 1)
(def accent-color "var(--color-accent-tertiary)")
(def secondary-color "var(--color-accent-quaternary)")
(def black-color "var(--app-black)")
(def white-color "var(--app-white)")
(def gray-color "var(--df-secondary)")
(mf/defc path-point*
{::mf/private true}
[{:keys [position zoom edit-mode is-hover is-selected is-preview is-start-path is-last is-new is-curve]}]
@ -97,11 +104,11 @@
(/ point-radius zoom)
(/ point-radius-selected zoom))
:style {:stroke-width (/ point-radius-stroke-width zoom)
:stroke (cond ^boolean is-active pc/black-color
^boolean is-preview pc/secondary-color
:else pc/accent-color)
:fill (cond is-selected pc/accent-color
:else pc/white-color)}}]
:stroke (cond ^boolean is-active black-color
^boolean is-preview secondary-color
:else accent-color)
:fill (cond is-selected accent-color
:else white-color)}}]
[:circle {:cx x
:cy y
:r (/ point-radius-active-area zoom)
@ -155,8 +162,8 @@
:x2 x
:y2 y
:style {:stroke (if ^boolean is-hover
pc/black-color
pc/gray-color)
black-color
gray-color)
:stroke-width (/ point-radius-stroke-width zoom)}}]
(when ^boolean snap-angle
@ -165,7 +172,7 @@
:y1 (:y point)
:x2 x
:y2 y
:style {:stroke pc/secondary-color
:style {:stroke secondary-color
:stroke-width (/ point-radius-stroke-width zoom)}}])
[:rect
@ -175,10 +182,10 @@
:height (/ handler-side zoom)
:style {:stroke-width (/ handler-stroke-width zoom)
:stroke (cond ^boolean is-active pc/black-color
:else pc/accent-color)
:fill (cond ^boolean is-selected pc/accent-color
:else pc/white-color)}}]
:stroke (cond ^boolean is-active black-color
:else accent-color)
:fill (cond ^boolean is-selected accent-color
:else white-color)}}]
[:circle {:cx x
:cy y
:r (/ point-radius-active-area zoom)
@ -209,7 +216,7 @@
[:g.preview {:style {:pointer-events "none"}}
(when (some? path)
[:path {:style {:fill "none"
:stroke pc/black-color
:stroke black-color
:stroke-width (/ handler-stroke-width zoom)
:stroke-dasharray (/ path-preview-dasharray zoom)}
:d (str path)}])
@ -238,7 +245,7 @@
:y1 (:y from)
:x2 (:x to)
:y2 (:y to)
:style {:stroke pc/secondary-color
:style {:stroke secondary-color
:stroke-width (/ path-snap-stroke-width zoom)}}])]))
(defn- matching-handler? [content node handlers]
@ -253,12 +260,19 @@
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 [editor-ref (mf/use-ref nil)
edit-path-ref (pc/make-edit-path-ref (:id shape))
(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)
editor-ref (mf/use-ref nil)
{:keys [edit-mode
drag-handler
@ -333,7 +347,7 @@
[:g.path-editor {:ref editor-ref}
[:path {:d (.toString content)
:style {:fill "none"
:stroke pc/accent-color
:stroke accent-color
:strokeWidth (/ 1 zoom)}}]
(when (and preview (not drag-handler))
[:> path-preview* {:segment preview

View file

@ -164,15 +164,18 @@
editing-shape (when edition (get base-objects edition))
create-comment? (= :comments drawing-tool)
drawing-path? (or (and edition (= :draw (get-in edit-path [edition :edit-mode])))
(and (some? drawing-obj) (= :path (:type drawing-obj))))
node-editing? (and edition (= :path (get-in base-objects [edition :type])))
text-editing? (and edition (= :text (get-in base-objects [edition :type])))
edit-path (get edit-path edition)
edit-path-mode (get edit-path :edit-mode)
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)
mode-inspect? (= options-mode :inspect)
on-click (actions/on-click hover selected edition drawing-path? drawing-tool space? selrect z?)
on-context-menu (actions/on-context-menu hover hover-ids read-only?)
@ -282,7 +285,12 @@
[:div {:class (stl/css :viewport) :style #js {"--zoom" zoom} :data-testid "viewport"}
(when (:can-edit permissions)
[:& top-bar/top-bar {:layout layout}])
[:> top-bar/top-bar* {:layout layout
:selected selected-shapes
:edit-path edit-path
:drawing drawing
:edition edition
:is-read-only read-only?}])
[: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

View file

@ -227,8 +227,9 @@
(dw/start-editing-selected))
(some? selected-shape)
(do (reset! hover selected-shape)
(st/emit! (dw/select-shape (:id selected-shape))))
(do
(reset! hover selected-shape)
(st/emit! (dw/select-shape (:id selected-shape))))
(and (not selected-shape) (some? grid-layout-id) (not read-only?))
(st/emit! (dw/start-edition-mode grid-layout-id)))))))))))

View file

@ -12,7 +12,6 @@
[app.main.data.workspace.path.shortcuts :as sc]
[app.main.store :as st]
[app.main.ui.icons :as i]
[app.main.ui.workspace.shapes.path.common :as pc]
[app.util.i18n :as i18n :refer [tr]]
[rumext.v2 :as mf]))
@ -46,7 +45,6 @@
(def ^:private snap-nodes-icon
(i/icon-xref :snap-nodes (stl/css :snap-nodes-icon :pathbar-icon)))
(defn check-enabled [content selected-points]
(let [segments (path.segm/get-segments-with-points content selected-points)
num-segments (count segments)
@ -67,9 +65,10 @@
:join-nodes (and points-selected? (>= num-points 2) (< num-segments max-segments))
:separate-nodes segments-selected?}))
(mf/defc path-actions*
[{:keys [shape edit-path]}]
(let [{:keys [edit-mode selected-points snap-toggled]} edit-path
(mf/defc path-actions [{:keys [shape]}]
(let [{:keys [edit-mode selected-points snap-toggled] :as all} (mf/deref pc/current-edit-path-ref)
content (:content shape)
enabled-buttons
@ -78,66 +77,66 @@
#(check-enabled content selected-points))
on-select-draw-mode
(mf/use-callback
(mf/use-fn
(fn [_]
(st/emit! (drp/change-edit-mode :draw))))
on-select-edit-mode
(mf/use-callback
(mf/use-fn
(fn [_]
(st/emit! (drp/change-edit-mode :move))))
on-add-node
(mf/use-callback
(mf/use-fn
(mf/deps (:add-node enabled-buttons))
(fn [_]
(when (:add-node enabled-buttons)
(st/emit! (drp/add-node)))))
on-remove-node
(mf/use-callback
(mf/use-fn
(mf/deps (:remove-node enabled-buttons))
(fn [_]
(when (:remove-node enabled-buttons)
(st/emit! (drp/remove-node)))))
on-merge-nodes
(mf/use-callback
(mf/use-fn
(mf/deps (:merge-nodes enabled-buttons))
(fn [_]
(when (:merge-nodes enabled-buttons)
(st/emit! (drp/merge-nodes)))))
on-join-nodes
(mf/use-callback
(mf/use-fn
(mf/deps (:join-nodes enabled-buttons))
(fn [_]
(when (:join-nodes enabled-buttons)
(st/emit! (drp/join-nodes)))))
on-separate-nodes
(mf/use-callback
(mf/use-fn
(mf/deps (:separate-nodes enabled-buttons))
(fn [_]
(when (:separate-nodes enabled-buttons)
(st/emit! (drp/separate-nodes)))))
on-make-corner
(mf/use-callback
(mf/use-fn
(mf/deps (:make-corner enabled-buttons))
(fn [_]
(when (:make-corner enabled-buttons)
(st/emit! (drp/make-corner)))))
on-make-curve
(mf/use-callback
(mf/use-fn
(mf/deps (:make-curve enabled-buttons))
(fn [_]
(when (:make-curve enabled-buttons)
(st/emit! (drp/make-curve)))))
on-toggle-snap
(mf/use-callback
(mf/use-fn
(fn [_]
(st/emit! (drp/toggle-snap))))]

View file

@ -7,23 +7,22 @@
(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.refs :as refs]
[app.main.store :as st]
[app.main.ui.context :as ctx]
[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.main.ui.workspace.viewport.path-actions :refer [path-actions*]]
[app.util.i18n :as i18n :refer [tr]]
[rumext.v2 :as mf]))
(mf/defc view-only-actions
(mf/defc view-only-actions*
[]
(let [handle-close-view-mode
(mf/use-callback
(mf/use-fn
(fn []
(st/emit! :interrupt
(dw/set-options-mode :design)
@ -38,43 +37,44 @@
:on-click handle-close-view-mode}
(tr "workspace.top-bar.read-only.done")]]]))
(mf/defc top-bar
{::mf/wrap [mf/memo]}
[{:keys [layout]}]
(let [edition (mf/deref refs/selected-edition)
selected (mf/deref refs/selected-objects)
drawing (mf/deref refs/workspace-drawing)
rulers? (mf/deref refs/rulers?)
drawing-obj (:object drawing)
(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)
drawing-obj (get drawing :object)
shape (or drawing-obj (-> selected first))
shape-id (dm/get-prop shape :id)
single? (= (count selected) 1)
editing? (= (:id shape) edition)
draw-path? (and (some? drawing-obj)
(cfh/path-shape? drawing-obj)
(not= :curve (:tool drawing)))
single? (= (count selected) 1)
editing? (= shape-id edition)
workspace-read-only? (mf/use-ctx ctx/workspace-read-only?)
hide-ui? (:hide-ui layout)
draw-path? (and (some? drawing-obj)
(cfh/path-shape? drawing-obj)
(not= :curve (:tool drawing)))
path-edition? (or (and single? editing?
(and (not (cfh/text-shape? shape))
(not (cfh/frame-shape? shape))))
draw-path?)
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))]
grid-edition?
(and single? editing? (ctl/grid-layout? shape))]
[:*
(when-not hide-ui?
(when-not ^boolean hide-ui?
[:& top-toolbar {:layout layout}])
(cond
workspace-read-only?
[:& view-only-actions]
^boolean
is-read-only
[:> view-only-actions*]
path-edition?
^boolean
is-path-edition
[:div {:class (stl/css-case :viewport-actions-path true :viewport-actions-no-rulers (not rulers?))}
[:& path-actions {:shape shape}]]
[:> path-actions* {:shape shape :edit-path edit-path}]]
grid-edition?
[:& grid-edition-actions {:shape shape}])]))

View file

@ -173,14 +173,18 @@
editing-shape (when edition (get base-objects edition))
edit-path (get edit-path edition)
edit-path-mode (get edit-path :edit-mode)
create-comment? (= :comments drawing-tool)
drawing-path? (or (and edition (= :draw (get-in edit-path [edition :edit-mode])))
(and (some? drawing-obj) (= :path (:type drawing-obj))))
node-editing? (and edition (= :path (get-in base-objects [edition :type])))
text-editing? (and edition (= :text (get-in base-objects [edition :type])))
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)
mode-inspect? (= options-mode :inspect)
on-click (actions/on-click hover selected edition drawing-path? drawing-tool space? selrect z?)
on-context-menu (actions/on-context-menu hover hover-ids read-only?)
@ -338,7 +342,12 @@
[:div {:class (stl/css :viewport) :style #js {"--zoom" zoom} :data-testid "viewport"}
(when (:can-edit permissions)
[:& top-bar/top-bar {:layout layout}])
[:> top-bar/top-bar* {:layout layout
:selected selected-shapes
:edit-path edit-path
:drawing drawing
:edition edition
:is-read-only read-only?}])
[:div {:class (stl/css :viewport-overlays)}
(when show-comments?
[:> comments/comments-layer* {:vbox vbox