diff --git a/common/src/app/common/geom/shapes.cljc b/common/src/app/common/geom/shapes.cljc index 7ba26d1fa..412e0b44f 100644 --- a/common/src/app/common/geom/shapes.cljc +++ b/common/src/app/common/geom/shapes.cljc @@ -160,6 +160,7 @@ (dm/export gtr/transform-rect) (dm/export gtr/calculate-adjust-matrix) (dm/export gtr/update-group-selrect) +(dm/export gtr/update-mask-selrect) (dm/export gtr/resize-modifiers) (dm/export gtr/rotation-modifiers) (dm/export gtr/merge-modifiers) diff --git a/common/src/app/common/geom/shapes/transforms.cljc b/common/src/app/common/geom/shapes/transforms.cljc index a2d331232..0cd7a31af 100644 --- a/common/src/app/common/geom/shapes/transforms.cljc +++ b/common/src/app/common/geom/shapes/transforms.cljc @@ -318,7 +318,8 @@ (update :width + (:width deltas)) (update :height + (:height deltas))))))) -(defn update-group-selrect [group children] +(defn update-group-selrect + [group children] (let [shape-center (gco/center-shape group) ;; Points for every shape inside the group points (->> children (mapcat :points)) @@ -346,6 +347,18 @@ (assoc :flip-y false) (apply-transform (gmt/matrix) true)))) +(defn update-mask-selrect + [masked-group children] + (let [mask (first children)] + (-> masked-group + (assoc :selrect (-> mask :selrect)) + (assoc :points (-> mask :points)) + (assoc :x (-> mask :selrect :x)) + (assoc :y (-> mask :selrect :y)) + (assoc :width (-> mask :selrect :width)) + (assoc :height (-> mask :selrect :height)) + (assoc :flip-x (-> mask :flip-x)) + (assoc :flip-y (-> mask :flip-y))))) ;; --- Modifiers @@ -600,3 +613,4 @@ (->> shapes (map (comp gpr/points->selrect :points transform-shape)) (gpr/join-selrects))) + diff --git a/common/src/app/common/pages/changes_builder.cljc b/common/src/app/common/pages/changes_builder.cljc index 3fa2aeeb0..4d28d8eb4 100644 --- a/common/src/app/common/pages/changes_builder.cljc +++ b/common/src/app/common/pages/changes_builder.cljc @@ -7,7 +7,11 @@ (ns app.common.pages.changes-builder (:require [app.common.data :as d] - [app.common.pages.helpers :as cph])) + [app.common.geom.shapes :as gsh] + [app.common.geom.shapes.bool :as gshb] + [app.common.pages :as cp] + [app.common.pages.helpers :as cph] + [app.common.uuid :as uuid])) ;; Auxiliary functions to help create a set of changes (undo + redo) @@ -22,6 +26,10 @@ :undo-changes [] :origin origin})) +(defn set-save-undo? + [changes save-undo?] + (assoc changes :save-undo? save-undo?)) + (defn with-page [changes page] (vary-meta changes assoc ::page page @@ -29,7 +37,10 @@ ::objects (:objects page))) (defn with-objects [changes objects] - (vary-meta changes assoc ::objects objects)) + (let [file-data (-> (cp/make-file-data (uuid/next) uuid/zero) + (assoc-in [:pages-index uuid/zero :objects] objects))] + (vary-meta changes assoc ::file-data file-data + ::applied-changes-count 0))) (defn amend-last-change "Modify the last redo-changes added with an update function." @@ -52,7 +63,21 @@ (defn- assert-objects [changes] - (assert (contains? (meta changes) ::objects) "Call (with-objects) before using this function")) + (assert (contains? (meta changes) ::file-data) "Call (with-objects) before using this function")) + +(defn- apply-changes-local + [changes] + (if-let [file-data (::file-data (meta changes))] + (let [index (::applied-changes-count (meta changes)) + redo-changes (:redo-changes changes) + new-changes (if (< index (count redo-changes)) + (->> (subvec (:redo-changes changes) index) + (map #(assoc % :page-id uuid/zero))) + []) + new-file-data (cp/process-changes file-data new-changes)] + (vary-meta changes assoc ::file-data new-file-data + ::applied-changes-count (count redo-changes))) + changes)) ;; Page changes @@ -60,31 +85,36 @@ [changes id name] (-> changes (update :redo-changes conj {:type :add-page :id id :name name}) - (update :undo-changes conj {:type :del-page :id id}))) + (update :undo-changes conj {:type :del-page :id id}) + (apply-changes-local))) (defn add-page [changes id page] (-> changes (update :redo-changes conj {:type :add-page :id id :page page}) - (update :undo-changes conj {:type :del-page :id id}))) + (update :undo-changes conj {:type :del-page :id id}) + (apply-changes-local))) (defn mod-page [changes page new-name] (-> changes (update :redo-changes conj {:type :mod-page :id (:id page) :name new-name}) - (update :undo-changes conj {:type :mod-page :id (:id page) :name (:name page)}))) + (update :undo-changes conj {:type :mod-page :id (:id page) :name (:name page)}) + (apply-changes-local))) (defn del-page [changes page] (-> changes (update :redo-changes conj {:type :del-page :id (:id page)}) - (update :undo-changes conj {:type :add-page :id (:id page) :page page}))) + (update :undo-changes conj {:type :add-page :id (:id page) :page page}) + (apply-changes-local))) (defn move-page [changes page-id index prev-index] (-> changes (update :redo-changes conj {:type :mov-page :id page-id :index index}) - (update :undo-changes conj {:type :mov-page :id page-id :index prev-index}))) + (update :undo-changes conj {:type :mov-page :id page-id :index prev-index}) + (apply-changes-local))) (defn set-page-option [changes option-key option-val] @@ -101,7 +131,27 @@ (update :undo-changes conj {:type :set-option :page-id page-id :option option-key - :value old-val})))) + :value old-val}) + (apply-changes-local)))) + +(defn update-page-option + [changes option-key update-fn & args] + (assert-page changes) + (let [page-id (::page-id (meta changes)) + page (::page (meta changes)) + old-val (get-in page [:options option-key]) + new-val (apply update-fn old-val args)] + + (-> changes + (update :redo-changes conj {:type :set-option + :page-id page-id + :option option-key + :value new-val}) + (update :undo-changes conj {:type :set-option + :page-id page-id + :option option-key + :value old-val}) + (apply-changes-local)))) ;; Shape tree changes @@ -132,7 +182,8 @@ (-> changes (update :redo-changes conj add-change) - (update :undo-changes d/preconj del-change))))) + (update :undo-changes d/preconj del-change) + (apply-changes-local))))) (defn change-parent ([changes parent-id shapes] @@ -141,7 +192,8 @@ ([changes parent-id shapes index] (assert-page-id changes) (assert-objects changes) - (let [objects (::objects (meta changes)) + (let [objects (get-in (meta changes) [::file-data :pages-index uuid/zero :objects]) + set-parent-change (cond-> {:type :mov-objects :parent-id parent-id @@ -163,7 +215,8 @@ (-> changes (update :redo-changes conj set-parent-change) - (update :undo-changes #(reduce mk-undo-change % shapes)))))) + (update :undo-changes #(reduce mk-undo-change % shapes)) + (apply-changes-local))))) (defn update-shapes "Calculate the changes and undos to be done when a function is applied to a @@ -174,15 +227,15 @@ ([changes ids update-fn {:keys [attrs ignore-geometry?] :or {attrs nil ignore-geometry? false}}] (assert-page-id changes) (assert-objects changes) - (let [objects (::objects (meta changes)) + (let [objects (get-in (meta changes) [::file-data :pages-index uuid/zero :objects]) generate-operation - (fn [changes attr old new ignore-geometry?] + (fn [operations attr old new ignore-geometry?] (let [old-val (get old attr) new-val (get new attr)] (if (= old-val new-val) - changes - (-> changes + operations + (-> operations (update :rops conj {:type :set :attr attr :val new-val :ignore-geometry ignore-geometry?}) (update :uops conj {:type :set :attr attr :val old-val :ignore-touched true}))))) @@ -213,14 +266,15 @@ (seq uops) (update :undo-changes d/preconj (assoc change :operations uops)))))] - (reduce update-shape changes ids)))) + (-> (reduce update-shape changes ids) + (apply-changes-local))))) (defn remove-objects [changes ids] (assert-page-id changes) (assert-objects changes) (let [page-id (::page-id (meta changes)) - objects (::objects (meta changes)) + objects (get-in (meta changes) [::file-data :pages-index uuid/zero :objects]) add-redo-change (fn [change-set id] @@ -235,11 +289,11 @@ (d/preconj change-set {:type :add-obj + :id id :page-id page-id :parent-id (:frame-id shape) :frame-id (:frame-id shape) - :id id - :index (cph/get-position-on-parent objects (:id shape)) + :index (cph/get-position-on-parent objects id) :obj (cond-> shape (contains? shape :shapes) (assoc :shapes []))}))) @@ -260,14 +314,63 @@ (update :redo-changes #(reduce add-redo-change % ids)) (update :undo-changes #(as-> % $ (reduce add-undo-change-parent $ ids) - (reduce add-undo-change-shape $ ids)))))) + (reduce add-undo-change-shape $ ids))) + (apply-changes-local)))) (defn resize-parents [changes ids] (assert-page-id changes) + (assert-objects changes) (let [page-id (::page-id (meta changes)) - shapes (vec ids)] - (-> changes - (update :redo-changes conj {:type :reg-objects :page-id page-id :shapes shapes}) - (update :undo-changes conj {:type :reg-objects :page-id page-id :shapes shapes})))) + objects (get-in (meta changes) [::file-data :pages-index uuid/zero :objects]) + + xform (comp + (mapcat #(cons % (cph/get-parent-ids objects %))) + (map (d/getf objects)) + (filter #(contains? #{:group :bool} (:type %))) + (distinct)) + all-parents (sequence xform ids) + + generate-operation + (fn [operations attr old new] + (let [old-val (get old attr) + new-val (get new attr)] + (if (= old-val new-val) + operations + (-> operations + (update :rops conj {:type :set :attr attr :val new-val :ignore-touched true}) + (update :uops conj {:type :set :attr attr :val old-val :ignore-touched true}))))) + + resize-parent + (fn [changes parent] + (let [children (->> parent :shapes (map (d/getf objects))) + resized-parent (cond + (empty? children) + changes + + (= (:type parent) :bool) + (gshb/update-bool-selrect parent children objects) + + (= (:type parent) :group) + (if (:masked-group? parent) + (gsh/update-mask-selrect parent children) + (gsh/update-group-selrect parent children))) + + {rops :rops uops :uops} + (reduce #(generate-operation %1 %2 parent resized-parent) + {:rops [] :uops []} + (keys parent)) + + change {:type :mod-obj + :page-id page-id + :id (:id parent)}] + + (if (seq rops) + (-> changes + (update :redo-changes conj (assoc change :operations rops)) + (update :undo-changes conj (assoc change :operations uops))) + changes)))] + + (-> (reduce resize-parent changes all-parents) + (apply-changes-local)))) diff --git a/common/src/app/common/pages/helpers.cljc b/common/src/app/common/pages/helpers.cljc index 838349bd4..19e6e792b 100644 --- a/common/src/app/common/pages/helpers.cljc +++ b/common/src/app/common/pages/helpers.cljc @@ -194,15 +194,6 @@ (conj done (:id current)) (concat (rest pending) (:shapes current)))))) -(defn get-index-in-parent - "Retrieves the index in the parent" - [objects shape-id] - (let [shape (get objects shape-id) - parent (get objects (:parent-id shape)) - [parent-idx _] (d/seek (fn [[_idx child-id]] (= child-id shape-id)) - (d/enumerate (:shapes parent)))] - parent-idx)) - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; COMPONENTS HELPERS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/frontend/package.json b/frontend/package.json index faf25a204..17bbe38d2 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -14,7 +14,7 @@ "scripts": { "compile-test": "clojure -M:dev:shadow-cljs compile test --config-merge '{:autorun false}'", "lint-scss": "yarn run prettier -c resources/styles", - "run-test": "node target/test.js", + "run-test": "node target/tests.js", "test": "yarn run compile-test && yarn run run-test", "watch-gulp": "gulp watch", "watch-main": "shadow-cljs watch main", diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 83338dabf..5dbdcc2e1 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -1526,9 +1526,8 @@ (pcb/amend-changes (partial process-rchange media-idx)) (pcb/amend-changes (partial change-add-obj-index paste-objects selected index))) - ;; Adds a reg-objects operation so the groups are updated. We add all the new objects + ;; Adds a resize-parents operation so the groups are updated. We add all the new objects new-objects-ids (->> changes :redo-changes (filter #(= (:type %) :add-obj)) (mapv :id)) - changes (pcb/resize-parents changes new-objects-ids) selected (->> changes diff --git a/frontend/src/app/main/data/workspace/bool.cljs b/frontend/src/app/main/data/workspace/bool.cljs index 959f7bd5a..1e2e4b5ee 100644 --- a/frontend/src/app/main/data/workspace/bool.cljs +++ b/frontend/src/app/main/data/workspace/bool.cljs @@ -8,7 +8,7 @@ (:require [app.common.data :as d] [app.common.geom.shapes :as gsh] - [app.common.pages.changes-builder :as cb] + [app.common.pages.changes-builder :as pcb] [app.common.pages.helpers :as cph] [app.common.path.shapes-to-path :as stp] [app.common.uuid :as uuid] @@ -96,10 +96,10 @@ (when-not (empty? shapes) (let [[boolean-data index] (create-bool-data bool-type name shapes objects) shape-id (:id boolean-data) - changes (-> (cb/empty-changes it page-id) - (cb/with-objects objects) - (cb/add-obj boolean-data {:index index}) - (cb/change-parent shape-id shapes))] + changes (-> (pcb/empty-changes it page-id) + (pcb/with-objects objects) + (pcb/add-obj boolean-data {:index index}) + (pcb/change-parent shape-id shapes))] (rx/of (dch/commit-changes changes) (dwc/select-shapes (d/ordered-set shape-id))))))))) diff --git a/frontend/src/app/main/data/workspace/changes.cljs b/frontend/src/app/main/data/workspace/changes.cljs index 2a7b058bb..270086962 100644 --- a/frontend/src/app/main/data/workspace/changes.cljs +++ b/frontend/src/app/main/data/workspace/changes.cljs @@ -6,9 +6,9 @@ (ns app.main.data.workspace.changes (:require - [app.common.data :as d] [app.common.logging :as log] [app.common.pages :as cp] + [app.common.pages.changes-builder :as pcb] [app.common.spec :as us] [app.common.spec.change :as spec.change] [app.main.data.workspace.state-helpers :as wsh] @@ -31,78 +31,38 @@ (def commit-changes? (ptk/type? ::commit-changes)) -(defn- generate-operation - "Given an object old and new versions and an attribute will append into changes - the set and undo operations" - [changes attr old new ignore-geometry?] - (let [old-val (get old attr) - new-val (get new attr)] - (if (= old-val new-val) - changes - (-> changes - (update :rops conj {:type :set :attr attr :val new-val :ignore-geometry ignore-geometry?}) - (update :uops conj {:type :set :attr attr :val old-val :ignore-touched true}))))) - -(defn- update-shape-changes - "Calculate the changes and undos to be done when a function is applied to a - single object" - [changes page-id objects update-fn attrs id ignore-geometry?] - (let [old-obj (get objects id) - new-obj (update-fn old-obj) - - attrs (or attrs (d/concat-set (keys old-obj) (keys new-obj))) - - {rops :rops uops :uops} - (reduce #(generate-operation %1 %2 old-obj new-obj ignore-geometry?) - {:rops [] :uops []} - attrs) - - uops (cond-> uops - (seq uops) - (conj {:type :set-touched :touched (:touched old-obj)})) - - change {:type :mod-obj :page-id page-id :id id}] - - (cond-> changes - (seq rops) - (update :redo-changes conj (assoc change :operations rops)) - - (seq uops) - (update :undo-changes conj (assoc change :operations uops))))) - (defn update-shapes - ([ids f] (update-shapes ids f nil nil)) - ([ids f keys] (update-shapes ids f nil keys)) - ([ids f page-id {:keys [reg-objects? save-undo? attrs ignore-tree] - :or {reg-objects? false save-undo? true attrs nil}}] + ([ids update-fn] (update-shapes ids update-fn nil nil)) + ([ids update-fn keys] (update-shapes ids update-fn nil keys)) + ([ids update-fn page-id {:keys [reg-objects? save-undo? attrs ignore-tree] + :or {reg-objects? false save-undo? true attrs nil}}] (us/assert ::coll-of-uuid ids) - (us/assert fn? f) + (us/assert fn? update-fn) (ptk/reify ::update-shapes ptk/WatchEvent (watch [it state _] (let [page-id (or page-id (:current-page-id state)) - objects (wsh/lookup-page-objects state page-id) - changes {:redo-changes [] - :undo-changes [] - :origin it - :save-undo? save-undo?} - + objects (wsh/lookup-page-objects state) ids (into [] (filter some?) ids) changes (reduce - #(update-shape-changes %1 page-id objects f attrs %2 (get ignore-tree %2)) - changes ids)] + (fn [changes id] + (pcb/update-shapes changes + [id] + update-fn + {:attrs attrs + :ignore-geometry? (get ignore-tree id)})) + (-> (pcb/empty-changes it page-id) + (pcb/set-save-undo? save-undo?) + (pcb/with-objects objects)) + ids)] - (when-not (empty? (:redo-changes changes)) - (let [reg-objs {:type :reg-objects - :page-id page-id - :shapes ids} - changes (cond-> changes + (when (seq (:redo-changes changes)) + (let [changes (cond-> changes reg-objects? - (-> (update :redo-changes conj reg-objs) - (update :undo-changes conj reg-objs)))] + (pcb/resize-parents ids))] (rx/of (commit-changes changes))))))))) (defn update-indices @@ -125,6 +85,7 @@ (ptk/reify ::commit-changes cljs.core/IDeref (-deref [_] + {:file-id file-id :hint-events @st/last-events :hint-origin (ptk/type origin) diff --git a/frontend/src/app/main/data/workspace/common.cljs b/frontend/src/app/main/data/workspace/common.cljs index 461a0f7f5..d610d4d84 100644 --- a/frontend/src/app/main/data/workspace/common.cljs +++ b/frontend/src/app/main/data/workspace/common.cljs @@ -11,6 +11,7 @@ [app.common.geom.shapes :as gsh] [app.common.logging :as log] [app.common.pages :as cp] + [app.common.pages.changes-builder :as pcb] [app.common.pages.helpers :as cph] [app.common.spec :as us] [app.common.spec.interactions :as csi] @@ -280,40 +281,25 @@ {:keys [frame-id parent-id]} shape] [frame-id parent-id (inc index)]))))) -(defn add-shape-changes - ([page-id objects selected attrs] - (add-shape-changes page-id objects selected attrs true)) - ([page-id objects selected attrs reg-object?] - (let [id (:id attrs) - shape (gpr/setup-proportions attrs) +(defn make-new-shape + [attrs objects selected] + (let [default-attrs (if (= :frame (:type attrs)) + cp/default-frame-attrs + cp/default-shape-attrs) - default-attrs (if (= :frame (:type shape)) - cp/default-frame-attrs - cp/default-shape-attrs) + selected-non-frames + (into #{} (comp (map (d/getf objects)) + (remove cph/frame-shape?)) + selected) - shape (merge default-attrs shape) + [frame-id parent-id index] + (get-shape-layer-position objects selected-non-frames attrs)] - not-frame? #(not (= :frame (get-in objects [% :type]))) - selected (into #{} (filter not-frame?) selected) - - [frame-id parent-id index] (get-shape-layer-position objects selected attrs) - - redo-changes (cond-> [{:type :add-obj - :id id - :page-id page-id - :frame-id frame-id - :parent-id parent-id - :index index - :obj shape}] - reg-object? - (conj {:type :reg-objects - :page-id page-id - :shapes [id]})) - undo-changes [{:type :del-obj - :page-id page-id - :id id}]] - - [redo-changes undo-changes]))) + (-> (merge default-attrs attrs) + (gpr/setup-proportions) + (assoc :frame-id frame-id + :parent-id parent-id + :index index)))) (defn add-shape ([attrs] @@ -326,26 +312,23 @@ (watch [it state _] (let [page-id (:current-page-id state) objects (wsh/lookup-page-objects state page-id) - - id (or (:id attrs) (uuid/next)) - name (-> objects - (retrieve-used-names) - (generate-unique-name (:name attrs))) - selected (wsh/lookup-selected state) - [rchanges uchanges] (add-shape-changes - page-id - objects - selected - (-> attrs - (assoc :id id ) - (assoc :name name)))] + id (or (:id attrs) (uuid/next)) + name (-> objects + (retrieve-used-names) + (generate-unique-name (:name attrs))) + + shape (make-new-shape + (assoc attrs :id id :name name) + objects + selected) + + changes (-> (pcb/empty-changes it page-id) + (pcb/add-obj shape))] (rx/concat - (rx/of (dch/commit-changes {:redo-changes rchanges - :undo-changes uchanges - :origin it}) + (rx/of (dch/commit-changes changes) (when-not no-select? (select-shapes (d/ordered-set id)))) (when (= :text (:type attrs)) @@ -361,28 +344,15 @@ to-move-shapes (->> (cph/get-immediate-children objects) (remove cph/frame-shape?) - (mapv :id) (d/enumerate) - (filterv (comp shapes second))) + (filterv (comp shapes :id second)) + (mapv second)) - rchanges [{:type :mov-objects - :parent-id frame-id - :frame-id frame-id - :page-id page-id - :index 0 - :shapes (mapv second to-move-shapes)}] + changes (-> (pcb/empty-changes it page-id) + (pcb/with-objects objects) + (pcb/change-parent frame-id to-move-shapes 0))] - uchanges (->> to-move-shapes - (mapv (fn [[index shape-id]] - {:type :mov-objects - :parent-id uuid/zero - :frame-id uuid/zero - :page-id page-id - :index index - :shapes [shape-id]})))] - (rx/of (dch/commit-changes {:redo-changes rchanges - :undo-changes uchanges - :origin it})))))) + (rx/of (dch/commit-changes changes)))))) (s/def ::set-of-uuid (s/every ::us/uuid :kind set?)) @@ -395,10 +365,9 @@ (watch [it state _] (let [page-id (:current-page-id state) objects (wsh/lookup-page-objects state page-id) - options (wsh/lookup-page-options state page-id) + page (wsh/lookup-page state page-id) ids (cph/clean-loops objects ids) - flows (:flows options) groups-to-unmask (reduce (fn [group-ids id] @@ -416,6 +385,8 @@ interacting-shapes (filter (fn [shape] + ;; If any of the deleted shapes is the destination of + ;; some interaction, this must be deleted, too. (let [interactions (:interactions shape)] (some #(and (csi/has-destination %) (contains? ids (:destination %))) @@ -423,7 +394,26 @@ (vals objects)) starting-flows - (filter #(contains? ids (:starting-frame %)) flows) + (filter (fn [flow] + ;; If any of the deleted is a frame that starts a flow, + ;; this must be deleted, too. + (contains? ids (:starting-frame flow))) + (-> page :options :flows)) + + all-parents + (reduce (fn [res id] + ;; All parents of any deleted shape must be resized. + (into res (cph/get-parent-ids objects id))) + (d/ordered-set) + ids) + + all-children + (->> ids ;; Children of deleted shapes must be also deleted. + (reduce (fn [res id] + (into res (cph/get-children-ids objects id))) + []) + (reverse) + (into (d/ordered-set))) empty-parents-xform (comp @@ -435,142 +425,36 @@ (take-while some?) (map :id)) - all-parents - (reduce (fn [res id] - (into res (cph/get-parent-ids objects id))) - (d/ordered-set) - ids) - - all-children - (->> ids - (reduce (fn [res id] - (into res (cph/get-children-ids objects id))) - []) - (reverse) - (into (d/ordered-set))) - empty-parents + ;; Any parent whose children are all deleted, must be deleted too. (into (d/ordered-set) empty-parents-xform all-parents) - mk-del-obj-xf - (comp (filter (partial contains? objects)) - (map (fn [id] - {:type :del-obj - :page-id page-id - :id id}))) + changes (-> (pcb/empty-changes it page-id) + (pcb/with-page page) + (pcb/with-objects objects) + (pcb/remove-objects all-children) + (pcb/remove-objects ids) + (pcb/remove-objects empty-parents) + (pcb/resize-parents all-parents) + (pcb/update-shapes groups-to-unmask + (fn [shape] + (assoc shape :masked-group? false))) + (pcb/update-shapes (map :id interacting-shapes) + (fn [shape] + (update shape :interactions + (fn [interactions] + (when interactions + (d/removev #(and (csi/has-destination %) + (contains? ids (:destination %))) + interactions)))))) + (cond-> + (seq starting-flows) + (pcb/update-page-option :flows (fn [flows] + (reduce #(csp/remove-flow %1 (:id %2)) + flows + starting-flows)))))] - mk-add-obj-xf - (comp (filter (partial contains? objects)) - (map (fn [id] - (let [item (get objects id)] - {:type :add-obj - :id (:id item) - :page-id page-id - :index (cph/get-position-on-parent objects id) - :frame-id (:frame-id item) - :parent-id (:parent-id item) - :obj item})))) - - mk-mod-touched-xf - (comp (filter (partial contains? objects)) - (map (fn [id] - (let [parent (get objects id)] - {:type :mod-obj - :page-id page-id - :id (:id parent) - :operations [{:type :set-touched - :touched (:touched parent)}]})))) - - mk-mod-int-del-xf - (comp (filter some?) - (map (fn [obj] - {:type :mod-obj - :page-id page-id - :id (:id obj) - :operations [{:type :set - :attr :interactions - :val (vec (remove (fn [interaction] - (and (csi/has-destination interaction) - (contains? ids (:destination interaction)))) - (:interactions obj)))}]}))) - mk-mod-int-add-xf - (comp (filter some?) - (map (fn [obj] - {:type :mod-obj - :page-id page-id - :id (:id obj) - :operations [{:type :set - :attr :interactions - :val (:interactions obj)}]}))) - - mk-mod-del-flow-xf - (comp (filter some?) - (map (fn [flow] - {:type :set-option - :page-id page-id - :option :flows - :value (csp/remove-flow flows (:id flow))}))) - - mk-mod-add-flow-xf - (comp (filter some?) - (map (fn [_] - {:type :set-option - :page-id page-id - :option :flows - :value flows}))) - - mk-mod-unmask-xf - (comp (filter (partial contains? objects)) - (map (fn [id] - {:type :mod-obj - :page-id page-id - :id id - :operations [{:type :set - :attr :masked-group? - :val false}]}))) - - mk-mod-mask-xf - (comp (filter (partial contains? objects)) - (map (fn [id] - {:type :mod-obj - :page-id page-id - :id id - :operations [{:type :set - :attr :masked-group? - :val true}]}))) - - rchanges - (-> [] - (into mk-del-obj-xf all-children) - (into mk-del-obj-xf ids) - (into mk-del-obj-xf empty-parents) - (conj {:type :reg-objects - :page-id page-id - :shapes (vec all-parents)}) - (into mk-mod-unmask-xf groups-to-unmask) - (into mk-mod-int-del-xf interacting-shapes) - (into mk-mod-del-flow-xf starting-flows)) - - uchanges - (-> [] - (into mk-add-obj-xf (reverse empty-parents)) - (into mk-add-obj-xf (reverse ids)) - (into mk-add-obj-xf (reverse all-children)) - (conj {:type :reg-objects - :page-id page-id - :shapes (vec all-parents)}) - (into mk-mod-touched-xf (reverse all-parents)) - (into mk-mod-mask-xf groups-to-unmask) - (into mk-mod-int-add-xf interacting-shapes) - (into mk-mod-add-flow-xf starting-flows))] - - ;; (println "================ rchanges") - ;; (cljs.pprint/pprint rchanges) - ;; (println "================ uchanges") - ;; (cljs.pprint/pprint uchanges) - (rx/of (dch/commit-changes {:redo-changes rchanges - :undo-changes uchanges - :origin it})))))) + (rx/of (dch/commit-changes changes)))))) ;; --- Add shape to Workspace diff --git a/frontend/src/app/main/data/workspace/grid.cljs b/frontend/src/app/main/data/workspace/grid.cljs index 3e2cde907..6c01740e5 100644 --- a/frontend/src/app/main/data/workspace/grid.cljs +++ b/frontend/src/app/main/data/workspace/grid.cljs @@ -8,8 +8,10 @@ (:require [app.common.colors :as clr] [app.common.data :as d] + [app.common.pages.changes-builder :as pcb] [app.common.spec :as us] [app.main.data.workspace.changes :as dch] + [app.main.data.workspace.state-helpers :as wsh] [beicon.core :as rx] [potok.core :as ptk])) @@ -72,15 +74,8 @@ (ptk/reify ::set-default-grid ptk/WatchEvent (watch [it state _] - (let [pid (:current-page-id state) - prev-value (get-in state [:workspace-data :pages-index pid :options :saved-grids type])] + (let [page (wsh/lookup-page state)] (rx/of (dch/commit-changes - {:redo-changes [{:type :set-option - :page-id pid - :option [:saved-grids type] - :value params}] - :undo-changes [{:type :set-option - :page-id pid - :option [:saved-grids type] - :value prev-value}] - :origin it})))))) + (-> (pcb/empty-changes it) + (pcb/with-page page) + (pcb/set-page-option [:saved-grids type] params)))))))) diff --git a/frontend/src/app/main/data/workspace/groups.cljs b/frontend/src/app/main/data/workspace/groups.cljs index 2cfdc1a4e..422299e62 100644 --- a/frontend/src/app/main/data/workspace/groups.cljs +++ b/frontend/src/app/main/data/workspace/groups.cljs @@ -9,9 +9,8 @@ [app.common.data :as d] [app.common.geom.shapes :as gsh] [app.common.pages :as cp] - [app.common.pages.changes-builder :as cb] + [app.common.pages.changes-builder :as pcb] [app.common.pages.helpers :as cph] - [app.common.spec :as us] [app.main.data.workspace.changes :as dch] [app.main.data.workspace.common :as dwc] [app.main.data.workspace.state-helpers :as wsh] @@ -65,7 +64,7 @@ result))))))) (defn prepare-create-group - [objects page-id shapes base-name keep-name?] + [it objects page-id shapes base-name keep-name?] (let [frame-id (:frame-id (first shapes)) parent-id (:parent-id (first shapes)) gname (if (and keep-name? @@ -78,62 +77,22 @@ selrect (gsh/selection-rect shapes) group (-> (cp/make-minimal-group frame-id selrect gname) (gsh/setup selrect) - (assoc :shapes (mapv :id shapes))) - - rchanges [{:type :add-obj - :id (:id group) - :page-id page-id - :frame-id frame-id - :parent-id parent-id - :obj group - :index (::index (first shapes))} - - {:type :mov-objects - :page-id page-id - :parent-id (:id group) - :shapes (mapv :id shapes)}] - - uchanges (-> (mapv (fn [obj] - {:type :mov-objects - :page-id page-id - :parent-id (:parent-id obj) - :index (::index obj) - :shapes [(:id obj)]}) - shapes) - (conj {:type :del-obj - :id (:id group) - :page-id page-id})) + (assoc :shapes (mapv :id shapes) + :parent-id parent-id + :frame-id frame-id + :index (::index (first shapes)))) ;; Look at the `get-empty-groups-after-group-creation` ;; docstring to understand the real purpose of this code ids-to-delete (get-empty-groups-after-group-creation objects parent-id shapes) - delete-group - (fn [changes id] - (conj changes {:type :del-obj - :id id - :page-id page-id})) + changes (-> (pcb/empty-changes it page-id) + (pcb/with-objects objects) + (pcb/add-obj group) + (pcb/change-parent (:id group) shapes) + (pcb/remove-objects ids-to-delete))] - add-deleted-group - (fn [changes id] - (let [obj (-> (get objects id) - (dissoc :shapes))] - (into [{:type :add-obj - :id id - :page-id page-id - :frame-id (:frame-id obj) - :parent-id (:parent-id obj) - :obj obj - :index (::index obj)}] - changes))) - - rchanges (->> ids-to-delete - (reduce delete-group rchanges)) - - uchanges (->> ids-to-delete - (reduce add-deleted-group uchanges))] - - [group rchanges uchanges])) + [group changes])) (defn prepare-remove-group [it page-id group objects] @@ -159,36 +118,13 @@ :shape-ref :touched))] - (cond-> (-> (cb/empty-changes it page-id) - (cb/with-objects objects) - (cb/change-parent parent-id children index-in-parent) - (cb/remove-objects [(:id group)])) + (cond-> (-> (pcb/empty-changes it page-id) + (pcb/with-objects objects) + (pcb/change-parent parent-id children index-in-parent) + (pcb/remove-objects [(:id group)])) (some? ids-to-detach) - (cb/update-shapes ids-to-detach detach-fn)))) - -(defn prepare-remove-mask - [page-id mask] - (let [rchanges [{:type :mod-obj - :page-id page-id - :id (:id mask) - :operations [{:type :set - :attr :masked-group? - :val nil}]} - {:type :reg-objects - :page-id page-id - :shapes [(:id mask)]}] - uchanges [{:type :mod-obj - :page-id page-id - :id (:id mask) - :operations [{:type :set - :attr :masked-group? - :val (:masked-group? mask)}]} - {:type :reg-objects - :page-id page-id - :shapes [(:id mask)]}]] - [rchanges uchanges])) - + (pcb/update-shapes ids-to-detach detach-fn)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; GROUPS @@ -204,11 +140,9 @@ selected (cph/clean-loops objects selected) shapes (shapes-for-grouping objects selected)] (when-not (empty? shapes) - (let [[group rchanges uchanges] - (prepare-create-group objects page-id shapes "Group-1" false)] - (rx/of (dch/commit-changes {:redo-changes rchanges - :undo-changes uchanges - :origin it}) + (let [[group changes] + (prepare-create-group it objects page-id shapes "Group-1" false)] + (rx/of (dch/commit-changes changes) (dwc/select-shapes (d/ordered-set (:id group)))))))))) (def ungroup-selected @@ -237,85 +171,39 @@ (ptk/reify ::mask-group ptk/WatchEvent (watch [it state _] - (let [page-id (:current-page-id state) - objects (wsh/lookup-page-objects state page-id) - selected (wsh/lookup-selected state) - selected (cph/clean-loops objects selected) - shapes (shapes-for-grouping objects selected)] + (let [page-id (:current-page-id state) + objects (wsh/lookup-page-objects state page-id) + selected (wsh/lookup-selected state) + selected (cph/clean-loops objects selected) + shapes (shapes-for-grouping objects selected) + first-shape (first shapes)] (when-not (empty? shapes) (let [;; If the selected shape is a group, we can use it. If not, ;; create a new group and set it as masked. - [group rchanges uchanges] + [group changes] (if (and (= (count shapes) 1) (= (:type (first shapes)) :group)) - [(first shapes) [] []] - (prepare-create-group objects page-id shapes "Group-1" true)) + [first-shape (-> (pcb/empty-changes it page-id) + (pcb/with-objects objects))] + (prepare-create-group it objects page-id shapes "Group-1" true)) - ;; Assertions just for documentation purposes - _ (us/assert vector? rchanges) - _ (us/assert vector? uchanges) + changes (-> changes + (pcb/update-shapes (:shapes group) + (fn [shape] + (assoc shape + :constraints-h :scale + :constraints-v :scale))) + (pcb/update-shapes [(:id group)] + (fn [group] + (assoc group + :masked-group? true + :selrect (:selrect first-shape) + :points (:points first-shape) + :transform (:transform first-shape) + :transform-inverse (:transform-inverse first-shape)))) + (pcb/resize-parents [(:id group)]))] - children (map #(get objects %) (:shapes group)) - - rchanges (d/concat-vec - rchanges - (for [child children] - {:type :mod-obj - :page-id page-id - :id (:id child) - :operations [{:type :set - :attr :constraints-h - :val :scale} - {:type :set - :attr :constraints-v - :val :scale}]}) - [{:type :mod-obj - :page-id page-id - :id (:id group) - :operations [{:type :set - :attr :masked-group? - :val true} - {:type :set - :attr :selrect - :val (-> shapes first :selrect)} - {:type :set - :attr :points - :val (-> shapes first :points)} - {:type :set - :attr :transform - :val (-> shapes first :transform)} - {:type :set - :attr :transform-inverse - :val (-> shapes first :transform-inverse)}]} - {:type :reg-objects - :page-id page-id - :shapes [(:id group)]}]) - - uchanges (d/concat-vec - uchanges - (for [child children] - {:type :mod-obj - :page-id page-id - :id (:id child) - :operations [{:type :set - :attr :constraints-h - :val (:constraints-h child)} - {:type :set - :attr :constraints-v - :val (:constraints-v child)}]}) - [{:type :mod-obj - :page-id page-id - :id (:id group) - :operations [{:type :set - :attr :masked-group? - :val nil}]} - {:type :reg-objects - :page-id page-id - :shapes [(:id group)]}])] - - (rx/of (dch/commit-changes {:redo-changes rchanges - :undo-changes uchanges - :origin it}) + (rx/of (dch/commit-changes changes) (dwc/select-shapes (d/ordered-set (:id group)))))))))) (def unmask-group @@ -325,13 +213,18 @@ (let [page-id (:current-page-id state) objects (wsh/lookup-page-objects state page-id) - changes-in-bulk (->> (wsh/lookup-selected state) - (map #(get objects %)) - (filter #(or (= :bool (:type %)) (= :group (:type %)))) - (map #(prepare-remove-mask page-id %))) - rchanges-in-bulk (into [] (mapcat first) changes-in-bulk) - uchanges-in-bulk (into [] (mapcat second) changes-in-bulk)] + masked-groups (->> (wsh/lookup-selected state) + (map #(get objects %)) + (filter #(or (= :bool (:type %)) (= :group (:type %))))) - (rx/of (dch/commit-changes {:redo-changes rchanges-in-bulk - :undo-changes uchanges-in-bulk - :origin it})))))) + changes (reduce (fn [changes mask] + (-> changes + (pcb/update-shapes [(:id mask)] + (fn [shape] + (dissoc shape :masked-group?))) + (pcb/resize-parents [(:id mask)]))) + (-> (pcb/empty-changes it page-id) + (pcb/with-objects objects)) + masked-groups)] + + (rx/of (dch/commit-changes changes)))))) diff --git a/frontend/src/app/main/data/workspace/guides.cljs b/frontend/src/app/main/data/workspace/guides.cljs index 3fce84bf2..e9d16710c 100644 --- a/frontend/src/app/main/data/workspace/guides.cljs +++ b/frontend/src/app/main/data/workspace/guides.cljs @@ -28,14 +28,11 @@ (ptk/reify ::update-guides ptk/WatchEvent (watch [it state _] - (let [page (wsh/lookup-page state) - guides (get-in page [:options :guides] {}) - new-guides (assoc guides (:id guide) guide) - + (let [page (wsh/lookup-page state) changes (-> (pcb/empty-changes it) (pcb/with-page page) - (pcb/set-page-option :guides new-guides))] + (pcb/update-page-option :guides assoc (:id guide) guide))] (rx/of (dwc/commit-changes changes)))))) (defn remove-guide [guide] @@ -49,14 +46,11 @@ ptk/WatchEvent (watch [it state _] - (let [page (wsh/lookup-page state) - guides (get-in page [:options :guides] {}) - new-guides (dissoc guides (:id guide)) - + (let [page (wsh/lookup-page state) changes (-> (pcb/empty-changes it) (pcb/with-page page) - (pcb/set-page-option :guides new-guides))] + (pcb/update-page-option :guides dissoc (:id guide)))] (rx/of (dwc/commit-changes changes)))))) (defn remove-guides diff --git a/frontend/src/app/main/data/workspace/interactions.cljs b/frontend/src/app/main/data/workspace/interactions.cljs index 93331bd30..654507ef7 100644 --- a/frontend/src/app/main/data/workspace/interactions.cljs +++ b/frontend/src/app/main/data/workspace/interactions.cljs @@ -8,6 +8,7 @@ (:require [app.common.data :as d] [app.common.geom.point :as gpt] + [app.common.pages.changes-builder :as pcb] [app.common.pages.helpers :as cph] [app.common.spec :as us] [app.common.spec.interactions :as csi] @@ -27,13 +28,9 @@ (ptk/reify ::add-flow ptk/WatchEvent (watch [it state _] - (let [page-id (:current-page-id state) - flows (get-in state [:workspace-data - :pages-index - page-id - :options - :flows] []) + (let [page (wsh/lookup-page state) + flows (get-in page [:options :flows] []) unames (into #{} (map :name flows)) name (dwc/generate-unique-name unames "Flow-1") @@ -42,15 +39,9 @@ :starting-frame starting-frame}] (rx/of (dch/commit-changes - {:redo-changes [{:type :set-option - :page-id page-id - :option :flows - :value (csp/add-flow flows new-flow)}] - :undo-changes [{:type :set-option - :page-id page-id - :option :flows - :value flows}] - :origin it})))))) + (-> (pcb/empty-changes it) + (pcb/with-page page) + (pcb/update-page-option :flows csp/add-flow new-flow)))))))) (defn add-flow-selected-frame [] @@ -66,22 +57,11 @@ (ptk/reify ::remove-flow ptk/WatchEvent (watch [it state _] - (let [page-id (:current-page-id state) - flows (get-in state [:workspace-data - :pages-index - page-id - :options - :flows] [])] + (let [page (wsh/lookup-page state)] (rx/of (dch/commit-changes - {:redo-changes [{:type :set-option - :page-id page-id - :option :flows - :value (csp/remove-flow flows flow-id)}] - :undo-changes [{:type :set-option - :page-id page-id - :option :flows - :value flows}] - :origin it})))))) + (-> (pcb/empty-changes it) + (pcb/with-page page) + (pcb/update-page-option :flows csp/remove-flow flow-id)))))))) (defn rename-flow [flow-id name] @@ -90,24 +70,12 @@ (ptk/reify ::rename-flow ptk/WatchEvent (watch [it state _] - (let [page-id (:current-page-id state) - flows (get-in state [:workspace-data - :pages-index - page-id - :options - :flows] [])] + (let [page (wsh/lookup-page state) ] (rx/of (dch/commit-changes - {:redo-changes [{:type :set-option - :page-id page-id - :option :flows - :value (csp/update-flow flows flow-id - #(csp/rename-flow % name))}] - :undo-changes [{:type :set-option - :page-id page-id - :option :flows - :value flows}] - :origin it})))))) - + (-> (pcb/empty-changes it) + (pcb/with-page page) + (pcb/update-page-option :flows csp/update-flow flow-id + #(csp/rename-flow % name))))))))) (defn start-rename-flow [id] diff --git a/frontend/src/app/main/data/workspace/libraries.cljs b/frontend/src/app/main/data/workspace/libraries.cljs index 2b6589268..68b034754 100644 --- a/frontend/src/app/main/data/workspace/libraries.cljs +++ b/frontend/src/app/main/data/workspace/libraries.cljs @@ -288,7 +288,7 @@ shapes (dwg/shapes-for-grouping objects selected)] (when-not (empty? shapes) (let [[group rchanges uchanges] - (dwlh/generate-add-component shapes objects page-id file-id)] + (dwlh/generate-add-component it shapes objects page-id file-id)] (when-not (empty? rchanges) (rx/of (dch/commit-changes {:redo-changes rchanges :undo-changes uchanges diff --git a/frontend/src/app/main/data/workspace/libraries_helpers.cljs b/frontend/src/app/main/data/workspace/libraries_helpers.cljs index d2e62573f..4c5b20b79 100644 --- a/frontend/src/app/main/data/workspace/libraries_helpers.cljs +++ b/frontend/src/app/main/data/workspace/libraries_helpers.cljs @@ -131,7 +131,7 @@ "If there is exactly one id, and it's a group, use it as root. Otherwise, create a group that contains all ids. Then, make a component with it, and link all shapes to their corresponding one in the component." - [shapes objects page-id file-id] + [it shapes objects page-id file-id] (if (and (= (count shapes) 1) (:component-id (first shapes))) empty-changes @@ -140,7 +140,8 @@ (if (and (= (count shapes) 1) (= (:type (first shapes)) :group)) [(first shapes) [] []] - (dwg/prepare-create-group objects page-id shapes name true)) + (let [[group changes] (dwg/prepare-create-group it objects page-id shapes name true)] + [group (:redo-changes changes) (:undo-changes changes)])) ;; Asserts for documentation purposes _ (us/assert vector? rchanges) diff --git a/frontend/src/app/main/data/workspace/path/changes.cljs b/frontend/src/app/main/data/workspace/path/changes.cljs index 87fb18be1..697965eb4 100644 --- a/frontend/src/app/main/data/workspace/path/changes.cljs +++ b/frontend/src/app/main/data/workspace/path/changes.cljs @@ -6,7 +6,7 @@ (ns app.main.data.workspace.path.changes (:require - [app.common.pages.helpers :as cph] + [app.common.pages.changes-builder :as pcb] [app.common.spec :as us] [app.main.data.workspace.changes :as dch] [app.main.data.workspace.path.helpers :as helpers] @@ -17,70 +17,37 @@ [potok.core :as ptk])) (defn generate-path-changes - "Generates content changes and the undos for the content given" - [objects page-id shape old-content new-content] + "Generates changes to update the new content of the shape" + [it objects page-id shape old-content new-content] (us/verify ::spec/content old-content) (us/verify ::spec/content new-content) - (let [shape-id (:id shape) - frame-id (:frame-id shape) - parent-id (:parent-id shape) - parent-index (cph/get-position-on-parent objects shape-id) + (let [shape-id (:id shape) - [old-points old-selrect] (helpers/content->points+selrect shape old-content) - [new-points new-selrect] (helpers/content->points+selrect shape new-content) + [new-points new-selrect] + (helpers/content->points+selrect shape new-content) - rch (cond - ;; https://tree.taiga.io/project/penpot/issue/2366 - (nil? shape-id) - [] + changes (-> (pcb/empty-changes it page-id) + (pcb/with-objects objects))] - (empty? new-content) - [{:type :del-obj - :id shape-id - :page-id page-id} - {:type :reg-objects - :page-id page-id - :shapes [shape-id]}] + (cond + ;; https://tree.taiga.io/project/penpot/issue/2366 + (nil? shape-id) + changes - :else - [{:type :mod-obj - :id shape-id - :page-id page-id - :operations [{:type :set :attr :content :val new-content} - {:type :set :attr :selrect :val new-selrect} - {:type :set :attr :points :val new-points}]} - {:type :reg-objects - :page-id page-id - :shapes [shape-id]}]) + (empty? new-content) + (-> changes + (pcb/remove-objects [shape-id]) + (pcb/resize-parents [shape-id])) - uch (cond - ;; https://tree.taiga.io/project/penpot/issue/2366 - (nil? shape-id) - [] - - (empty? new-content) - [{:type :add-obj - :id shape-id - :obj shape - :page-id page-id - :frame-id frame-id - :parent-id parent-id - :index parent-index} - {:type :reg-objects - :page-id page-id - :shapes [shape-id]}] - - :else - [{:type :mod-obj - :id shape-id - :page-id page-id - :operations [{:type :set :attr :content :val old-content} - {:type :set :attr :selrect :val old-selrect} - {:type :set :attr :points :val old-points}]} - {:type :reg-objects - :page-id page-id - :shapes [shape-id]}])] - [rch uch])) + :else + (-> changes + (pcb/update-shapes [shape-id] + (fn [shape] + (assoc shape + :content new-content + :selrect new-selrect + :points new-points))) + (pcb/resize-parents [shape-id]))))) (defn save-path-content ([] @@ -105,10 +72,8 @@ old-content (get-in state [:workspace-local :edit-path id :old-content]) shape (st/get-path state)] (if (and (some? old-content) (some? (:id shape))) - (let [[rch uch] (generate-path-changes objects page-id shape old-content (:content shape))] - (rx/of (dch/commit-changes {:redo-changes rch - :undo-changes uch - :origin it}))) + (let [changes (generate-path-changes it objects page-id shape old-content (:content shape))] + (rx/of (dch/commit-changes changes))) (rx/empty))))))) diff --git a/frontend/src/app/main/data/workspace/path/edition.cljs b/frontend/src/app/main/data/workspace/path/edition.cljs index f25960d44..4eb4763de 100644 --- a/frontend/src/app/main/data/workspace/path/edition.cljs +++ b/frontend/src/app/main/data/workspace/path/edition.cljs @@ -61,15 +61,11 @@ point-change (->> (map hash-map old-points new-points) (reduce merge))] (when (and (some? new-content) (some? shape)) - (let [[rch uch] (changes/generate-path-changes objects page-id shape (:content shape) new-content)] + (let [changes (changes/generate-path-changes it objects page-id shape (:content shape) new-content)] (if (empty? new-content) - (rx/of (dch/commit-changes {:redo-changes rch - :undo-changes uch - :origin it}) + (rx/of (dch/commit-changes changes) dwc/clear-edition-mode) - (rx/of (dch/commit-changes {:redo-changes rch - :undo-changes uch - :origin it}) + (rx/of (dch/commit-changes changes) (selection/update-selection point-change) (fn [state] (update-in state [:workspace-local :edit-path id] dissoc :content-modifiers :moving-nodes :moving-handler)))))))))) diff --git a/frontend/src/app/main/data/workspace/path/shapes_to_path.cljs b/frontend/src/app/main/data/workspace/path/shapes_to_path.cljs index 54dfda178..d0d7b6ce8 100644 --- a/frontend/src/app/main/data/workspace/path/shapes_to_path.cljs +++ b/frontend/src/app/main/data/workspace/path/shapes_to_path.cljs @@ -6,7 +6,7 @@ (ns app.main.data.workspace.path.shapes-to-path (:require - [app.common.pages.changes-builder :as cb] + [app.common.pages.changes-builder :as pcb] [app.common.pages.helpers :as cph] [app.common.path.shapes-to-path :as upsp] [app.main.data.workspace.changes :as dch] @@ -28,9 +28,9 @@ selected) changes - (-> (cb/empty-changes it page-id) - (cb/with-objects objects) - (cb/remove-objects children-ids) - (cb/update-shapes selected #(upsp/convert-to-path % objects)))] + (-> (pcb/empty-changes it page-id) + (pcb/with-objects objects) + (pcb/remove-objects children-ids) + (pcb/update-shapes selected #(upsp/convert-to-path % objects)))] (rx/of (dch/commit-changes changes)))))) diff --git a/frontend/src/app/main/data/workspace/path/tools.cljs b/frontend/src/app/main/data/workspace/path/tools.cljs index fce88f9db..71a686662 100644 --- a/frontend/src/app/main/data/workspace/path/tools.cljs +++ b/frontend/src/app/main/data/workspace/path/tools.cljs @@ -34,13 +34,11 @@ (when (and (seq points) (some? shape)) (let [new-content (-> (tool-fn (:content shape) points) (ups/close-subpaths)) - [rch uch] (changes/generate-path-changes objects page-id shape (:content shape) new-content)] + changes (changes/generate-path-changes it objects page-id shape (:content shape) new-content)] (rx/concat (rx/of (dch/update-shapes [id] upsp/convert-to-path)) - (rx/of (dch/commit-changes {:redo-changes rch - :undo-changes uch - :origin it}) + (rx/of (dch/commit-changes changes) (when (empty? new-content) dwc/clear-edition-mode)))))))))) diff --git a/frontend/src/app/main/data/workspace/selection.cljs b/frontend/src/app/main/data/workspace/selection.cljs index ac7d6907a..3d5ea5f36 100644 --- a/frontend/src/app/main/data/workspace/selection.cljs +++ b/frontend/src/app/main/data/workspace/selection.cljs @@ -376,17 +376,18 @@ (filter #(= (:type %) :frame)) (filter #(some? (ctp/get-frame-flow flows (:id %)))))] (if-not (empty? frames-with-flow) - (let [new-flows (reduce - (fn [flows frame] - (let [name (dwc/generate-unique-name @unames "Flow-1") - _ (vswap! unames conj name) - new-flow {:id (uuid/next) - :name name - :starting-frame (get ids-map (:id frame))}] - (ctp/add-flow flows new-flow))) - flows - frames-with-flow)] - (pcb/set-page-option changes :flows new-flows)) + (let [update-flows (fn [flows] + (reduce + (fn [flows frame] + (let [name (dwc/generate-unique-name @unames "Flow-1") + _ (vswap! unames conj name) + new-flow {:id (uuid/next) + :name name + :starting-frame (get ids-map (:id frame))}] + (ctp/add-flow flows new-flow))) + flows + frames-with-flow))] + (pcb/update-page-option changes :flows update-flows)) changes))) (defn duplicate-changes-update-indices diff --git a/frontend/src/app/main/data/workspace/svg_upload.cljs b/frontend/src/app/main/data/workspace/svg_upload.cljs index 021da3dee..0495075eb 100644 --- a/frontend/src/app/main/data/workspace/svg_upload.cljs +++ b/frontend/src/app/main/data/workspace/svg_upload.cljs @@ -12,6 +12,7 @@ [app.common.geom.point :as gpt] [app.common.geom.shapes :as gsh] [app.common.math :as mth] + [app.common.pages.changes-builder :as pcb] [app.common.pages.helpers :as cph] [app.common.spec :refer [max-safe-int min-safe-int]] [app.common.uuid :as uuid] @@ -397,32 +398,23 @@ (mapv #(usvg/inherit-attributes attrs %)))] [shape children])))) -(defn add-svg-child-changes [page-id objects selected frame-id parent-id svg-data [unames [rchs uchs]] [index data]] +(defn add-svg-child-changes [page-id objects selected frame-id parent-id svg-data [unames changes] [index data]] (let [[shape children] (parse-svg-element frame-id svg-data data unames)] (if (some? shape) (let [shape-id (:id shape) - [rch1 uch1] (dwc/add-shape-changes page-id objects selected shape false) + new-shape (dwc/make-new-shape shape objects selected) + changes (-> changes + (pcb/with-objects objects) + (pcb/add-obj new-shape) + (pcb/change-parent parent-id [new-shape] index)) - ;; Mov-objects won't have undo because we "delete" the object in the undo of the - ;; previous operation - rch2 [{:type :mov-objects - :parent-id parent-id - :frame-id frame-id - :page-id page-id - :index index - :shapes [shape-id]}] - - ;; Careful! the undo changes are concatenated reversed (we undo in reverse order - changes [(d/concat-vec rchs rch1 rch2) - (d/concat-vec uch1 uchs)] - unames (conj unames (:name shape)) + unames (conj unames (:name new-shape)) reducer-fn (partial add-svg-child-changes page-id objects selected frame-id shape-id svg-data)] (reduce reducer-fn [unames changes] (d/enumerate children))) - ;; Cannot create the data from current tags - [unames [rchs uchs]]))) + [unames changes]))) (declare create-svg-shapes) @@ -493,27 +485,29 @@ root-id (:id root-shape) ;; Creates the root shape - changes (dwc/add-shape-changes page-id objects selected root-shape false) + new-shape (dwc/make-new-shape root-shape objects selected) + changes (-> (pcb/empty-changes it page-id) + (pcb/add-obj new-shape)) root-attrs (-> (:attrs svg-data) (usvg/format-styles)) - ;; Reduces the children to create the changes to add the children shapes - [_ [rchanges uchanges]] + ;; Reduce the children to create the changes to add the children shapes + [_ changes] (reduce (partial add-svg-child-changes page-id objects selected frame-id root-id svg-data) [unames changes] (d/enumerate (->> (:content svg-data) (mapv #(usvg/inherit-attributes root-attrs %))))) - reg-objects-action {:type :reg-objects - :page-id page-id - :shapes (->> rchanges (filter #(= :add-obj (:type %))) (map :id) reverse vec)} + changes (pcb/resize-parents changes + (->> changes + :redo-changes + (filter #(= :add-obj (:type %))) + (map :id) + reverse + vec))] - rchanges (conj rchanges reg-objects-action)] - - (rx/of (dch/commit-changes {:redo-changes rchanges - :undo-changes uchanges - :origin it}) + (rx/of (dch/commit-changes changes) (dwc/select-shapes (d/ordered-set root-id)))) (catch :default e diff --git a/frontend/src/app/main/data/workspace/transforms.cljs b/frontend/src/app/main/data/workspace/transforms.cljs index 45608c7f5..71562c0a0 100644 --- a/frontend/src/app/main/data/workspace/transforms.cljs +++ b/frontend/src/app/main/data/workspace/transforms.cljs @@ -12,6 +12,7 @@ [app.common.geom.point :as gpt] [app.common.geom.shapes :as gsh] [app.common.math :as mth] + [app.common.pages.changes-builder :as pcb] [app.common.pages.common :as cpc] [app.common.pages.helpers :as cph] [app.common.spec :as us] @@ -673,26 +674,13 @@ (remove #(or (nil? %) (= (:frame-id %) frame-id)))) - rch [{:type :mov-objects - :page-id page-id - :parent-id frame-id - :shapes (mapv :id moving-shapes)}] + changes (-> (pcb/empty-changes it page-id) + (pcb/with-objects objects) + (pcb/change-parent frame-id moving-shapes))] - - uch (->> moving-shapes - (reverse) - (mapv (fn [shape] - {:type :mov-objects - :page-id page-id - :parent-id (:parent-id shape) - :index (cph/get-index-in-parent objects (:id shape)) - :shapes [(:id shape)]})))] - - (when-not (empty? uch) + (when-not (empty? changes) (rx/of dwu/pop-undo-into-transaction - (dch/commit-changes {:redo-changes rch - :undo-changes uch - :origin it}) + (dch/commit-changes changes) (dwu/commit-undo-transaction) (dwc/expand-collapse frame-id))))))) diff --git a/frontend/test/app/test_helpers/pages.cljs b/frontend/test/app/test_helpers/pages.cljs index 6d4ea9706..6f2cb3990 100644 --- a/frontend/test/app/test_helpers/pages.cljs +++ b/frontend/test/app/test_helpers/pages.cljs @@ -12,6 +12,7 @@ [app.main.data.workspace :as dw] [app.main.data.workspace.state-helpers :as wsh] [app.main.data.workspace.groups :as dwg] + [app.main.data.workspace.layout :as layout] [app.main.data.workspace.libraries-helpers :as dwlh])) ;; ---- Helpers to manage pages and objects @@ -21,8 +22,8 @@ (def initial-state {:current-file-id current-file-id :current-page-id nil - :workspace-global dw/default-workspace-global - :workspace-local dw/default-workspace-local + :workspace-layout layout/default-layout + :workspace-global layout/default-global :workspace-data {:id current-file-id :components {} :pages [] @@ -87,12 +88,12 @@ shapes (dwg/shapes-for-grouping (:objects page) ids)] (if (empty? shapes) state - (let [[group rchanges uchanges] - (dwg/prepare-create-group (:objects page) (:id page) shapes prefix true)] + (let [[group changes] + (dwg/prepare-create-group nil (:objects page) (:id page) shapes prefix true)] (swap! idmap assoc label (:id group)) (update state :workspace-data - cp/process-changes rchanges)))))) + cp/process-changes (:redo-changes changes))))))) (defn make-component [state label ids] @@ -101,7 +102,8 @@ shapes (dwg/shapes-for-grouping objects ids) [group rchanges uchanges] - (dwlh/generate-add-component shapes + (dwlh/generate-add-component nil + shapes (:objects page) (:id page) current-file-id)]