Adding shapes over the selected shapes

This commit is contained in:
alonso.torres 2021-01-15 15:15:02 +01:00 committed by Andrey Antukh
parent 8f57ab343c
commit 20731be1a4
9 changed files with 172 additions and 45 deletions

View file

@ -58,6 +58,7 @@
(d/export helpers/frame-id-by-position) (d/export helpers/frame-id-by-position)
(d/export helpers/set-touched-group) (d/export helpers/set-touched-group)
(d/export helpers/touched-group?) (d/export helpers/touched-group?)
(d/export helpers/get-base-shape)
;; Process changes ;; Process changes
(d/export changes/process-changes) (d/export changes/process-changes)

View file

@ -311,3 +311,16 @@
[shape group] [shape group]
((or (:touched shape) #{}) group)) ((or (:touched shape) #{}) group))
(defn get-base-shape
"Selects the shape that will be the base to add the shapes over"
[objects selected]
(let [;; Gets the tree-index for all the shapes
indexed-shapes (indexed-shapes objects)
;; Filters the selected and retrieve a list of ids
sorted-ids (->> indexed-shapes
(filter (comp selected second))
(map second))]
;; The first id will be the top-most
(get objects (first sorted-ids))))

View file

@ -1172,7 +1172,6 @@
(if (= :image (:type item)) (if (= :image (:type item))
(let [img-part {:id (:id metadata) (let [img-part {:id (:id metadata)
:name (:name item) :name (:name item)
:file-name (path/baseName (:path metadata))
:file-data (::data item)}] :file-data (::data item)}]
(update res :images conj img-part)) (update res :images conj img-part))
res))) res)))
@ -1252,7 +1251,7 @@
(js/console.error "ERROR" e)))))))) (js/console.error "ERROR" e))))))))
(defn paste-from-event (defn paste-from-event
[event] [event in-viewport?]
(ptk/reify ::paste-from-event (ptk/reify ::paste-from-event
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (watch [_ state stream]
@ -1263,7 +1262,7 @@
decoded-data (and (t/transit? text-data) (t/decode text-data))] decoded-data (and (t/transit? text-data) (t/decode text-data))]
(cond (cond
(seq image-data) (rx/from (map paste-image image-data)) (seq image-data) (rx/from (map paste-image image-data))
decoded-data (rx/of (paste-shape decoded-data)) decoded-data (rx/of (paste-shape decoded-data in-viewport?))
(string? text-data) (rx/of (paste-text text-data)) (string? text-data) (rx/of (paste-text text-data))
:else (rx/empty))) :else (rx/empty)))
(catch :default err (catch :default err
@ -1277,9 +1276,8 @@
(= :frame (get-in objects [(first selected) :type])))))) (= :frame (get-in objects [(first selected) :type]))))))
(defn- paste-shape (defn- paste-shape
[{:keys [selected objects images] :as data}] [{:keys [selected objects images] :as data} in-viewport?]
(letfn [ (letfn [;; Given a file-id and img (part generated by the
;; Given a file-id and img (part generated by the
;; copy-selected event), uploads the new media. ;; copy-selected event), uploads the new media.
(upload-media [file-id imgpart] (upload-media [file-id imgpart]
(->> (http/data-url->blob (:file-data imgpart)) (->> (http/data-url->blob (:file-data imgpart))
@ -1289,7 +1287,7 @@
:file-id file-id :file-id file-id
:content blob :content blob
:is-local true})) :is-local true}))
(rx/mapcat #(rp/mutation! :upload-media-object %)) (rx/mapcat #(rp/mutation! :upload-file-media-object %))
(rx/map (fn [media] (rx/map (fn [media]
(assoc media :prev-id (:id imgpart)))))) (assoc media :prev-id (:id imgpart))))))
@ -1306,35 +1304,82 @@
mdata))) mdata)))
item)) item))
(calculate-paste-position [state mouse-pos in-viewport?]
(let [page-objects (dwc/lookup-page-objects state)
selected-objs (map #(get objects %) selected)
has-frame? (d/seek #(= (:type %) :frame) selected-objs)
page-selected (get-in state [:workspace-local :selected])
wrapper (gsh/selection-rect selected-objs)
orig-pos (gpt/point (:x1 wrapper) (:y1 wrapper))]
(cond
(and (selected-frame? state) (not has-frame?))
(let [frame-id (first page-selected)
delta (get page-objects frame-id)]
[frame-id frame-id delta])
(empty? page-selected)
(let [frame-id (cp/frame-id-by-position page-objects mouse-pos)
delta (gpt/subtract mouse-pos orig-pos)]
[frame-id frame-id delta])
:else
(let [base (cp/get-base-shape page-objects page-selected)
index (cp/position-on-parent (:id base) page-objects)
frame-id (:frame-id base)
parent-id (:parent-id base)
delta (if in-viewport?
(gpt/subtract mouse-pos orig-pos)
(gpt/subtract (gpt/point (:selrect base)) orig-pos))]
[frame-id parent-id delta index]))))
;; Change the indexes if the paste is done with an element selected
(change-add-obj-index [objects selected index change]
(let [set-index (fn [[result index] id]
[(assoc result id index) (inc index)])
map-ids (when index
(->> (vals objects)
(filter #(not (selected (:parent-id %))))
(map :id)
(reduce set-index [{} (inc index)])
first))]
(if (and (= :add-obj (:type change))
(contains? map-ids (:old-id change)))
(assoc change :index (get map-ids (:old-id change)))
change)))
;; Procceed with the standard shape paste procediment. ;; Procceed with the standard shape paste procediment.
(do-paste [state mouse-pos media] (do-paste [state mouse-pos media]
(let [media-idx (d/index-by :prev-id media) (let [media-idx (d/index-by :prev-id media)
selected-objs (map #(get objects %) selected)
wrapper (gsh/selection-rect selected-objs)
orig-pos (gpt/point (:x1 wrapper) (:y1 wrapper))
page-id (:current-page-id state) page-id (:current-page-id state)
page-objects (dwc/lookup-page-objects state page-id) ;; Calculate position for the pasted elements
page-selected (get-in state [:workspace-local :selected]) [frame-id parent-id delta index] (calculate-paste-position state mouse-pos in-viewport?)
[frame-id delta] objects (->> objects
(if (selected-frame? state) (d/mapm (fn [_ shape]
[(first page-selected) (-> shape
(get page-objects (first page-selected))] (assoc :frame-id frame-id)
[(cp/frame-id-by-position page-objects mouse-pos) (assoc :parent-id parent-id)))))
(gpt/subtract mouse-pos orig-pos)])
objects (d/mapm (fn [_ v] (assoc v :frame-id frame-id :parent-id frame-id)) objects)
page-id (:current-page-id state) page-id (:current-page-id state)
unames (-> (dwc/lookup-page-objects state page-id) unames (-> (dwc/lookup-page-objects state page-id)
(dwc/retrieve-used-names)) (dwc/retrieve-used-names))
rchanges (dws/prepare-duplicate-changes objects page-id unames selected delta) rchanges (->> (dws/prepare-duplicate-changes objects page-id unames selected delta)
rchanges (mapv (partial process-rchange media-idx) rchanges) (mapv (partial process-rchange media-idx))
(mapv (partial change-add-obj-index objects selected index)))
uchanges (mapv #(array-map :type :del-obj :page-id page-id :id (:id %)) uchanges (mapv #(array-map :type :del-obj :page-id page-id :id (:id %))
(reverse rchanges)) (reverse rchanges))
;; Adds a reg-objects operation so the groups are updated. We add all the new objects
new-objects-ids (->> rchanges (filter #(= (:type %) :add-obj)) (mapv :id))
rchanges (conj rchanges {:type :reg-objects
:page-id page-id
:shapes new-objects-ids})
selected (->> rchanges selected (->> rchanges
(filter #(selected (:old-id %))) (filter #(selected (:old-id %)))
(map #(get-in % [:obj :id])) (map #(get-in % [:obj :id]))
@ -1556,7 +1601,8 @@
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (watch [_ state stream]
;; Not interrupt when we're editing a path ;; Not interrupt when we're editing a path
(let [edition-id (get-in state [:workspace-local :edition]) (let [edition-id (or (get-in state [:workspace-drawing :object :id])
(get-in state [:workspace-local :edition]))
path-edit-mode (get-in state [:workspace-local :edit-path edition-id :edit-mode])] path-edit-mode (get-in state [:workspace-local :edit-path edition-id :edit-mode])]
(if-not (= :draw path-edit-mode) (if-not (= :draw path-edit-mode)
(rx/of :interrupt (deselect-all true)) (rx/of :interrupt (deselect-all true))

View file

@ -524,22 +524,47 @@
(update-in [:workspace-local :hover] disj id) (update-in [:workspace-local :hover] disj id)
(update :workspace-local dissoc :edition)))))) (update :workspace-local dissoc :edition))))))
(defn get-shape-layer-position
[objects selected attrs]
(cond
(= :frame (:type attrs))
[uuid/zero uuid/zero nil]
(empty? selected)
(let [position @ms/mouse-position
frame-id (:frame-id attrs (cp/frame-id-by-position objects position))]
[frame-id frame-id nil])
:else
(let [shape (cp/get-base-shape objects selected)
index (cp/position-on-parent (:id shape) objects)
{:keys [frame-id parent-id]} shape]
[frame-id parent-id (inc index)])))
(defn add-shape-changes (defn add-shape-changes
[page-id attrs] [page-id objects selected attrs]
(let [id (:id attrs) (let [id (:id attrs)
frame-id (:frame-id attrs) shape (gpr/setup-proportions attrs)
shape (gpr/setup-proportions attrs)
default-attrs (if (= :frame (:type shape)) default-attrs (if (= :frame (:type shape))
cp/default-frame-attrs cp/default-frame-attrs
cp/default-shape-attrs) cp/default-shape-attrs)
shape (merge default-attrs shape) shape (merge default-attrs shape)
[frame-id parent-id index] (get-shape-layer-position objects selected attrs)
redo-changes [{:type :add-obj redo-changes [{:type :add-obj
:id id :id id
:page-id page-id :page-id page-id
:frame-id frame-id :frame-id frame-id
:obj shape}] :parent-id parent-id
:index index
:obj shape}
{:type :reg-objects
:page-id page-id
:shapes [id]}]
undo-changes [{:type :del-obj undo-changes [{:type :del-obj
:page-id page-id :page-id page-id
:id id}]] :id id}]]
@ -560,16 +585,15 @@
(retrieve-used-names) (retrieve-used-names)
(generate-unique-name (:name attrs))) (generate-unique-name (:name attrs)))
position @ms/mouse-position selected (get-in state [:workspace-local :selected])
frame-id (if (= :frame (:type attrs))
uuid/zero
(or (:frame-id attrs)
(cp/frame-id-by-position objects position)))
[rchanges uchanges] (add-shape-changes page-id (assoc attrs [rchanges uchanges] (add-shape-changes
:id id page-id
:frame-id frame-id objects
:name name))] selected
(-> attrs
(assoc :id id )
(assoc :name name)))]
(rx/concat (rx/concat
(rx/of (commit-changes rchanges uchanges {:commit-local? true}) (rx/of (commit-changes rchanges uchanges {:commit-local? true})
(select-shapes (d/ordered-set id))) (select-shapes (d/ordered-set id)))

View file

@ -60,8 +60,7 @@
(rx/of (dwc/start-undo-transaction)) (rx/of (dwc/start-undo-transaction))
(rx/empty)) (rx/empty))
(rx/of (dws/deselect-all) (rx/of (dwc/add-shape shape))
(dwc/add-shape shape))
(if (= :frame (:type shape)) (if (= :frame (:type shape))
(->> (uw/ask! {:cmd :selection/query (->> (uw/ask! {:cmd :selection/query

View file

@ -548,8 +548,7 @@
(update-in [:workspace-local :edit-path id :content-modifiers (inc index)] assoc (update-in [:workspace-local :edit-path id :content-modifiers (inc index)] assoc
:c1x dx :c1y dy) :c1x dx :c1y dy)
(update-in [:workspace-local :edit-path id :content-modifiers index] assoc (update-in [:workspace-local :edit-path id :content-modifiers index] assoc
:x dx :y dy :c2x dx :c2y dy) :x dx :y dy :c2x dx :c2y dy))))))
)))))
(defn modify-handler [id index prefix dx dy match-opposite?] (defn modify-handler [id index prefix dx dy match-opposite?]
(ptk/reify ::modify-point (ptk/reify ::modify-point

View file

@ -251,6 +251,33 @@
(def ^:private change->name #(get-in % [:obj :name])) (def ^:private change->name #(get-in % [:obj :name]))
(defn update-indices
"Fixes the indices for a set of changes after a duplication. We need to
fix the indices to take into the account the movement of indices.
index-map is a map that goes from parent-id => vector([id index-in-parent])"
[changes index-map]
(let [inc-indices
(fn [[offset result] [id index]]
[(inc offset) (conj result [id (+ index offset)])])
fix-indices
(fn [_ entry]
(->> entry
(sort-by second)
(reduce inc-indices [1 []])
(second)
(into {})))
objects-indices (->> index-map (d/mapm fix-indices) (vals) (reduce merge))
update-change
(fn [change]
(let [index (get objects-indices (:old-id change))]
(-> change
(assoc :index index))))]
(mapv update-change changes)))
(defn prepare-duplicate-changes (defn prepare-duplicate-changes
"Prepare objects to paste: generate new id, give them unique names, "Prepare objects to paste: generate new id, give them unique names,
move to the position of mouse pointer, and find in what frame they move to the position of mouse pointer, and find in what frame they
@ -269,6 +296,18 @@
(into chgs result))) (into chgs result)))
chgs))) chgs)))
(defn duplicate-changes-update-indices
"Parses the change set when duplicating to set-up the appropiate indices"
[objects ids changes]
(let [process-id
(fn [index-map id]
(let [parent-id (get-in objects [id :parent-id])
parent-index (cp/position-on-parent id objects)]
(update index-map parent-id (fnil conj []) [id parent-index])))
index-map (reduce process-id {} ids)]
(-> changes (update-indices index-map))))
(defn- prepare-duplicate-change (defn- prepare-duplicate-change
[objects page-id names id delta] [objects page-id names id delta]
(let [obj (get objects id)] (let [obj (get objects id)]
@ -347,7 +386,9 @@
delta (gpt/point 0 0) delta (gpt/point 0 0)
unames (dwc/retrieve-used-names objects) unames (dwc/retrieve-used-names objects)
rchanges (prepare-duplicate-changes objects page-id unames selected delta) rchanges (->> (prepare-duplicate-changes objects page-id unames selected delta)
(duplicate-changes-update-indices objects selected))
uchanges (mapv #(array-map :type :del-obj :page-id page-id :id (:id %)) uchanges (mapv #(array-map :type :del-obj :page-id page-id :id (:id %))
(reverse rchanges)) (reverse rchanges))

View file

@ -143,6 +143,7 @@
(let [page-id (:current-page-id state) (let [page-id (:current-page-id state)
objects (dwc/lookup-page-objects state page-id) objects (dwc/lookup-page-objects state page-id)
frame-id (cp/frame-id-by-position objects {:x x :y y}) frame-id (cp/frame-id-by-position objects {:x x :y y})
selected (get-in state [:workspace-local :selected])
[width height] (svg-dimensions data) [width height] (svg-dimensions data)
x (- x (/ width 2)) x (- x (/ width 2))
@ -152,7 +153,7 @@
(fn add-svg-child [parent-id root-shape [unames [rchs uchs]] [index {:keys [content] :as data}]] (fn add-svg-child [parent-id root-shape [unames [rchs uchs]] [index {:keys [content] :as data}]]
(let [shape (parse-svg-element root-shape data unames) (let [shape (parse-svg-element root-shape data unames)
shape-id (:id shape) shape-id (:id shape)
[rch1 uch1] (dwc/add-shape-changes page-id shape) [rch1 uch1] (dwc/add-shape-changes page-id objects selected shape)
;; Mov-objects won't have undo because we "delete" the object in the undo of the ;; Mov-objects won't have undo because we "delete" the object in the undo of the
;; previous operation ;; previous operation
@ -176,7 +177,7 @@
root-shape (create-raw-svg svg-name frame-id x y width height data) root-shape (create-raw-svg svg-name frame-id x y width height data)
root-id (:id root-shape) root-id (:id root-shape)
changes (dwc/add-shape-changes page-id root-shape) changes (dwc/add-shape-changes page-id objects selected root-shape)
[_ [rchanges uchanges]] (reduce (partial add-svg-child root-id root-shape) [unames changes] (d/enumerate (:content data)))] [_ [rchanges uchanges]] (reduce (partial add-svg-child root-id root-shape) [unames changes] (d/enumerate (:content data)))]
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}) (rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})

View file

@ -254,6 +254,7 @@
zoom-view-ref (mf/use-ref nil) zoom-view-ref (mf/use-ref nil)
last-position (mf/use-var nil) last-position (mf/use-var nil)
disable-paste (mf/use-var false) disable-paste (mf/use-var false)
in-viewport? (mf/use-var false)
drawing (mf/deref refs/workspace-drawing) drawing (mf/deref refs/workspace-drawing)
drawing-tool (:tool drawing) drawing-tool (:tool drawing)
drawing-obj (:object drawing) drawing-obj (:object drawing)
@ -553,7 +554,7 @@
;; paste the content into the workspace ;; paste the content into the workspace
(let [tag-name (-> event dom/get-target dom/get-tag-name)] (let [tag-name (-> event dom/get-target dom/get-tag-name)]
(when (and (not (#{"INPUT" "TEXTAREA"} tag-name)) (not @disable-paste)) (when (and (not (#{"INPUT" "TEXTAREA"} tag-name)) (not @disable-paste))
(st/emit! (dw/paste-from-event event)))))) (st/emit! (dw/paste-from-event event @in-viewport?))))))
on-resize on-resize
(mf/use-callback (mf/use-callback
@ -640,6 +641,8 @@
:on-mouse-up on-mouse-up :on-mouse-up on-mouse-up
:on-pointer-down on-pointer-down :on-pointer-down on-pointer-down
:on-pointer-up on-pointer-up :on-pointer-up on-pointer-up
:on-pointer-enter #(reset! in-viewport? true)
:on-pointer-leave #(reset! in-viewport? false)
:on-drag-enter on-drag-enter :on-drag-enter on-drag-enter
:on-drag-over on-drag-over :on-drag-over on-drag-over
:on-drop on-drop} :on-drop on-drop}