diff --git a/common/app/common/geom/shapes/path.cljc b/common/app/common/geom/shapes/path.cljc
index 5f8e2d8f8..1c9f26466 100644
--- a/common/app/common/geom/shapes/path.cljc
+++ b/common/app/common/geom/shapes/path.cljc
@@ -146,3 +146,18 @@
(not (nil? c2x)) (set-tr :c2x :c2y)))]
(mapv #(update % :params transform-params) content)))
+
+(defn apply-content-modifiers [content modifiers]
+ (let [red-fn (fn [content [index params]]
+ (if (contains? content index)
+ (cond-> content
+ (:x params) (update-in [index :params :x] + (:x params))
+ (:y params) (update-in [index :params :y] + (:y params))
+
+ (:c1x params) (update-in [index :params :c1x] + (:c1x params))
+ (:c1y params) (update-in [index :params :c1y] + (:c1y params))
+
+ (:c2x params) (update-in [index :params :c2x] + (:c2x params))
+ (:c2y params) (update-in [index :params :c2y] + (:c2y params)))
+ content))]
+ (reduce red-fn content modifiers)))
diff --git a/frontend/resources/images/icons/nodes-add.svg b/frontend/resources/images/icons/nodes-add.svg
new file mode 100644
index 000000000..9c5ecf93a
--- /dev/null
+++ b/frontend/resources/images/icons/nodes-add.svg
@@ -0,0 +1,3 @@
+
diff --git a/frontend/resources/images/icons/nodes-corner.svg b/frontend/resources/images/icons/nodes-corner.svg
new file mode 100644
index 000000000..295e316ab
--- /dev/null
+++ b/frontend/resources/images/icons/nodes-corner.svg
@@ -0,0 +1,3 @@
+
diff --git a/frontend/resources/images/icons/nodes-curve.svg b/frontend/resources/images/icons/nodes-curve.svg
new file mode 100644
index 000000000..b12913fc5
--- /dev/null
+++ b/frontend/resources/images/icons/nodes-curve.svg
@@ -0,0 +1,3 @@
+
diff --git a/frontend/resources/images/icons/nodes-join.svg b/frontend/resources/images/icons/nodes-join.svg
new file mode 100644
index 000000000..551451cb9
--- /dev/null
+++ b/frontend/resources/images/icons/nodes-join.svg
@@ -0,0 +1,3 @@
+
diff --git a/frontend/resources/images/icons/nodes-merge.svg b/frontend/resources/images/icons/nodes-merge.svg
new file mode 100644
index 000000000..5e0d9c336
--- /dev/null
+++ b/frontend/resources/images/icons/nodes-merge.svg
@@ -0,0 +1,3 @@
+
diff --git a/frontend/resources/images/icons/nodes-remove.svg b/frontend/resources/images/icons/nodes-remove.svg
new file mode 100644
index 000000000..e00ecd534
--- /dev/null
+++ b/frontend/resources/images/icons/nodes-remove.svg
@@ -0,0 +1,3 @@
+
diff --git a/frontend/resources/images/icons/nodes-separate.svg b/frontend/resources/images/icons/nodes-separate.svg
new file mode 100644
index 000000000..4e188e3cb
--- /dev/null
+++ b/frontend/resources/images/icons/nodes-separate.svg
@@ -0,0 +1,3 @@
+
diff --git a/frontend/resources/images/icons/nodes-snap.svg b/frontend/resources/images/icons/nodes-snap.svg
new file mode 100644
index 000000000..1bd5edac4
--- /dev/null
+++ b/frontend/resources/images/icons/nodes-snap.svg
@@ -0,0 +1,3 @@
+
diff --git a/frontend/resources/styles/main/partials/workspace.scss b/frontend/resources/styles/main/partials/workspace.scss
index ccccec01e..69282ab40 100644
--- a/frontend/resources/styles/main/partials/workspace.scss
+++ b/frontend/resources/styles/main/partials/workspace.scss
@@ -225,3 +225,76 @@
padding: $x-small;
}
}
+
+.viewport-actions {
+ position: absolute;
+ margin-left: auto;
+ width: 100%;
+ margin-top: 2rem;
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ align-items: center;
+
+ .path-actions {
+ display: flex;
+ flex-direction: row;
+ background: white;
+ border-radius: 3px;
+ padding: 0.5rem;
+ border: 1px solid $color-gray-20;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
+ }
+
+ .viewport-actions-group {
+ display: flex;
+ flex-direction: row;
+ border-right: 1px solid $color-gray-20;
+ }
+
+ .viewport-actions-entry {
+ width: 27px;
+ height: 20px;
+ margin: 0 0.25rem;
+ cursor: pointer;
+
+ svg {
+ width: 27px;
+ height: 20px;
+ }
+
+ &:hover svg {
+ fill: $color-primary;
+ }
+
+ &.disabled {
+ opacity: 0.3;
+
+ &:hover svg {
+ fill: initial;
+ }
+ }
+ }
+
+ .viewport-actions-entry-wide {
+ width: 27px;
+ height: 20px;
+
+ svg {
+ width: 27px;
+ height: 20px;
+ }
+ }
+
+ .path-actions > :first-child .viewport-actions-entry {
+ margin-left: 0;
+ }
+
+ .path-actions > :last-child {
+ border: none;
+ }
+
+ .path-actions > :last-child .viewport-actions-entry {
+ margin-right: 0;
+ }
+}
diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs
index 70cf54a20..112f98afa 100644
--- a/frontend/src/app/main/data/workspace.cljs
+++ b/frontend/src/app/main/data/workspace.cljs
@@ -32,6 +32,7 @@
[app.main.data.workspace.texts :as dwtxt]
[app.main.data.workspace.transforms :as dwt]
[app.main.data.workspace.drawing :as dwd]
+ [app.main.data.workspace.drawing.path :as dwdp]
[app.main.repo :as rp]
[app.main.store :as st]
[app.main.streams :as ms]
@@ -1629,6 +1630,8 @@
(def add-shape dwc/add-shape)
(def start-edition-mode dwc/start-edition-mode)
+(defn start-path-edit [id] (dwdp/start-path-edit id))
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Shortcuts
diff --git a/frontend/src/app/main/data/workspace/drawing.cljs b/frontend/src/app/main/data/workspace/drawing.cljs
index 63b0668fc..7987488a2 100644
--- a/frontend/src/app/main/data/workspace/drawing.cljs
+++ b/frontend/src/app/main/data/workspace/drawing.cljs
@@ -99,6 +99,6 @@
(box/handle-drawing-box))))))
;; Export
-(def close-drawing-path path/close-drawing-path)
+#_(def close-drawing-path path/close-drawing-path)
diff --git a/frontend/src/app/main/data/workspace/drawing/path.cljs b/frontend/src/app/main/data/workspace/drawing/path.cljs
index 420ddda6f..4d956d7b6 100644
--- a/frontend/src/app/main/data/workspace/drawing/path.cljs
+++ b/frontend/src/app/main/data/workspace/drawing/path.cljs
@@ -17,7 +17,9 @@
[app.util.data :as d]
[app.util.geom.path :as ugp]
[app.main.streams :as ms]
- [app.main.data.workspace.drawing.common :as common]))
+ [app.main.data.workspace.common :as dwc]
+ [app.main.data.workspace.drawing.common :as common]
+ [app.common.geom.shapes.path :as gsp]))
;;;;
@@ -149,14 +151,14 @@
(update [_ state]
(-> state
(assoc-in [:workspace-drawing :object :initialized?] true)
- (assoc-in [:workspace-drawing :object :last-point] nil)))))
+ (assoc-in [:workspace-local :edit-path :last-point] nil)))))
(defn finish-path []
(ptk/reify ::finish-path
ptk/UpdateEvent
(update [_ state]
(-> state
- (assoc-in [:workspace-drawing :object :last-point] nil)
+ (update :workspace-local dissoc :edit-path)
(update-in [:workspace-drawing :object] calculate-selrect)))))
(defn preview-next-point [{:keys [x y]}]
@@ -164,9 +166,9 @@
ptk/UpdateEvent
(update [_ state]
(let [position (gpt/point x y)
- {:keys [last-point prev-handler] :as shape} (get-in state [:workspace-drawing :object])
+ {:keys [last-point prev-handler] :as shape} (get-in state [:workspace-local :edit-path])
command (next-node shape position last-point prev-handler)]
- (assoc-in state [:workspace-drawing :object :preview] command)))))
+ (assoc-in state [:workspace-local :edit-path :preview] command)))))
(defn add-node [{:keys [x y]}]
(ptk/reify ::add-node
@@ -174,14 +176,11 @@
(update [_ state]
(let [position (gpt/point x y)
- {:keys [last-point prev-handler]} (get-in state [:workspace-drawing :object])]
- (update-in
- state
- [:workspace-drawing :object]
- #(-> %
- (append-node position last-point prev-handler)
- (assoc :last-point position)
- (dissoc :prev-handler)))))))
+ {:keys [last-point prev-handler]} (get-in state [:workspace-local :edit-path])]
+ (-> state
+ (assoc-in [:workspace-local :edit-path :last-point] position)
+ (update-in [:workspace-local :edit-path] dissoc :prev-handler)
+ (update-in [:workspace-drawing :object] append-node position last-point prev-handler))))))
(defn drag-handler [{:keys [x y]}]
(ptk/reify ::drag-handler
@@ -193,16 +192,16 @@
index (dec (count (:content shape)))]
(-> state
(update-in [:workspace-drawing :object] move-handler index :next true position)
- (assoc-in [:workspace-drawing :object :drag-handler] position))))))
+ (assoc-in [:workspace-local :edit-path :drag-handler] position))))))
(defn finish-drag []
(ptk/reify ::finish-drag
ptk/UpdateEvent
(update [_ state]
- (let [handler (get-in state [:workspace-drawing :object :drag-handler])]
+ (let [handler (get-in state [:workspace-local :edit-path :drag-handler])]
(-> state
- (update-in [:workspace-drawing :object] dissoc :drag-handler)
- (assoc-in [:workspace-drawing :object :prev-handler] handler))))))
+ (update-in [:workspace-local :edit-path] dissoc :drag-handler)
+ (assoc-in [:workspace-local :edit-path :prev-handler] handler))))))
(defn make-click-stream
[stream down-event]
@@ -238,7 +237,6 @@
ptk/WatchEvent
(watch [_ state stream]
- ;; clicks stream<[MouseEvent, Position]>
(let [mouse-down (->> stream (rx/filter ms/mouse-down?))
finish-events (->> stream (rx/filter finish-event?))
@@ -266,10 +264,7 @@
(rx/merge mousemove-events
mousedown-events)
(rx/of (finish-path))
- (rx/of common/handle-finish-drawing)))
-
-
- )))
+ (rx/of common/handle-finish-drawing))))))
#_(def handle-drawing-path
(ptk/reify ::handle-drawing-path
@@ -331,7 +326,7 @@
(rx/of finish-drawing-path
common/handle-finish-drawing)))))))
-(defn close-drawing-path []
+#_(defn close-drawing-path []
(ptk/reify ::close-drawing-path
ptk/UpdateEvent
(update [_ state]
@@ -340,3 +335,121 @@
ptk/WatchEvent
(watch [_ state stream]
(rx/of ::end-path-drawing))))
+
+
+(defn stop-path-edit []
+ (ptk/reify ::stop-path-edit
+ ptk/UpdateEvent
+ (update [_ state]
+ (update state :workspace-local dissoc :edit-path))))
+
+(defn start-path-edit
+ [id]
+ (ptk/reify ::start-path-edit
+ ptk/UpdateEvent
+ (update [_ state]
+ (assoc-in state [:workspace-local :edit-path] {}))
+
+ ptk/WatchEvent
+ (watch [_ state stream]
+ (->> stream
+ (rx/filter #(= % :interrupt))
+ (rx/take 1)
+ (rx/map #(stop-path-edit))))))
+
+(defn modify-point [index dx dy]
+ (ptk/reify ::modify-point
+
+ ptk/UpdateEvent
+ (update [_ state]
+
+ (-> state
+ (update-in [:workspace-local :edit-path :content-modifiers (inc index)] assoc
+ :c1x dx :c1y dy)
+ (update-in [:workspace-local :edit-path :content-modifiers index] assoc
+ :x dx :y dy :c2x dx :c2y dy)
+ ))))
+
+(defn modify-handler [index type dx dy]
+ (ptk/reify ::modify-point
+ ptk/UpdateEvent
+ (update [_ state]
+ (let [s1 (if (= type :prev) -1 1)
+ s2 (if (= type :prev) 1 -1)]
+ (-> state
+ (update-in [:workspace-local :edit-path :content-modifiers (inc index)] assoc
+ :c1x (* s1 dx) :c1y (* s1 dy))
+ (update-in [:workspace-local :edit-path :content-modifiers index] assoc
+ :c2x (* s2 dx) :c2y (* s2 dy) ))
+ ))))
+
+(defn apply-content-modifiers []
+ (ptk/reify ::apply-content-modifiers
+ ;;ptk/UpdateEvent
+ ;;(update [_ state]
+ ;; (update-in state [:workspace-local :edit-path] dissoc :content-modifiers))
+
+ ptk/WatchEvent
+ (watch [_ state stream]
+ (let [id (get-in state [:workspace-local :edition])
+ page-id (:current-page-id state)
+ old-content (get-in state [:workspace-data :pages-index page-id :objects id :content])
+ old-selrect (get-in state [:workspace-data :pages-index page-id :objects id :selrect])
+ content-modifiers (get-in state [:workspace-local :edit-path :content-modifiers])
+ new-content (gsp/apply-content-modifiers old-content content-modifiers)
+ new-selrect (gsh/content->selrect new-content)
+ rch [{:type :mod-obj
+ :id id
+ :page-id page-id
+ :operations [{:type :set :attr :content :val new-content}
+ {:type :set :attr :selrect :val new-selrect}]}]
+
+ uch [{:type :mod-obj
+ :id id
+ :page-id page-id
+ :operations [{:type :set :attr :content :val old-content}
+ {:type :set :attr :selrect :val old-selrect}]}]]
+
+ (rx/of (dwc/commit-changes rch uch {:commit-local? true})
+ (fn [state] (update-in state [:workspace-local :edit-path] dissoc :content-modifiers)))))))
+
+(defn start-move-path-point
+ [index]
+ (ptk/reify ::start-move-path-point
+ ptk/WatchEvent
+ (watch [_ state stream]
+ (let [start-point @ms/mouse-position
+ start-delta-x (get-in state [:workspace-local :edit-path :content-modifiers index :x] 0)
+ start-delta-y (get-in state [:workspace-local :edit-path :content-modifiers index :y] 0)]
+ (rx/concat
+ (->> ms/mouse-position
+ (rx/take-until (->> stream (rx/filter ms/mouse-up?)))
+ (rx/map #(modify-point
+ index
+ (+ start-delta-x (- (:x %) (:x start-point)))
+ (+ start-delta-y (- (:y %) (:y start-point))))))
+ (rx/concat (rx/of (apply-content-modifiers)))
+ )))))
+
+(defn start-move-handler
+ [index type]
+ (ptk/reify ::start-move-handler
+ ptk/WatchEvent
+ (watch [_ state stream]
+ (let [[cx cy] (if (= :prev type) [:c2x :c2y] [:c1x :c1y])
+ cidx (if (= :prev type) index (inc index))
+
+ start-point @ms/mouse-position
+ start-delta-x (get-in state [:workspace-local :edit-path :content-modifiers cidx cx] 0)
+ start-delta-y (get-in state [:workspace-local :edit-path :content-modifiers cidx cy] 0)]
+
+ (rx/concat
+ (->> ms/mouse-position
+ (rx/take-until (->> stream (rx/filter ms/mouse-up?)))
+ (rx/map #(modify-handler
+ index
+ type
+ (+ start-delta-x (- (:x %) (:x start-point)))
+ (+ start-delta-y (- (:y %) (:y start-point)))))
+ )
+ (rx/concat (rx/of (apply-content-modifiers))))))))
diff --git a/frontend/src/app/main/ui/icons.cljs b/frontend/src/app/main/ui/icons.cljs
index 5f9fc0705..0550c4a4a 100644
--- a/frontend/src/app/main/ui/icons.cljs
+++ b/frontend/src/app/main/ui/icons.cljs
@@ -128,6 +128,14 @@
(def checkbox-checked (icon-xref :checkbox-checked))
(def checkbox-unchecked (icon-xref :checkbox-unchecked))
(def code (icon-xref :code))
+(def nodes-add (icon-xref :nodes-add))
+(def nodes-corner (icon-xref :nodes-corner))
+(def nodes-curve (icon-xref :nodes-curve))
+(def nodes-join (icon-xref :nodes-join))
+(def nodes-merge (icon-xref :nodes-merge))
+(def nodes-remove (icon-xref :nodes-remove))
+(def nodes-separate (icon-xref :nodes-separate))
+(def nodes-snap (icon-xref :nodes-snap))
(def loader-pencil
(mf/html
diff --git a/frontend/src/app/main/ui/workspace.cljs b/frontend/src/app/main/ui/workspace.cljs
index ea77a68b9..dfca19099 100644
--- a/frontend/src/app/main/ui/workspace.cljs
+++ b/frontend/src/app/main/ui/workspace.cljs
@@ -29,7 +29,7 @@
[app.main.ui.workspace.rules :refer [horizontal-rule vertical-rule]]
[app.main.ui.workspace.scroll :as scroll]
[app.main.ui.workspace.sidebar :refer [left-sidebar right-sidebar]]
- [app.main.ui.workspace.viewport :refer [viewport coordinates]]
+ [app.main.ui.workspace.viewport :refer [viewport viewport-actions coordinates]]
[app.util.dom :as dom]
[beicon.core :as rx]
[cuerdas.core :as str]
@@ -65,6 +65,7 @@
(when (contains? layout :rules)
[:& workspace-rules {:local local}])
+ [:& viewport-actions]
[:& viewport {:file file
:local local
:layout layout}]]]
diff --git a/frontend/src/app/main/ui/workspace/selection.cljs b/frontend/src/app/main/ui/workspace/selection.cljs
index 120bcd543..5f9708a58 100644
--- a/frontend/src/app/main/ui/workspace/selection.cljs
+++ b/frontend/src/app/main/ui/workspace/selection.cljs
@@ -31,7 +31,8 @@
[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.measurements :as msr]
+ [app.main.ui.workspace.shapes.path :refer [path-editor]]))
(def rotation-handler-size 25)
(def resize-point-radius 4)
@@ -366,8 +367,13 @@
[:& text-edition-selection-handlers {:shape shape
:zoom zoom
:color color}]
- (and (or (= type :path)
- (= type :curve))
+
+ (= (= type :path)
+ (= edition (:id shape)))
+ [:& path-editor {:zoom zoom
+ :shape shape}]
+
+ (and (= type :curve)
(= edition (:id shape)))
[:& path-edition-selection-handlers {:shape shape
:zoom zoom
diff --git a/frontend/src/app/main/ui/workspace/shapes/common.cljs b/frontend/src/app/main/ui/workspace/shapes/common.cljs
index 302c0cf48..af268cd69 100644
--- a/frontend/src/app/main/ui/workspace/shapes/common.cljs
+++ b/frontend/src/app/main/ui/workspace/shapes/common.cljs
@@ -23,6 +23,7 @@
(defn- on-mouse-down
[event {:keys [id type] :as shape}]
(let [selected @refs/selected-shapes
+ edition @refs/selected-edition
selected? (contains? selected id)
drawing? @refs/selected-drawing-tool
button (.-which (.-nativeEvent event))]
@@ -35,9 +36,8 @@
nil
(= type :frame)
- (when selected?
- (dom/stop-propagation event)
- (st/emit! (dw/start-move-selected)))
+ (do (dom/stop-propagation event)
+ (st/emit! (dw/start-move-selected)))
:else
(do
@@ -50,7 +50,8 @@
(st/emit! (dw/deselect-all)))
(st/emit! (dw/select-shape id))))
- (st/emit! (dw/start-move-selected)))))))
+ (when (not= edition id)
+ (st/emit! (dw/start-move-selected))))))))
(defn on-context-menu
[event shape]
diff --git a/frontend/src/app/main/ui/workspace/shapes/path.cljs b/frontend/src/app/main/ui/workspace/shapes/path.cljs
index d1f761ab5..338a45f79 100644
--- a/frontend/src/app/main/ui/workspace/shapes/path.cljs
+++ b/frontend/src/app/main/ui/workspace/shapes/path.cljs
@@ -10,22 +10,40 @@
(ns app.main.ui.workspace.shapes.path
(:require
[rumext.alpha :as mf]
- [app.common.data :as d]
+ [okulary.core :as l]
+ [app.util.data :as d]
[app.util.dom :as dom]
[app.util.timers :as ts]
+ [app.main.refs :as refs]
[app.main.streams :as ms]
[app.main.constants :as c]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.data.workspace :as dw]
[app.main.data.workspace.drawing :as dr]
+ [app.main.data.workspace.drawing.path :as drp]
[app.main.ui.keyboard :as kbd]
[app.main.ui.shapes.path :as path]
[app.main.ui.shapes.filters :as filters]
[app.main.ui.shapes.shape :refer [shape-container]]
[app.main.ui.workspace.shapes.common :as common]
[app.util.geom.path :as ugp]
- [app.common.geom.shapes.path :as gsp]))
+ [app.common.geom.point :as gpt]
+ [app.common.geom.shapes.path :as gsp]
+ [app.main.ui.cursors :as cur]
+ [app.main.ui.icons :as i]))
+
+(def primary-color "#1FDEA7")
+(def secondary-color "#DB00FF")
+(def black-color "#000000")
+(def white-color "#FFFFFF")
+(def gray-color "#B1B2B5")
+
+(def edit-path-ref
+ (l/derived :edit-path refs/workspace-local))
+
+(def content-modifiers-ref
+ (l/derived :content-modifiers edit-path-ref))
(mf/defc path-wrapper
{::mf/wrap-props false}
@@ -43,12 +61,15 @@
on-double-click (mf/use-callback
(mf/deps shape)
(fn [event]
- (prn "?? PATH")
- (when (and (not (::dr/initialized? shape)) (hover? (:id shape)))
+ (when (not (::dr/initialized? shape))
(do
(dom/stop-propagation event)
(dom/prevent-default event)
- (st/emit! (dw/start-edition-mode (:id shape)))))))]
+ (st/emit! (dw/start-edition-mode (:id shape))
+ (dw/start-path-edit (:id shape)))))))
+
+ content-modifiers (mf/deref content-modifiers-ref)
+ shape (update shape :content gsp/apply-content-modifiers content-modifiers)]
[:> shape-container {:shape shape
:on-double-click on-double-click
@@ -57,75 +78,142 @@
[:& path/path-shape {:shape shape
:background? true}]]))
+(mf/defc path-actions [{:keys [shape]}]
+ [:div.path-actions
+ [:div.viewport-actions-group
+ [:div.viewport-actions-entry i/nodes-add]
+ [:div.viewport-actions-entry i/nodes-remove]]
-(mf/defc path-handler [{:keys [point handler zoom selected]}]
+ [:div.viewport-actions-group
+ [:div.viewport-actions-entry i/nodes-merge]
+ [:div.viewport-actions-entry i/nodes-join]
+ [:div.viewport-actions-entry i/nodes-separate]]
+
+ [:div.viewport-actions-group
+ [:div.viewport-actions-entry i/nodes-corner]
+ [:div.viewport-actions-entry i/nodes-curve]]
+
+ [:div.viewport-actions-group
+ [:div.viewport-actions-entry i/nodes-snap]]])
+
+
+(mf/defc path-preview [{:keys [zoom command from]}]
+ (when (not= :move-to (:command command))
+ [:path {:style {:fill "transparent"
+ :stroke secondary-color
+ :stroke-width (/ 1 zoom)}
+ :d (ugp/content->path [{:command :move-to
+ :params {:x (:x from)
+ :y (:y from)}}
+ command])}]))
+
+(mf/defc path-point [{:keys [index position stroke-color fill-color zoom]}]
+ (let [{:keys [x y]} position
+ on-click (fn [event]
+ (dom/stop-propagation event)
+ (dom/prevent-default event))
+ on-mouse-down (fn [event]
+ (dom/stop-propagation event)
+ (dom/prevent-default event)
+ (st/emit! (drp/start-move-path-point index)))]
+ [:circle
+ {:cx x
+ :cy y
+ :r (/ 3 zoom)
+ :on-click on-click
+ :on-mouse-down on-mouse-down
+ :style {:cursor cur/resize-alt
+ :stroke-width (/ 1 zoom)
+ :stroke (or stroke-color black-color)
+ :fill (or fill-color white-color)}}]))
+
+(mf/defc path-handler [{:keys [index point handler zoom selected type]}]
(when (and point handler)
- (let [{:keys [x y]} handler]
- [:g.handler
+ (let [{:keys [x y]} handler
+ on-click (fn [event]
+ (dom/stop-propagation event)
+ (dom/prevent-default event))
+ on-mouse-down (fn [event]
+ (dom/stop-propagation event)
+ (dom/prevent-default event)
+ (st/emit! (drp/start-move-handler index type)))]
+ [:g.handler {:class (name type)}
[:line
{:x1 (:x point)
:y1 (:y point)
:x2 x
:y2 y
- :style {:stroke "#B1B2B5"
+ :style {:stroke gray-color
:stroke-width (/ 1 zoom)}}]
[:rect
{:x (- x (/ 3 zoom))
:y (- y (/ 3 zoom))
:width (/ 6 zoom)
:height (/ 6 zoom)
- :style {:stroke-width (/ 1 zoom)
- :stroke (if selected "#000000" "#1FDEA7")
- :fill (if selected "#1FDEA7" "#FFFFFF")}}]])))
+ :on-click on-click
+ :on-mouse-down on-mouse-down
+ :style {:cursor cur/resize-alt
+ :stroke-width (/ 1 zoom)
+ :stroke (if selected black-color primary-color)
+ :fill (if selected primary-color white-color)}}]])))
(mf/defc path-editor
[{:keys [shape zoom]}]
- (let [points (gsp/content->points (:content shape))
- drag-handler (:drag-handler shape)
- prev-handler (:prev-handler shape)
- last-command (last (:content shape))
- selected false
+ (let [{:keys [content]} shape
+ {:keys [drag-handler prev-handler preview content-modifiers]} (mf/deref edit-path-ref)
+ content (gsp/apply-content-modifiers content content-modifiers)
+ points (gsp/content->points content)
+ last-command (last content)
last-p (last points)
- handlers (ugp/extract-handlers (:content shape))
- handlers (if (and prev-handler (not drag-handler))
- (conj handlers {:point last-p :prev prev-handler})
- handlers)
- ]
+ selected false]
[:g.path-editor
- (when (and (:preview shape) (not (:drag-handler shape)))
- [:*
- [:path {:style {:fill "transparent"
- :stroke "#DB00FF"
- :stroke-width (/ 1 zoom)}
- :d (ugp/content->path [{:command :move-to
- :params {:x (:x last-p)
- :y (:y last-p)}}
- (:preview shape)])}]
- [:circle
- {:cx (-> shape :preview :params :x)
- :cy (-> shape :preview :params :y)
- :r (/ 3 zoom)
- :style {:stroke-width (/ 1 zoom)
- :stroke "#DB00FF"
- :fill "#FFFFFF"}}]])
+ (when (and preview (not drag-handler))
+ [:g.preview {:style {:pointer-events "none"}}
+ [:& path-preview {:command preview
+ :from last-p
+ :zoom zoom}]
+ [:& path-point {:position (:params preview)
+ :fill-color secondary-color
+ :zoom zoom}]])
- (for [{:keys [point prev next]} handlers]
- [:*
- [:& path-handler {:point point
- :handler prev
- :zoom zoom
- :type :prev
- :selected false}]
- [:& path-handler {:point point
- :handler next
- :zoom zoom
- :type :next
- :selected false}]])
+ (for [[index [cmd next]] (d/enumerate (d/with-next content))]
+ (let [point (gpt/point (:params cmd))]
+ [:g.path-node
+ (when (= :curve-to (:command cmd))
+ [:& path-handler {:point point
+ :handler (gpt/point (-> cmd :params :c2x) (-> cmd :params :c2y))
+ :zoom zoom
+ :type :prev
+ :index index
+ :selected false}])
+
+ (when (= :curve-to (:command next))
+ [:& path-handler {:point point
+ :handler (gpt/point (-> next :params :c1x) (-> next :params :c1y))
+ :zoom zoom
+ :type :next
+ :index index
+ :selected false}])
+
+ (when (and (= index (dec (count content)))
+ prev-handler (not drag-handler))
+ [:& path-handler {:point point
+ :handler prev-handler
+ :zoom zoom
+ :type :prev
+ :index index
+ :selected false}])
+
+ [:& path-point {:position point
+ :stroke-color (when-not selected primary-color)
+ :fill-color (when selected primary-color)
+ :index index
+ :zoom zoom}]]))
(when drag-handler
- [:*
+ [:g.drag-handler
(when (not= :move-to (:command last-command))
[:& path-handler {:point last-p
:handler (ugp/opposite-handler last-p drag-handler)
@@ -136,14 +224,4 @@
:handler drag-handler
:zoom zoom
:type :drag
- :selected false}]])
-
- (for [{:keys [x y] :as point} points]
- [:circle
- {:cx x
- :cy y
- :r (/ 3 zoom)
- :style {:stroke-width (/ 1 zoom)
- :stroke (if selected "#000000" "#1FDEA7")
- :fill (if selected "#1FDEA7" "#FFFFFF")}
- }])]))
+ :selected false}]])]))
diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs
index a2e7461ac..547ceb36c 100644
--- a/frontend/src/app/main/ui/workspace/viewport.cljs
+++ b/frontend/src/app/main/ui/workspace/viewport.cljs
@@ -1,4 +1,4 @@
-;; This Source Code Form is subject to the terms of the Mozilla Public
+; 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/.
;;
@@ -52,7 +52,8 @@
[goog.events :as events]
[potok.core :as ptk]
[promesa.core :as p]
- [rumext.alpha :as mf])
+ [rumext.alpha :as mf]
+ [app.main.ui.workspace.shapes.path :refer [path-actions]])
(:import goog.events.EventType))
;; --- Coordinates Widget
@@ -222,7 +223,6 @@
drawing-obj (:object drawing)
zoom (or zoom 1)
-
on-mouse-down
(mf/use-callback
(mf/deps drawing-tool edition)
@@ -234,15 +234,13 @@
alt? (kbd/alt? event)]
(st/emit! (ms/->MouseEvent :down ctrl? shift? alt?))
(cond
- (and (= 1 (.-which event)))
-
+ (and (= 1 (.-which event)) (not edition))
(if drawing-tool
(when (not (#{:comments :path} drawing-tool))
(st/emit! (dd/start-drawing drawing-tool)))
(st/emit! dw/handle-selection))
- (and (not edition)
- (= 2 (.-which event)))
+ (and (= 2 (.-which event)))
(handle-viewport-positioning viewport-ref)))))
on-context-menu
@@ -294,12 +292,16 @@
on-double-click
(mf/use-callback
+ (mf/deps edition)
(fn [event]
(dom/stop-propagation event)
(let [ctrl? (kbd/ctrl? event)
shift? (kbd/shift? event)
alt? (kbd/alt? event)]
- (st/emit! (ms/->MouseEvent :double-click ctrl? shift? alt?)))))
+ (st/emit! (ms/->MouseEvent :double-click ctrl? shift? alt?))
+
+ (if edition
+ (st/emit! dw/clear-edition-mode)))))
on-key-down
(mf/use-callback
@@ -610,3 +612,13 @@
(when (= options-mode :prototype)
[:& interactions {:selected selected}])]]))
+
+(mf/defc viewport-actions []
+ (let [edition (mf/deref refs/selected-edition)
+ selected (mf/deref refs/selected-objects)
+ shape (-> selected first)]
+ (when (and (= (count selected) 1)
+ (= (:id shape) edition)
+ (= :path (:type shape)))
+ [:div.viewport-actions
+ [:& path-actions {:shape shape}]])))
diff --git a/frontend/src/app/util/geom/path.cljs b/frontend/src/app/util/geom/path.cljs
index 1da9ce3a0..e8d40fff9 100644
--- a/frontend/src/app/util/geom/path.cljs
+++ b/frontend/src/app/util/geom/path.cljs
@@ -213,9 +213,3 @@
opposite (gpt/add point (gpt/negate phv))]
opposite))
-(defn extract-handlers [content]
- (let [extract (fn [{param1 :params :as cmd1} {param2 :params :as cmd2}]
- {:point (gpt/point (:x param1) (:y param1))
- :prev (when (:c2x param1) (gpt/point (:c2x param1) (:c2y param1)))
- :next (when (:c1x param2) (gpt/point (:c1x param2) (:c1y param2)))})]
- (map extract content (d/concat [] (rest content) [nil]))))