♻️ Refactor path editor components: path-handler and path-point

This commit is contained in:
Andrey Antukh 2025-04-04 10:38:57 +02:00
parent da2f519805
commit 3d864c4ff1

View file

@ -37,16 +37,25 @@
(def path-preview-dasharray 4) (def path-preview-dasharray 4)
(def path-snap-stroke-width 1) (def path-snap-stroke-width 1)
(mf/defc path-point [{:keys [position zoom edit-mode hover? selected? preview? start-path? last-p? new-point? curve?]}] (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]}]
(let [{:keys [x y]} position (let [{:keys [x y]} position
is-draw (= edit-mode :draw)
is-move (= edit-mode :move)
is-active
(or ^boolean is-selected
^boolean is-hover)
on-enter on-enter
(mf/use-callback (mf/use-fn
(fn [_] (fn [_]
(st/emit! (drp/path-pointer-enter position)))) (st/emit! (drp/path-pointer-enter position))))
on-leave on-leave
(mf/use-callback (mf/use-fn
(fn [_] (fn [_]
(st/emit! (drp/path-pointer-leave position)))) (st/emit! (drp/path-pointer-leave position))))
@ -55,41 +64,44 @@
(dom/stop-propagation event) (dom/stop-propagation event)
(dom/prevent-default event) (dom/prevent-default event)
(when (and new-point? (some? (meta position))) ;; FIXME: revisit this, using meta here breaks equality checks
(when (and is-new (some? (meta position)))
(st/emit! (drp/create-node-at-position (meta position)))) (st/emit! (drp/create-node-at-position (meta position))))
(let [shift? (kbd/shift? event) (let [is-shift (kbd/shift? event)
mod? (kbd/mod? event)] is-mod (kbd/mod? event)]
(cond (cond
last-p? is-last
(st/emit! (drp/reset-last-handler)) (st/emit! (drp/reset-last-handler))
(and (= edit-mode :move) mod? (not curve?)) (and is-move is-mod (not is-curve))
(st/emit! (drp/make-curve position)) (st/emit! (drp/make-curve position))
(and (= edit-mode :move) mod? curve?) (and is-move is-mod is-curve)
(st/emit! (drp/make-corner position)) (st/emit! (drp/make-corner position))
(= edit-mode :move) is-move
;; If we're dragging a selected item we don't change the selection ;; If we're dragging a selected item we don't change the selection
(st/emit! (drp/start-move-path-point position shift?)) (st/emit! (drp/start-move-path-point position is-shift))
(and (= edit-mode :draw) start-path?) (and is-draw is-start-path)
(st/emit! (drp/start-path-from-point position)) (st/emit! (drp/start-path-from-point position))
(and (= edit-mode :draw) (not start-path?)) (and is-draw (not is-start-path))
(st/emit! (drp/close-path-drag-start position)))))] (st/emit! (drp/close-path-drag-start position)))))]
[:g.path-point [:g.path-point
[:circle.path-point [:circle.path-point
{:cx x {:cx x
:cy y :cy y
:r (if (or selected? hover?) (/ point-radius zoom) (/ point-radius-selected zoom)) :r (if ^boolean is-active
(/ point-radius zoom)
(/ point-radius-selected zoom))
:style {:stroke-width (/ point-radius-stroke-width zoom) :style {:stroke-width (/ point-radius-stroke-width zoom)
:stroke (cond (or selected? hover?) pc/black-color :stroke (cond ^boolean is-active pc/black-color
preview? pc/secondary-color ^boolean is-preview pc/secondary-color
:else pc/accent-color) :else pc/accent-color)
:fill (cond selected? pc/accent-color :fill (cond is-selected pc/accent-color
:else pc/white-color)}}] :else pc/white-color)}}]
[:circle {:cx x [:circle {:cx x
:cy y :cy y
@ -97,70 +109,87 @@
:on-pointer-down on-pointer-down :on-pointer-down on-pointer-down
:on-pointer-enter on-enter :on-pointer-enter on-enter
:on-pointer-leave on-leave :on-pointer-leave on-leave
:pointer-events (when-not preview? "visible") :pointer-events (when-not ^boolean is-preview "visible")
:class (cond (= edit-mode :draw) (cur/get-static "pen-node") :class (cond ^boolean is-draw (cur/get-static "pen-node")
(= edit-mode :move) (cur/get-static "pointer-node")) ^boolean is-move (cur/get-static "pointer-node"))
:style {:stroke-width 0 :style {:stroke-width 0
:fill "none"}}]])) :fill "none"}}]]))
(mf/defc path-handler [{:keys [index prefix point handler zoom selected? hover? edit-mode snap-angle?]}] ;; FIXME: is-selected prop looks unused
(when (and point handler)
(let [{:keys [x y]} handler
on-enter
(fn [_]
(st/emit! (drp/path-handler-enter index prefix)))
on-leave (mf/defc path-handler*
(fn [_] {::mf/private true}
(st/emit! (drp/path-handler-leave index prefix))) [{:keys [index prefix point handler zoom is-selected is-hover edit-mode snap-angle]}]
(let [x (dm/get-prop handler :x)
y (dm/get-prop handler :y)
is-draw (= edit-mode :draw)
is-move (= edit-mode :move)
on-pointer-down is-active
(fn [event] (or ^boolean is-selected
(dom/stop-propagation event) ^boolean is-hover)
(dom/prevent-default event)
(cond on-enter
(= edit-mode :move) (mf/use-fn
(st/emit! (drp/start-move-handler index prefix))))] (mf/deps index prefix)
(fn [_] (st/emit! (drp/path-handler-enter index prefix))))
[:g.handler {:pointer-events (if (= edit-mode :draw) "none" "visible")} on-leave
(mf/use-fn
(mf/deps index prefix)
(fn [_] (st/emit! (drp/path-handler-leave index prefix))))
on-pointer-down
(mf/use-fn
(mf/deps index prefix is-move)
(fn [event]
(dom/stop-propagation event)
(dom/prevent-default event)
(when ^boolean is-move
(st/emit! (drp/start-move-handler index prefix)))))]
[:g.handler {:pointer-events (if ^boolean is-draw "none" "visible")}
[:line
{:x1 (:x point)
:y1 (:y point)
:x2 x
:y2 y
:style {:stroke (if ^boolean is-hover
pc/black-color
pc/gray-color)
:stroke-width (/ point-radius-stroke-width zoom)}}]
(when ^boolean snap-angle
[:line [:line
{:x1 (:x point) {:x1 (:x point)
:y1 (:y point) :y1 (:y point)
:x2 x :x2 x
:y2 y :y2 y
:style {:stroke (if hover? pc/black-color pc/gray-color) :style {:stroke pc/secondary-color
:stroke-width (/ point-radius-stroke-width zoom)}}] :stroke-width (/ point-radius-stroke-width zoom)}}])
(when snap-angle? [:rect
[:line {:x (- x (/ handler-side 2 zoom))
{:x1 (:x point) :y (- y (/ handler-side 2 zoom))
:y1 (:y point) :width (/ handler-side zoom)
:x2 x :height (/ handler-side zoom)
:y2 y
:style {:stroke pc/secondary-color
:stroke-width (/ point-radius-stroke-width zoom)}}])
[:rect :style {:stroke-width (/ handler-stroke-width zoom)
{:x (- x (/ handler-side 2 zoom)) :stroke (cond ^boolean is-active pc/black-color
:y (- y (/ handler-side 2 zoom)) :else pc/accent-color)
:width (/ handler-side zoom) :fill (cond ^boolean is-selected pc/accent-color
:height (/ handler-side zoom) :else pc/white-color)}}]
[:circle {:cx x
:style {:stroke-width (/ handler-stroke-width zoom) :cy y
:stroke (cond (or selected? hover?) pc/black-color :r (/ point-radius-active-area zoom)
:else pc/accent-color) :on-pointer-down on-pointer-down
:fill (cond selected? pc/accent-color :on-pointer-enter on-enter
:else pc/white-color)}}] :on-pointer-leave on-leave
[:circle {:cx x :class (when ^boolean is-move
:cy y (cur/get-static "pointer-move"))
:r (/ point-radius-active-area zoom) :style {:fill "none"
:on-pointer-down on-pointer-down :stroke-width 0}}]]))
:on-pointer-enter on-enter
:on-pointer-leave on-leave
:class (when (= edit-mode :move) (cur/get-static "pointer-move"))
:style {:fill "none"
:stroke-width 0}}]])))
(mf/defc path-preview [{:keys [zoom command from]}] (mf/defc path-preview [{:keys [zoom command from]}]
[:g.preview {:style {:pointer-events "none"}} [:g.preview {:style {:pointer-events "none"}}
@ -173,9 +202,9 @@
:params {:x (:x from) :params {:x (:x from)
:y (:y from)}} :y (:y from)}}
command])}]) command])}])
[:& path-point {:position (:params command) [:> path-point* {:position (:params command)
:preview? true :is-preview true
:zoom zoom}]]) :zoom zoom}]])
(mf/defc path-snap [{:keys [selected points zoom]}] (mf/defc path-snap [{:keys [selected points zoom]}]
(let [ranges (mf/use-memo (mf/deps selected points) #(snap/create-ranges points selected)) (let [ranges (mf/use-memo (mf/deps selected points) #(snap/create-ranges points selected))
@ -250,7 +279,7 @@
last-p (->> content last path.segment/get-point) last-p (->> content last path.segment/get-point)
handlers (path.segment/content->handlers content) handlers (path.segment/content->handlers content)
start-p? (not (some? last-point)) is-path-start (not (some? last-point))
[snap-selected snap-points] [snap-selected snap-points]
(cond (cond
@ -297,20 +326,20 @@
:from last-p :from last-p
:zoom zoom}]) :zoom zoom}])
(when drag-handler (when (and drag-handler last-p)
[:g.drag-handler {:pointer-events "none"} [:g.drag-handler {:pointer-events "none"}
[:& path-handler {:point last-p [:> path-handler* {:point last-p
:handler drag-handler :handler drag-handler
:edit-mode edit-mode :edit-mode edit-mode
:zoom zoom}]]) :zoom zoom}]])
(when @hover-point (when @hover-point
[:g.hover-point [:g.hover-point
[:& path-point {:position @hover-point [:> path-point* {:position @hover-point
:edit-mode edit-mode :edit-mode edit-mode
:new-point? true :is-new true
:start-path? start-p? :is-start-path is-path-start
:zoom zoom}]]) :zoom zoom}]])
(for [[index position] (d/enumerate points)] (for [[index position] (d/enumerate points)]
(let [show-handler? (let [show-handler?
@ -321,10 +350,10 @@
pos-handlers (get handlers position) pos-handlers (get handlers position)
point-selected? (contains? selected-points (get point->base position)) point-selected? (contains? selected-points (get point->base position))
point-hover? (contains? hover-points (get point->base position)) point-hover? (contains? hover-points (get point->base position))
last-p? (= last-point (get point->base position)) is-last (= last-point (get point->base position))
pos-handlers (->> pos-handlers (filter show-handler?)) pos-handlers (->> pos-handlers (filter show-handler?))
curve? (boolean (seq pos-handlers))] is-curve (boolean (seq pos-handlers))]
[:g.path-node {:key (dm/str index "-" (:x position) "-" (:y position))} [:g.path-node {:key (dm/str index "-" (:x position) "-" (:y position))}
[:g.point-handlers {:pointer-events (when (= edit-mode :draw) "none")} [:g.point-handlers {:pointer-events (when (= edit-mode :draw) "none")}
@ -333,30 +362,34 @@
handler-hover? (contains? hover-handlers [hindex prefix]) handler-hover? (contains? hover-handlers [hindex prefix])
moving-handler? (= handler-position moving-handler) moving-handler? (= handler-position moving-handler)
matching-handler? (matching-handler? content position pos-handlers)] matching-handler? (matching-handler? content position pos-handlers)]
[:& path-handler {:key (dm/str (dm/str index "-" (:x position) "-" (:y position)) "-" hindex "-" (d/name prefix))
:point position
:handler handler-position
:index hindex
:prefix prefix
:zoom zoom
:hover? handler-hover?
:snap-angle? (and moving-handler? matching-handler?)
:edit-mode edit-mode}]))]
[:& path-point {:position position
:zoom zoom
:edit-mode edit-mode
:selected? point-selected?
:hover? point-hover?
:last-p? last-p?
:start-path? start-p?
:curve? curve?}]]))
(when prev-handler (when (and position handler-position)
[:> path-handler*
{:key (dm/str (dm/str index "-" (:x position) "-" (:y position)) "-" hindex "-" (d/name prefix))
:point position
:handler handler-position
:index hindex
:prefix prefix
:zoom zoom
:is-hover handler-hover?
:snap-angle (and moving-handler? matching-handler?)
:edit-mode edit-mode}])))]
[:> path-point* {:position position
:zoom zoom
:edit-mode edit-mode
:is-selected point-selected?
:is-hover point-hover?
:is-last is-last
:is-start-path is-path-start
:is-curve is-curve}]]))
(when (and prev-handler last-p)
[:g.prev-handler {:pointer-events "none"} [:g.prev-handler {:pointer-events "none"}
[:& path-handler {:point last-p [:> path-handler*
:edit-mode edit-mode {:point last-p
:handler prev-handler :edit-mode edit-mode
:zoom zoom}]]) :handler prev-handler
:zoom zoom}]])
(when show-snap? (when show-snap?
[:g.path-snap {:pointer-events "none"} [:g.path-snap {:pointer-events "none"}