diff --git a/common/src/app/common/geom/shapes.cljc b/common/src/app/common/geom/shapes.cljc index 28bc5dcef0..3ea608848a 100644 --- a/common/src/app/common/geom/shapes.cljc +++ b/common/src/app/common/geom/shapes.cljc @@ -14,6 +14,7 @@ [app.common.geom.shapes.constraints :as gct] [app.common.geom.shapes.corners :as gsc] [app.common.geom.shapes.intersect :as gin] + [app.common.geom.shapes.layout :as gcl] [app.common.geom.shapes.path :as gsp] [app.common.geom.shapes.rect :as gpr] [app.common.geom.shapes.transforms :as gtr] @@ -174,6 +175,10 @@ ;; Constratins (dm/export gct/calc-child-modifiers) +;; Layout +(dm/export gcl/calc-layout-data) +(dm/export gcl/calc-layout-modifiers) + ;; PATHS (dm/export gsp/content->selrect) (dm/export gsp/transform-content) diff --git a/common/src/app/common/geom/shapes/layout.cljc b/common/src/app/common/geom/shapes/layout.cljc new file mode 100644 index 0000000000..bf88e4286f --- /dev/null +++ b/common/src/app/common/geom/shapes/layout.cljc @@ -0,0 +1,41 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) UXBOX Labs SL + +(ns app.common.geom.shapes.layout + (:require + [app.common.geom.matrix :as gmt] + [app.common.geom.point :as gpt] + [app.common.geom.shapes.rect :as gre] + [app.common.geom.shapes.transforms :as gtr])) + +(defn calc-layout-data + "Digest the layout data to pass it to the constrains" + [_parent children transformed-rect] + + (let [[children-width children-height] + (->> children (reduce (fn [[acc-width acc-height] shape] + [(+ acc-width (-> shape :points gre/points->rect :width)) + (+ acc-height (-> shape :points gre/points->rect :height))]) [0 0]))] + {:start-x (:x transformed-rect) + :start-y (:y transformed-rect) + :children-width children-width + :children-height children-height}) + ) + +(defn calc-layout-modifiers + [_parent child current-modifier _modifiers _transformed-rect {:keys [start-x start-y] :as layout-data}] + + (let [current-modifier (dissoc current-modifier :displacement-after) + child' (-> child (assoc :modifiers current-modifier) gtr/transform-shape) + bounds' (-> child' :points gre/points->selrect) + corner-p (gpt/point start-x start-y) + displacement (gmt/translate-matrix (gpt/subtract corner-p (gpt/point bounds'))) + modifiers (-> current-modifier + (assoc :displacement-after displacement)) + + next-x (+ start-x (:width bounds'))] + + [modifiers (assoc layout-data :start-x next-x )])) diff --git a/common/src/app/common/geom/shapes/transforms.cljc b/common/src/app/common/geom/shapes/transforms.cljc index 5adb7e988e..9b44e45dca 100644 --- a/common/src/app/common/geom/shapes/transforms.cljc +++ b/common/src/app/common/geom/shapes/transforms.cljc @@ -491,6 +491,7 @@ ([center modifiers] (let [displacement (:displacement modifiers) + displacement-after (:displacement-after modifiers) resize-v1 (:resize-vector modifiers) resize-v2 (:resize-vector-2 modifiers) origin-1 (:resize-origin modifiers (gpt/point)) @@ -512,6 +513,9 @@ rt-modif (:rotation modifiers)] (cond-> (gmt/matrix) + (some? displacement-after) + (gmt/multiply displacement-after) + (some? resize-1) (-> (gmt/translate origin-1) (cond-> (some? resize-transform) diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 8d2abf1c85..1b734551cf 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -45,6 +45,7 @@ [app.main.data.workspace.path.shapes-to-path :as dwps] [app.main.data.workspace.persistence :as dwp] [app.main.data.workspace.selection :as dws] + [app.main.data.workspace.shape-layout :as dwsl] [app.main.data.workspace.shapes :as dwsh] [app.main.data.workspace.state-helpers :as wsh] [app.main.data.workspace.thumbnails :as dwth] @@ -799,7 +800,8 @@ ids)] (rx/of (dch/commit-changes changes) - (dwco/expand-collapse parent-id)))))) + (dwco/expand-collapse parent-id) + (dwsl/update-layout-positions [parent-id])))))) (defn relocate-selected-shapes [parent-id to-index] diff --git a/frontend/src/app/main/data/workspace/shape_layout.cljs b/frontend/src/app/main/data/workspace/shape_layout.cljs index 5bfee61f38..9055f042ac 100644 --- a/frontend/src/app/main/data/workspace/shape_layout.cljs +++ b/frontend/src/app/main/data/workspace/shape_layout.cljs @@ -8,6 +8,8 @@ (:require [app.common.data :as d] [app.main.data.workspace.changes :as dwc] + [app.main.data.workspace.state-helpers :as wsh] + [app.main.data.workspace.transforms :as dwt] [beicon.core :as rx] [potok.core :as ptk])) @@ -35,8 +37,17 @@ (defn update-layout-positions [ids] - (ptk/reify ::update-layout-positions)) + (ptk/reify ::update-layout-positions + ptk/WatchEvent + (watch [_ state _] + (let [objects (wsh/lookup-page-objects state) + ids (->> ids (filter #(get-in objects [% :layout])))] + (if (d/not-empty? ids) + (rx/of (dwt/set-modifiers ids) + (dwt/apply-modifiers)) + (rx/empty)))))) +;; TODO: Remove constraints from children (defn create-layout [ids] (ptk/reify ::create-layout diff --git a/frontend/src/app/main/data/workspace/transforms.cljs b/frontend/src/app/main/data/workspace/transforms.cljs index 4d87ff0f6e..92dea577a3 100644 --- a/frontend/src/app/main/data/workspace/transforms.cljs +++ b/frontend/src/app/main/data/workspace/transforms.cljs @@ -113,7 +113,7 @@ (declare set-objects-modifiers) (declare get-ignore-tree) -(defn- set-modifiers +(defn set-modifiers ([ids] (set-modifiers ids nil false)) @@ -150,21 +150,19 @@ ([angle shapes center] (ptk/reify ::set-rotation-modifiers - ptk/UpdateEvent - (update [_ state] + ptk/WatchEvent + (watch [_ state _] (let [objects (wsh/lookup-page-objects state) - shapes (->> shapes - (remove #(get % :blocked false)) - (mapcat #(cph/get-children objects (:id %))) - (concat shapes) - (filter #((cpc/editable-attrs (:type %)) :rotation))) - - update-shape - (fn [modifiers shape] - (let [rotate-modifiers (gsh/rotation-modifiers shape center angle)] - (assoc-in modifiers [(:id shape) :modifiers] rotate-modifiers)))] - - (update state :workspace-modifiers #(reduce update-shape % shapes))))))) + shapes + (->> shapes + (remove #(get % :blocked false)) + (mapcat #(cph/get-children objects (:id %))) + (concat shapes) + (filter #((cpc/editable-attrs (:type %)) :rotation)))] + (->> (rx/from shapes) + (rx/map (fn [shape] + (let [rotate-modifiers (gsh/rotation-modifiers shape center angle)] + (set-modifiers [(:id shape)] rotate-modifiers)))))))))) (defn- update-grow-type [shape old-shape] @@ -180,18 +178,20 @@ change-to-fixed? (assoc :grow-type :fixed)))) -(defn- apply-modifiers - ([ids] - (apply-modifiers ids nil)) +(defn apply-modifiers + ([] + (apply-modifiers nil)) - ([ids {:keys [undo-transation?] :or {undo-transation? true}}] - (us/verify (s/coll-of uuid?) ids) + ([{:keys [undo-transation?] :or {undo-transation? true}}] (ptk/reify ::apply-modifiers ptk/WatchEvent (watch [_ state _] (let [objects (wsh/lookup-page-objects state) - ids-with-children (into (vec ids) (mapcat #(cph/get-children-ids objects %)) ids) object-modifiers (get state :workspace-modifiers) + + ids (keys object-modifiers) + ids-with-children (into (vec ids) (mapcat #(cph/get-children-ids objects %)) ids) + shapes (map (d/getf objects) ids) ignore-tree (->> (map #(get-ignore-tree object-modifiers objects %) shapes) (reduce merge {}))] @@ -202,7 +202,7 @@ (rx/empty)) (rx/of (dwg/move-frame-guides ids-with-children) (dch/update-shapes - ids-with-children + ids (fn [shape] (let [modif (get object-modifiers (:id shape)) text-shape? (cph/text-shape? shape)] @@ -374,6 +374,26 @@ (let [children (map (d/getf objects) (:shapes shape)) transformed-rect (gsh/transform-selrect (:selrect shape) modifiers) + set-layout-child + (fn [snap-pixel? {:keys [modif-tree] :as layout-data} child] + (let [current-modifier (get-in modif-tree [(:id child) :modifiers]) + + ;; child (-> (merge child old-modif) gsh/transform-shape) + + [child-modifiers next-layout-data] (gsh/calc-layout-modifiers shape child current-modifier modifiers transformed-rect layout-data) + child-modifiers (cond-> child-modifiers snap-pixel? (set-pixel-precision child)) + + ;;child-modifiers (if (some? old-modif) + ;; (d/deep-merge (:modifiers old-modif) child-modifiers) + ;; child-modifiers) + + modif-tree + (cond-> modif-tree + (not (gsh/empty-modifiers? child-modifiers)) + (set-modifiers-rec child child-modifiers))] + + (assoc next-layout-data :modif-tree modif-tree))) + set-child (fn [snap-pixel? modif-tree child] (let [child-modifiers (gsh/calc-child-modifiers shape child modifiers ignore-constraints transformed-rect) @@ -387,12 +407,35 @@ (assoc-in [(:id shape) :modifiers] modifiers)) resize-modif? - (or (:resize-vector modifiers) (:resize-vector-2 modifiers))] + (or (:resize-vector modifiers) (:resize-vector-2 modifiers)) - (reduce (partial set-child (and snap-pixel? resize-modif?)) modif-tree children)))] - (let [modifiers (cond-> modifiers snap-pixel? (set-pixel-precision shape))] - (set-modifiers-rec modif-tree shape modifiers)))) + modif-tree + (reduce (partial set-child (and snap-pixel? resize-modif?)) modif-tree children)] + + (cond + (:layout shape) + (let [result + (->> children + (reduce (partial set-layout-child (and snap-pixel? resize-modif?)) + (merge {:modif-tree modif-tree} + (gsh/calc-layout-data shape children transformed-rect))))] + (:modif-tree result)) + + :else + modif-tree)))] + + (let [modifiers (cond-> modifiers snap-pixel? (set-pixel-precision shape)) + modif-tree (set-modifiers-rec modif-tree shape modifiers) + + parent (get objects (:parent-id shape)) + + modif-tree + (cond-> modif-tree + (:layout parent) + (set-modifiers-rec parent nil))] + + modif-tree))) (defn- get-ignore-tree "Retrieves a map with the flag `ignore-geometry?` given a tree of modifiers" @@ -534,7 +577,7 @@ (rx/map #(conj current %))))) (rx/mapcat (partial resize shape initial-position layout)) (rx/take-until stoper)) - (rx/of (apply-modifiers ids) + (rx/of (apply-modifiers) (finish-transform)))))))) (defn update-dimensions @@ -562,7 +605,7 @@ ptk/WatchEvent (watch [_ _ _] - (rx/of (apply-modifiers ids))))) + (rx/of (apply-modifiers))))) (defn change-orientation "Change orientation of shapes, from the sidebar options form. @@ -588,7 +631,7 @@ ptk/WatchEvent (watch [_ _ _] - (rx/of (apply-modifiers ids))))) + (rx/of (apply-modifiers))))) ;; -- Rotate -------------------------------------------------------- @@ -631,7 +674,7 @@ (let [delta-angle (calculate-angle pos mod? shift?)] (set-rotation-modifiers delta-angle shapes group-center)))) (rx/take-until stoper)) - (rx/of (apply-modifiers (map :id shapes)) + (rx/of (apply-modifiers) (finish-transform))))))) (defn increase-rotation @@ -648,7 +691,7 @@ (set-rotation-modifiers delta [shape])))] (rx/concat (rx/from (->> ids (map #(get objects %)) (map rotate-shape))) - (rx/of (apply-modifiers ids))))))) + (rx/of (apply-modifiers))))))) ;; -- Move ---------------------------------------------------------- @@ -772,7 +815,7 @@ (rx/of (dwu/start-undo-transaction) (calculate-frame-for-move ids) - (apply-modifiers ids {:undo-transation? false}) + (apply-modifiers {:undo-transation? false}) (finish-transform) (dwu/commit-undo-transaction))))))))) @@ -820,7 +863,7 @@ (rx/take-until stopper)) (rx/of (move-selected direction shift?))) - (rx/of (apply-modifiers selected) + (rx/of (apply-modifiers) (finish-transform)))) (rx/empty)))))) @@ -850,7 +893,7 @@ displ (gmt/translate-matrix delta)] (rx/of (set-modifiers [id] {:displacement displ} false true) - (apply-modifiers [id])))))) + (apply-modifiers)))))) (defn check-frame-move? [target-frame-id objects position shape] @@ -911,7 +954,7 @@ :resize-origin origin :displacement (gmt/translate-matrix (gpt/point (- (:width selrect)) 0))} true) - (apply-modifiers selected)))))) + (apply-modifiers)))))) (defn flip-vertical-selected [] (ptk/reify ::flip-vertical-selected @@ -928,4 +971,4 @@ :resize-origin origin :displacement (gmt/translate-matrix (gpt/point 0 (- (:height selrect))))} true) - (apply-modifiers selected)))))) + (apply-modifiers)))))) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs index bf52ed3e70..6bdaec134b 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs @@ -200,7 +200,7 @@ (mf/defc layout-container-menu {::mf/wrap [#(mf/memo' % (mf/check-props ["ids" "values" "type"]))]} - [{:keys [ids type values] :as props}] + [{:keys [ids _type values] :as props}] (let [open? (mf/use-state false) gap-selected? (mf/use-state false) toggle-open (fn [] (swap! open? not)) @@ -226,8 +226,6 @@ (fn [type] (st/emit! (dwsl/update-layout ids {:layout-padding-type type}))) - select-all #(dom/select-target %) - select-all-gap (fn [event] (reset! gap-selected? true)