0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-04-15 16:31:25 -05:00

Remove fill/auto when resizing

This commit is contained in:
alonso.torres 2022-11-03 15:11:29 +01:00
parent 7f0054959f
commit 4ecc166055
13 changed files with 479 additions and 349 deletions
common/src/app/common
frontend/src/app

View file

@ -138,20 +138,21 @@
(let [modifiers (get-in modif-tree [(:id parent) :modifiers])
transformed-parent (gtr/transform-shape parent modifiers)
children (->> transformed-parent
:shapes
(map (comp apply-modifiers (d/getf objects))))
{auto-width :width auto-height :height}
(when (and (d/not-empty? children) (or (ctl/auto-height? parent) (ctl/auto-width? parent)))
(gcl/layout-content-bounds parent children))
(when (and (d/not-empty? children) (or (ctl/auto-height? transformed-parent) (ctl/auto-width? transformed-parent)))
(gcl/layout-content-bounds transformed-parent children))
modifiers
(cond-> modifiers
(and (some? auto-width) (ctl/auto-width? parent))
(and (some? auto-width) (ctl/auto-width? transformed-parent))
(set-parent-auto-width transformed-parent auto-width)
(and (some? auto-height) (ctl/auto-height? parent))
(and (some? auto-height) (ctl/auto-height? transformed-parent))
(set-parent-auto-height transformed-parent auto-height))]
(assoc-in modif-tree [(:id parent) :modifiers] modifiers))))

View file

@ -71,9 +71,12 @@
(defn get-children-ids
[objects id]
(if-let [shapes (-> (get objects id) :shapes (some-> vec))]
(into shapes (mapcat #(get-children-ids objects %)) shapes)
[]))
(letfn [(get-children-ids-rec
[id processed]
(when (not (contains? processed id))
(when-let [shapes (-> (get objects id) :shapes (some-> vec))]
(into shapes (mapcat #(get-children-ids-rec % (conj processed id))) shapes))))]
(get-children-ids-rec id #{})))
(defn get-children
[objects id]

View file

@ -32,6 +32,7 @@
;; - structure-child: Structure recursive
;; * scale-content
;; * rotation
;; * change-properties
(def conjv (fnil conj []))
@ -118,6 +119,13 @@
(-> modifiers
(update :structure-child conjv {:type :scale-content :value value})))
(defn set-change-property
[modifiers property value]
(-> modifiers
(update :structure-child conjv {:type :change-property
:property property
:value value})))
(defn add-modifiers
[modifiers new-modifiers]
@ -376,7 +384,7 @@
(d/removev remove? shapes)))
apply-modifier
(fn [shape {:keys [type value index rotation]}]
(fn [shape {:keys [type property value index rotation]}]
(cond-> shape
(= type :rotation)
(update :rotation #(mod (+ % rotation) 360))
@ -395,7 +403,10 @@
(update :shapes remove-children value)
(= type :scale-content)
(apply-scale-content value)))]
(apply-scale-content value)
(= type :change-property)
(assoc property value)))]
(as-> shape $
(reduce apply-modifier $ (:structure-parent modifiers))

View file

@ -97,8 +97,16 @@
::layout-item-min-w
::layout-item-align-self]))
(defn layout? [shape]
(and (= :frame (:type shape)) (= :flex (:layout shape))))
(defn layout?
([objects id]
(layout? (get objects id)))
([shape]
(and (= :frame (:type shape)) (= :flex (:layout shape)))))
(defn layout-child? [objects shape]
(let [parent-id (:parent-id shape)
parent (get objects parent-id)]
(layout? parent)))
(defn wrap? [{:keys [layout-wrap-type]}]
(= layout-wrap-type :wrap))

View file

@ -0,0 +1,284 @@
;; 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) KALEIDOS INC
(ns app.main.data.workspace.modifiers
"Events related with shapes transformations"
(:require
[app.common.data :as d]
[app.common.geom.point :as gpt]
[app.common.geom.shapes :as gsh]
[app.common.geom.shapes.flex-layout :as gsl]
[app.common.math :as mth]
[app.common.pages.common :as cpc]
[app.common.pages.helpers :as cph]
[app.common.spec :as us]
[app.common.types.modifiers :as ctm]
[app.main.data.workspace.changes :as dch]
[app.main.data.workspace.comments :as-alias dwcm]
[app.main.data.workspace.guides :as-alias dwg]
[app.main.data.workspace.state-helpers :as wsh]
[app.main.data.workspace.undo :as dwu]
[beicon.core :as rx]
[cljs.spec.alpha :as s]
[potok.core :as ptk]))
;; -- temporary modifiers -------------------------------------------
;; During an interactive transformation of shapes (e.g. when resizing or rotating
;; a group with the mouse), there are a lot of objects that need to be modified
;; (in this case, the group and all its children).
;;
;; To avoid updating the shapes theirselves, and forcing redraw of all components
;; that depend on the "objects" global state, we set a "modifiers" structure, with
;; the changes that need to be applied, and store it in :workspace-modifiers global
;; variable. The viewport reads this and merges it into the objects list it uses to
;; paint the viewport content, redrawing only the objects that have new modifiers.
;;
;; When the interaction is finished (e.g. user releases mouse button), the
;; apply-modifiers event is done, that consolidates all modifiers into the base
;; geometric attributes of the shapes.
(defn- check-delta
"If the shape is a component instance, check its relative position respect the
root of the component, and see if it changes after applying a transformation."
[shape root transformed-shape transformed-root objects modif-tree]
(let [root
(cond
(:component-root? shape)
shape
(nil? root)
(cph/get-root-shape objects shape)
:else root)
transformed-root
(cond
(:component-root? transformed-shape)
transformed-shape
(nil? transformed-root)
(as-> (cph/get-root-shape objects transformed-shape) $
(gsh/transform-shape (merge $ (get modif-tree (:id $)))))
:else transformed-root)
shape-delta
(when root
(gpt/point (- (gsh/left-bound shape) (gsh/left-bound root))
(- (gsh/top-bound shape) (gsh/top-bound root))))
transformed-shape-delta
(when transformed-root
(gpt/point (- (gsh/left-bound transformed-shape) (gsh/left-bound transformed-root))
(- (gsh/top-bound transformed-shape) (gsh/top-bound transformed-root))))
;; There are cases in that the coordinates change slightly (e.g. when
;; rounding to pixel, or when recalculating text positions in different
;; zoom levels). To take this into account, we ignore movements smaller
;; than 1 pixel.
distance (if (and shape-delta transformed-shape-delta)
(gpt/distance-vector shape-delta transformed-shape-delta)
(gpt/point 0 0))
ignore-geometry? (and (< (:x distance) 1) (< (:y distance) 1))]
[root transformed-root ignore-geometry?]))
(defn- get-ignore-tree
"Retrieves a map with the flag `ignore-geometry?` given a tree of modifiers"
([modif-tree objects shape]
(get-ignore-tree modif-tree objects shape nil nil {}))
([modif-tree objects shape root transformed-root ignore-tree]
(let [children (map (d/getf objects) (:shapes shape))
shape-id (:id shape)
transformed-shape (gsh/transform-shape shape (get modif-tree shape-id))
[root transformed-root ignore-geometry?]
(check-delta shape root transformed-shape transformed-root objects modif-tree)
ignore-tree (assoc ignore-tree shape-id ignore-geometry?)
set-child
(fn [ignore-tree child]
(get-ignore-tree modif-tree objects child root transformed-root ignore-tree))]
(reduce set-child ignore-tree children))))
(defn- update-grow-type
[shape old-shape]
(let [auto-width? (= :auto-width (:grow-type shape))
auto-height? (= :auto-height (:grow-type shape))
changed-width? (not (mth/close? (:width shape) (:width old-shape)))
changed-height? (not (mth/close? (:height shape) (:height old-shape)))
change-to-fixed? (or (and auto-width? (or changed-height? changed-width?))
(and auto-height? changed-height?))]
(cond-> shape
change-to-fixed?
(assoc :grow-type :fixed))))
(defn- clear-local-transform []
(ptk/reify ::clear-local-transform
ptk/UpdateEvent
(update [_ state]
(-> state
(dissoc :workspace-modifiers)
(dissoc ::current-move-selected)))))
(defn create-modif-tree
[ids modifiers]
(us/verify (s/coll-of uuid?) ids)
(into {} (map #(vector % {:modifiers modifiers})) ids))
(defn build-modif-tree
[ids objects get-modifier]
(us/verify (s/coll-of uuid?) ids)
(into {} (map #(vector % {:modifiers (get-modifier (get objects %))})) ids))
(defn build-change-frame-modifiers
[modif-tree objects selected target-frame position]
(let [origin-frame-ids (->> selected (group-by #(get-in objects [% :frame-id])))
layout? (get-in objects [target-frame :layout])
child-set (set (get-in objects [target-frame :shapes]))
drop-index (when layout? (gsl/get-drop-index target-frame objects position))
update-frame-modifiers
(fn [modif-tree [original-frame shapes]]
(let [shapes (->> shapes (d/removev #(= target-frame %)))
shapes (cond->> shapes
(and layout? (= original-frame target-frame))
;; When movining inside a layout frame remove the shapes that are not immediate children
(filterv #(contains? child-set %)))]
(cond-> modif-tree
(not= original-frame target-frame)
(-> (update-in [original-frame :modifiers] ctm/set-remove-children shapes)
(update-in [target-frame :modifiers] ctm/set-add-children shapes drop-index))
(and layout? (= original-frame target-frame))
(update-in [target-frame :modifiers] ctm/set-add-children shapes drop-index))))]
(reduce update-frame-modifiers modif-tree origin-frame-ids)))
(defn modif->js
[modif-tree objects]
(clj->js (into {}
(map (fn [[k v]]
[(get-in objects [k :name]) v]))
modif-tree)))
(defn set-modifiers
([modif-tree]
(set-modifiers modif-tree false))
([modif-tree ignore-constraints]
(set-modifiers modif-tree ignore-constraints false))
([modif-tree ignore-constraints ignore-snap-pixel]
(ptk/reify ::set-modifiers
ptk/UpdateEvent
(update [_ state]
(let [objects
(wsh/lookup-page-objects state)
snap-pixel?
(and (not ignore-snap-pixel) (contains? (:workspace-layout state) :snap-pixel-grid))
modif-tree
(gsh/set-objects-modifiers modif-tree objects ignore-constraints snap-pixel?)]
(assoc state :workspace-modifiers modif-tree))))))
;; Rotation use different algorithm to calculate children modifiers (and do not use child constraints).
(defn set-rotation-modifiers
([angle shapes]
(set-rotation-modifiers angle shapes (-> shapes gsh/selection-rect gsh/center-selrect)))
([angle shapes center]
(ptk/reify ::set-rotation-modifiers
ptk/UpdateEvent
(update [_ state]
(let [objects (wsh/lookup-page-objects state)
ids
(->> shapes
(remove #(get % :blocked false))
(filter #((cpc/editable-attrs (:type %)) :rotation))
(map :id))
get-modifier
(fn [shape]
(ctm/rotation shape center angle))
modif-tree
(-> (build-modif-tree ids objects get-modifier)
(gsh/set-objects-modifiers objects false false))]
(assoc state :workspace-modifiers modif-tree))))))
(defn apply-modifiers
([]
(apply-modifiers nil))
([{:keys [undo-transation?] :or {undo-transation? true}}]
(ptk/reify ::apply-modifiers
ptk/WatchEvent
(watch [_ state _]
(let [objects (wsh/lookup-page-objects state)
object-modifiers (get state :workspace-modifiers)
ids (or (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 {}))]
(rx/concat
(if undo-transation?
(rx/of (dwu/start-undo-transaction))
(rx/empty))
(rx/of (ptk/event ::dwg/move-frame-guides ids-with-children)
(ptk/event ::dwcm/move-frame-comment-threads ids-with-children)
(dch/update-shapes
ids
(fn [shape]
(let [modif (get-in object-modifiers [(:id shape) :modifiers])
text-shape? (cph/text-shape? shape)]
(-> shape
(gsh/transform-shape modif)
(cond-> text-shape?
(update-grow-type shape)))))
{:reg-objects? true
:ignore-tree ignore-tree
;; Attributes that can change in the transform. This way we don't have to check
;; all the attributes
:attrs [:selrect
:points
:x
:y
:width
:height
:content
:transform
:transform-inverse
:rotation
:position-data
:flip-x
:flip-y
:grow-type
:layout-item-h-sizing
:layout-item-v-sizing
]})
(clear-local-transform))
(if undo-transation?
(rx/of (dwu/commit-undo-transaction))
(rx/empty))))))))

View file

@ -21,6 +21,7 @@
[app.main.data.modal :as md]
[app.main.data.workspace.changes :as dch]
[app.main.data.workspace.collapse :as dwc]
[app.main.data.workspace.shape-layout :as dwsl]
[app.main.data.workspace.state-helpers :as wsh]
[app.main.data.workspace.thumbnails :as dwt]
[app.main.data.workspace.zoom :as dwz]
@ -552,8 +553,11 @@
(filter #(= :frame (get-in % [:obj :type])))
(map #(vector (:old-id %) (get-in % [:obj :id]))))
id-duplicated (first new-selected)]
id-duplicated (first new-selected)
frames (into #{}
(map #(get-in objects [% :frame-id]))
selected)]
(rx/concat
(->> (rx/from dup-frames)
(rx/map (fn [[old-id new-id]] (dwt/duplicate-thumbnail old-id new-id))))
@ -561,6 +565,7 @@
;; Warning: This order is important for the focus mode.
(rx/of (dch/commit-changes changes)
(select-shapes new-selected)
(dwsl/update-layout-positions frames)
(memorize-duplicated id-original id-duplicated))))))))))
(defn change-hover-state

View file

@ -11,8 +11,8 @@
[app.common.types.modifiers :as ctm]
[app.common.types.shape.layout :as ctl]
[app.main.data.workspace.changes :as dwc]
[app.main.data.workspace.modifiers :as dwm]
[app.main.data.workspace.state-helpers :as wsh]
[app.main.data.workspace.transforms :as dwt]
[beicon.core :as rx]
[potok.core :as ptk]))
@ -50,11 +50,11 @@
ptk/WatchEvent
(watch [_ state _]
(let [objects (wsh/lookup-page-objects state)
ids (->> ids (filter #(get-in objects [% :layout])))]
ids (->> ids (filter (partial ctl/layout? objects)))]
(if (d/not-empty? ids)
(let [modif-tree (dwt/create-modif-tree ids (ctm/reflow))]
(rx/of (dwt/set-modifiers modif-tree)
(dwt/apply-modifiers)))
(let [modif-tree (dwm/create-modif-tree ids (ctm/reflow))]
(rx/of (dwm/set-modifiers modif-tree)
(dwm/apply-modifiers)))
(rx/empty))))))
;; TODO LAYOUT: Remove constraints from children

View file

@ -14,15 +14,14 @@
[app.common.geom.shapes.flex-layout :as gsl]
[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]
[app.common.types.modifiers :as ctm]
[app.common.types.shape-tree :as ctst]
[app.common.types.shape.layout :as ctl]
[app.main.data.workspace.changes :as dch]
[app.main.data.workspace.collapse :as dwc]
[app.main.data.workspace.comments :as-alias dwcm]
[app.main.data.workspace.guides :as-alias dwg]
[app.main.data.workspace.modifiers :as dwm]
[app.main.data.workspace.selection :as dws]
[app.main.data.workspace.state-helpers :as wsh]
[app.main.data.workspace.undo :as dwu]
@ -97,269 +96,12 @@
(update state :workspace-local dissoc :transform))))
;; -- Temporary modifiers -------------------------------------------
;; During an interactive transformation of shapes (e.g. when resizing or rotating
;; a group with the mouse), there are a lot of objects that need to be modified
;; (in this case, the group and all its children).
;;
;; To avoid updating the shapes theirselves, and forcing redraw of all components
;; that depend on the "objects" global state, we set a "modifiers" structure, with
;; the changes that need to be applied, and store it in :workspace-modifiers global
;; variable. The viewport reads this and merges it into the objects list it uses to
;; paint the viewport content, redrawing only the objects that have new modifiers.
;;
;; When the interaction is finished (e.g. user releases mouse button), the
;; apply-modifiers event is done, that consolidates all modifiers into the base
;; geometric attributes of the shapes.
(declare clear-local-transform)
(declare get-ignore-tree)
(defn create-modif-tree
[ids modifiers]
(us/verify (s/coll-of uuid?) ids)
(into {} (map #(vector % {:modifiers modifiers})) ids))
(defn build-modif-tree
[ids objects get-modifier]
(us/verify (s/coll-of uuid?) ids)
(into {} (map #(vector % {:modifiers (get-modifier (get objects %))})) ids))
(defn build-change-frame-modifiers
[modif-tree objects selected target-frame position]
(let [origin-frame-ids (->> selected (group-by #(get-in objects [% :frame-id])))
layout? (get-in objects [target-frame :layout])
child-set (set (get-in objects [target-frame :shapes]))
drop-index (when layout? (gsl/get-drop-index target-frame objects position))
update-frame-modifiers
(fn [modif-tree [original-frame shapes]]
(let [shapes (->> shapes (d/removev #(= target-frame %)))
shapes (cond->> shapes
(and layout? (= original-frame target-frame))
;; When movining inside a layout frame remove the shapes that are not immediate children
(filterv #(contains? child-set %)))]
(cond-> modif-tree
(not= original-frame target-frame)
(-> (update-in [original-frame :modifiers] ctm/set-remove-children shapes)
(update-in [target-frame :modifiers] ctm/set-add-children shapes drop-index))
(and layout? (= original-frame target-frame))
(update-in [target-frame :modifiers] ctm/set-add-children shapes drop-index))))]
(reduce update-frame-modifiers modif-tree origin-frame-ids)))
(defn modif->js
[modif-tree objects]
(clj->js (into {}
(map (fn [[k v]]
[(get-in objects [k :name]) v]))
modif-tree)))
(defn set-modifiers
([modif-tree]
(set-modifiers modif-tree false))
([modif-tree ignore-constraints]
(set-modifiers modif-tree ignore-constraints false))
([modif-tree ignore-constraints ignore-snap-pixel]
(ptk/reify ::set-modifiers
ptk/UpdateEvent
(update [_ state]
(let [objects
(wsh/lookup-page-objects state)
snap-pixel?
(and (not ignore-snap-pixel) (contains? (:workspace-layout state) :snap-pixel-grid))
modif-tree
(gsh/set-objects-modifiers modif-tree objects ignore-constraints snap-pixel?)]
(assoc state :workspace-modifiers modif-tree))))))
;; Rotation use different algorithm to calculate children modifiers (and do not use child constraints).
(defn- set-rotation-modifiers
([angle shapes]
(set-rotation-modifiers angle shapes (-> shapes gsh/selection-rect gsh/center-selrect)))
([angle shapes center]
(ptk/reify ::set-rotation-modifiers
ptk/UpdateEvent
(update [_ state]
(let [objects (wsh/lookup-page-objects state)
ids
(->> shapes
(remove #(get % :blocked false))
(filter #((cpc/editable-attrs (:type %)) :rotation))
(map :id))
get-modifier
(fn [shape]
(ctm/rotation shape center angle))
modif-tree
(-> (build-modif-tree ids objects get-modifier)
(gsh/set-objects-modifiers objects false false))]
(assoc state :workspace-modifiers modif-tree))))))
(defn- update-grow-type
[shape old-shape]
(let [auto-width? (= :auto-width (:grow-type shape))
auto-height? (= :auto-height (:grow-type shape))
changed-width? (not (mth/close? (:width shape) (:width old-shape)))
changed-height? (not (mth/close? (:height shape) (:height old-shape)))
change-to-fixed? (or (and auto-width? (or changed-height? changed-width?))
(and auto-height? changed-height?))]
(cond-> shape
change-to-fixed?
(assoc :grow-type :fixed))))
(defn apply-modifiers
([]
(apply-modifiers nil))
([{:keys [undo-transation?] :or {undo-transation? true}}]
(ptk/reify ::apply-modifiers
ptk/WatchEvent
(watch [_ state _]
(let [objects (wsh/lookup-page-objects state)
object-modifiers (get state :workspace-modifiers)
ids (or (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 {}))]
(rx/concat
(if undo-transation?
(rx/of (dwu/start-undo-transaction))
(rx/empty))
(rx/of (ptk/event ::dwg/move-frame-guides ids-with-children)
(ptk/event ::dwcm/move-frame-comment-threads ids-with-children)
(dch/update-shapes
ids
(fn [shape]
(let [modif (get-in object-modifiers [(:id shape) :modifiers])
text-shape? (cph/text-shape? shape)]
(-> shape
(gsh/transform-shape modif)
(cond-> text-shape?
(update-grow-type shape)))))
{:reg-objects? true
:ignore-tree ignore-tree
;; Attributes that can change in the transform. This way we don't have to check
;; all the attributes
:attrs [:selrect
:points
:x
:y
:width
:height
:content
:transform
:transform-inverse
:rotation
:position-data
:flip-x
:flip-y
:grow-type]})
(clear-local-transform))
(if undo-transation?
(rx/of (dwu/commit-undo-transaction))
(rx/empty))))))))
(defn- check-delta
"If the shape is a component instance, check its relative position respect the
root of the component, and see if it changes after applying a transformation."
[shape root transformed-shape transformed-root objects modif-tree]
(let [root
(cond
(:component-root? shape)
shape
(nil? root)
(cph/get-root-shape objects shape)
:else root)
transformed-root
(cond
(:component-root? transformed-shape)
transformed-shape
(nil? transformed-root)
(as-> (cph/get-root-shape objects transformed-shape) $
(gsh/transform-shape (merge $ (get modif-tree (:id $)))))
:else transformed-root)
shape-delta
(when root
(gpt/point (- (gsh/left-bound shape) (gsh/left-bound root))
(- (gsh/top-bound shape) (gsh/top-bound root))))
transformed-shape-delta
(when transformed-root
(gpt/point (- (gsh/left-bound transformed-shape) (gsh/left-bound transformed-root))
(- (gsh/top-bound transformed-shape) (gsh/top-bound transformed-root))))
;; There are cases in that the coordinates change slightly (e.g. when
;; rounding to pixel, or when recalculating text positions in different
;; zoom levels). To take this into account, we ignore movements smaller
;; than 1 pixel.
distance (if (and shape-delta transformed-shape-delta)
(gpt/distance-vector shape-delta transformed-shape-delta)
(gpt/point 0 0))
ignore-geometry? (and (< (:x distance) 1) (< (:y distance) 1))]
[root transformed-root ignore-geometry?]))
(defn- get-ignore-tree
"Retrieves a map with the flag `ignore-geometry?` given a tree of modifiers"
([modif-tree objects shape]
(get-ignore-tree modif-tree objects shape nil nil {}))
([modif-tree objects shape root transformed-root ignore-tree]
(let [children (map (d/getf objects) (:shapes shape))
shape-id (:id shape)
transformed-shape (gsh/transform-shape shape (get modif-tree shape-id))
[root transformed-root ignore-geometry?]
(check-delta shape root transformed-shape transformed-root objects modif-tree)
ignore-tree (assoc ignore-tree shape-id ignore-geometry?)
set-child
(fn [ignore-tree child]
(get-ignore-tree modif-tree objects child root transformed-root ignore-tree))]
(reduce set-child ignore-tree children))))
(defn- clear-local-transform []
(ptk/reify ::clear-local-transform
ptk/UpdateEvent
(update [_ state]
(-> state
(dissoc :workspace-modifiers)
(dissoc ::current-move-selected)))))
;; -- Resize --------------------------------------------------------
(defn start-resize
"Enter mouse resize mode, until mouse button is released."
[handler ids shape]
(letfn [(resize [shape initial layout [point lock? center? point-snap]]
(letfn [(resize [shape objects initial layout [point lock? center? point-snap]]
(let [{:keys [width height]} (:selrect shape)
{:keys [rotation]} shape
@ -423,18 +165,42 @@
(some? displacement)
(gpt/add displacement))
;; When the horizontal/vertical scale a flex children with auto/fill
;; we change it too fixed
layout? (ctl/layout? shape)
layout-child? (ctl/layout-child? objects shape)
auto-width? (ctl/auto-width? shape)
fill-width? (ctl/fill-width? shape)
auto-height? (ctl/auto-height? shape)
fill-height? (ctl/fill-height? shape)
set-fix-width?
(and (not (mth/close? (:x scalev) 1))
(or (and (or layout? layout-child?) auto-width?)
(and layout-child? fill-width?)))
set-fix-height?
(and (not (mth/close? (:y scalev) 1))
(or (and (or layout? layout-child?) auto-height?)
(and layout-child? fill-height?)))
modifiers
(-> (ctm/empty-modifiers)
(cond-> displacement
(ctm/set-move displacement))
(ctm/set-resize scalev resize-origin shape-transform shape-transform-inverse)
(cond-> set-fix-width?
(ctm/set-change-property :layout-item-h-sizing :fix))
(cond-> set-fix-height?
(ctm/set-change-property :layout-item-v-sizing :fix))
(cond-> scale-text
(ctm/set-scale-content (:x scalev))))
modif-tree (create-modif-tree ids modifiers)]
(rx/of (set-modifiers modif-tree))))
modif-tree (dwm/create-modif-tree ids modifiers)]
(rx/of (dwm/set-modifiers modif-tree))))
;; Unifies the instantaneous proportion lock modifier
;; activated by Shift key and the shapes own proportion
@ -458,6 +224,7 @@
zoom (get-in state [:workspace-local :zoom] 1)
objects (wsh/lookup-page-objects state page-id)
resizing-shapes (map #(get objects %) ids)]
(rx/concat
(->> ms/mouse-position
(rx/with-latest-from ms/mouse-position-shift ms/mouse-position-alt)
@ -465,9 +232,9 @@
(rx/switch-map (fn [[point _ _ :as current]]
(->> (snap/closest-snap-point page-id resizing-shapes objects layout zoom focus point)
(rx/map #(conj current %)))))
(rx/mapcat (partial resize shape initial-position layout))
(rx/mapcat (partial resize shape objects initial-position layout))
(rx/take-until stoper))
(rx/of (apply-modifiers)
(rx/of (dwm/apply-modifiers)
(finish-transform))))))))
(defn update-dimensions
@ -487,14 +254,14 @@
(fn [shape] (ctm/change-dimensions shape attr value))
modif-tree
(-> (build-modif-tree ids objects get-modifier)
(-> (dwm/build-modif-tree ids objects get-modifier)
(gsh/set-objects-modifiers objects false snap-pixel?))]
(assoc state :workspace-modifiers modif-tree)))
ptk/WatchEvent
(watch [_ _ _]
(rx/of (apply-modifiers)))))
(rx/of (dwm/apply-modifiers)))))
(defn change-orientation
"Change orientation of shapes, from the sidebar options form.
@ -512,14 +279,14 @@
(fn [shape] (ctm/change-orientation-modifiers shape orientation))
modif-tree
(-> (build-modif-tree ids objects get-modifier)
(-> (dwm/build-modif-tree ids objects get-modifier)
(gsh/set-objects-modifiers objects false snap-pixel?))]
(assoc state :workspace-modifiers modif-tree)))
ptk/WatchEvent
(watch [_ _ _]
(rx/of (apply-modifiers)))))
(rx/of (dwm/apply-modifiers)))))
;; -- Rotate --------------------------------------------------------
@ -560,9 +327,9 @@
(rx/map
(fn [[[pos mod?] shift?]]
(let [delta-angle (calculate-angle pos mod? shift?)]
(set-rotation-modifiers delta-angle shapes group-center))))
(dwm/set-rotation-modifiers delta-angle shapes group-center))))
(rx/take-until stoper))
(rx/of (apply-modifiers)
(rx/of (dwm/apply-modifiers)
(finish-transform)))))))
(defn increase-rotation
@ -576,10 +343,10 @@
objects (wsh/lookup-page-objects state page-id)
rotate-shape (fn [shape]
(let [delta (- rotation (:rotation shape))]
(set-rotation-modifiers delta [shape])))]
(dwm/set-rotation-modifiers delta [shape])))]
(rx/concat
(rx/from (->> ids (map #(get objects %)) (map rotate-shape)))
(rx/of (apply-modifiers)))))))
(rx/of (dwm/apply-modifiers)))))))
;; -- Move ----------------------------------------------------------
@ -705,15 +472,15 @@
(fn [move-vector]
(let [position (gpt/add from-position move-vector)
target-frame (ctst/top-nested-frame objects position)]
(-> (create-modif-tree ids (ctm/move move-vector))
(build-change-frame-modifiers objects selected target-frame position)
(set-modifiers)))))
(-> (dwm/create-modif-tree ids (ctm/move move-vector))
(dwm/build-change-frame-modifiers objects selected target-frame position)
(dwm/set-modifiers)))))
(rx/take-until stopper)))
(rx/of (dwu/start-undo-transaction)
(calculate-frame-for-move ids)
(apply-modifiers {:undo-transation? false})
(dwm/apply-modifiers {:undo-transation? false})
(finish-transform)
(dwu/commit-undo-transaction)))))))))
@ -756,12 +523,12 @@
(rx/merge
(->> move-events
(rx/scan #(gpt/add %1 mov-vec) (gpt/point 0 0))
(rx/map #(create-modif-tree selected (ctm/move %)))
(rx/map (partial set-modifiers))
(rx/map #(dwm/create-modif-tree selected (ctm/move %)))
(rx/map (partial dwm/set-modifiers))
(rx/take-until stopper))
(rx/of (move-selected direction shift?)))
(rx/of (apply-modifiers)
(rx/of (dwm/apply-modifiers)
(finish-transform))))
(rx/empty))))))
@ -789,10 +556,10 @@
(or (:y position) (:y bbox)))
delta (gpt/subtract pos cpos)
modif-tree (create-modif-tree [id] (ctm/move delta))]
modif-tree (dwm/create-modif-tree [id] (ctm/move delta))]
(rx/of (set-modifiers modif-tree)
(apply-modifiers))))))
(rx/of (dwm/set-modifiers modif-tree)
(dwm/apply-modifiers))))))
(defn- calculate-frame-for-move
[ids]
@ -851,14 +618,14 @@
selrect (gsh/selection-rect shapes)
origin (gpt/point (:x selrect) (+ (:y selrect) (/ (:height selrect) 2)))
modif-tree (create-modif-tree
modif-tree (dwm/create-modif-tree
selected
(-> (ctm/empty-modifiers)
(ctm/set-resize (gpt/point -1.0 1.0) origin)
(ctm/move (gpt/point (:width selrect) 0))))]
(rx/of (set-modifiers modif-tree true)
(apply-modifiers))))))
(rx/of (dwm/set-modifiers modif-tree true)
(dwm/apply-modifiers))))))
(defn flip-vertical-selected []
(ptk/reify ::flip-vertical-selected
@ -870,11 +637,11 @@
selrect (gsh/selection-rect shapes)
origin (gpt/point (+ (:x selrect) (/ (:width selrect) 2)) (:y selrect))
modif-tree (create-modif-tree
modif-tree (dwm/create-modif-tree
selected
(-> (ctm/empty-modifiers)
(ctm/set-resize (gpt/point 1.0 -1.0) origin)
(ctm/move (gpt/point 0 (:height selrect)))))]
(rx/of (set-modifiers modif-tree true)
(apply-modifiers))))))
(rx/of (dwm/set-modifiers modif-tree true)
(dwm/apply-modifiers))))))

View file

@ -443,7 +443,8 @@
(l/derived
(fn [objects]
(->> ids
(some #(-> (cph/get-parent objects %) ctl/layout?))))
(map (d/getf objects))
(some (partial ctl/layout-child? objects))))
workspace-page-objects))
(defn get-flex-child-viewer?
@ -452,8 +453,8 @@
(fn [state]
(let [objects (wsh/lookup-viewer-objects state page-id)]
(into []
(comp (filter #(= :flex (:layout (cph/get-parent objects %))))
(map #(get objects %)))
(comp (filter (partial ctl/layout-child? objects))
(map (d/getf objects)))
ids)))
st/state =))

View file

@ -87,6 +87,12 @@
flex-child? (->> selection-parents (some ctl/layout?))
flex-container? (ctl/layout? shape)
flex-auto-width? (ctl/auto-width? shape)
flex-fill-width? (ctl/fill-width? shape)
flex-auto-height? (ctl/auto-height? shape)
flex-fill-height? (ctl/fill-height? shape)
;; To show interactively the measures while the user is manipulating
;; the shape with the mouse, generate a copy of the shapes applying
;; the transient transformations.
@ -306,6 +312,7 @@
:placeholder "--"
:on-click select-all
:on-change on-width-change
:disabled (and (or flex-child? flex-container?) (or flex-auto-width? flex-fill-width?))
:value (:width values)}]]
[:div.input-element.height {:title (tr "workspace.options.height")}
@ -314,6 +321,7 @@
:placeholder "--"
:on-click select-all
:on-change on-height-change
:disabled (and (or flex-child? flex-container?) (or flex-auto-height? flex-fill-height?))
:value (:height values)}]]
[:div.lock-size {:class (dom/classnames

View file

@ -286,7 +286,7 @@
(when show-frame-outline?
[:& outline/shape-outlines
{:objects base-objects
{:objects objects-modified
:hover #{(->> @hover-ids
(filter #(cph/frame-shape? (get base-objects %)))
(remove selected)

View file

@ -13,6 +13,44 @@
[app.common.pages.helpers :as cph]
[rumext.v2 :as mf]))
;; Helper to debug the bounds when set the "hug" content property
#_(mf/defc debug-bounds
"Debug component to show the auto-layout drop areas"
{::mf/wrap-props false}
[props]
(let [objects (unchecked-get props "objects")
selected-shapes (unchecked-get props "selected-shapes")
hover-top-frame-id (unchecked-get props "hover-top-frame-id")
selected-frame
(when (and (= (count selected-shapes) 1) (= :frame (-> selected-shapes first :type)))
(first selected-shapes))
shape (or selected-frame (get objects hover-top-frame-id))]
(when (and shape (:layout shape))
(let [children (cph/get-immediate-children objects (:id shape))
layout-data (gsl/calc-layout-data shape children)
{pad-top :p1 pad-right :p2 pad-bottom :p3 pad-left :p4} (:layout-padding shape)
pad-top (or pad-top 0)
pad-right (or pad-right 0)
pad-bottom (or pad-bottom 0)
pad-left (or pad-left 0)
layout-bounds (gsl/layout-content-bounds shape children)]
[:g.debug-layout {:pointer-events "none"
:transform (gsh/transform-str shape)}
[:rect {:x (:x layout-bounds)
:y (:y layout-bounds)
:width (:width layout-bounds)
:height (:height layout-bounds)
:style {:stroke "red"
:fill "none"}}]]))))
(mf/defc debug-layout
"Debug component to show the auto-layout drop areas"
{::mf/wrap-props false}
@ -27,7 +65,7 @@
(first selected-shapes))
shape (or selected-frame (get objects hover-top-frame-id))]
(when (and shape (:layout shape))
(let [children (cph/get-immediate-children objects (:id shape))
layout-data (gsl/calc-layout-data shape children)

View file

@ -13,6 +13,7 @@
[app.common.pages.diff :as diff]
[app.common.pages.helpers :as cph]
[app.common.types.shape-tree :as ctst]
[app.common.types.shape.layout :as ctl]
[app.common.uuid :as uuid]
[app.util.geom.grid :as gg]
[app.util.geom.snap-points :as snap]
@ -70,7 +71,7 @@
(mapv grid->snap)))))
(defn- add-frame
[page-data frame]
[objects page-data frame]
(let [frame-id (:id frame)
parent-id (:parent-id frame)
frame-data (->> (snap/shape-snap-points frame)
@ -79,21 +80,24 @@
:pt %)))
grid-x-data (get-grids-snap-points frame :x)
grid-y-data (get-grids-snap-points frame :y)]
(-> page-data
;; Update root frame information
(assoc-in [uuid/zero :objects-data frame-id] frame-data)
(update-in [parent-id :x] (make-insert-tree-data frame-data :x))
(update-in [parent-id :y] (make-insert-tree-data frame-data :y))
;; Update frame information
(assoc-in [frame-id :objects-data frame-id] (d/concat-vec frame-data grid-x-data grid-y-data))
(update-in [frame-id :x] #(or % (rt/make-tree)))
(update-in [frame-id :y] #(or % (rt/make-tree)))
(update-in [frame-id :x] (make-insert-tree-data (d/concat-vec frame-data grid-x-data) :x))
(update-in [frame-id :y] (make-insert-tree-data (d/concat-vec frame-data grid-y-data) :y)))))
(cond-> page-data
(not (ctl/layout-child? objects frame))
(-> ;; Update root frame information
(assoc-in [uuid/zero :objects-data frame-id] frame-data)
(update-in [parent-id :x] (make-insert-tree-data frame-data :x))
(update-in [parent-id :y] (make-insert-tree-data frame-data :y))
;; Update frame information
(assoc-in [frame-id :objects-data frame-id] (d/concat-vec frame-data grid-x-data grid-y-data))
(update-in [frame-id :x] #(or % (rt/make-tree)))
(update-in [frame-id :y] #(or % (rt/make-tree)))
(update-in [frame-id :x] (make-insert-tree-data (d/concat-vec frame-data grid-x-data) :x))
(update-in [frame-id :y] (make-insert-tree-data (d/concat-vec frame-data grid-y-data) :y))))))
(defn- add-shape
[page-data shape]
[objects page-data shape]
(let [frame-id (:frame-id shape)
snap-points (snap/shape-snap-points shape)
shape-data (->> snap-points
@ -101,11 +105,11 @@
:type :shape
:id (:id shape)
:pt %)))]
(-> page-data
(assoc-in [frame-id :objects-data (:id shape)] shape-data)
(update-in [frame-id :x] (make-insert-tree-data shape-data :x))
(update-in [frame-id :y] (make-insert-tree-data shape-data :y)))))
(cond-> page-data
(not (ctl/layout-child? objects shape))
(-> (assoc-in [frame-id :objects-data (:id shape)] shape-data)
(update-in [frame-id :x] (make-insert-tree-data shape-data :x))
(update-in [frame-id :y] (make-insert-tree-data shape-data :y))))))
(defn- add-guide
[objects page-data guide]
@ -164,22 +168,22 @@
(update-in [:guides (:axis guide)] (make-delete-tree-data guide-data (:axis guide)))))))
(defn- update-frame
[page-data [_ new-frame]]
[objects page-data [_ new-frame]]
(let [frame-id (:id new-frame)
root-data (get-in page-data [uuid/zero :objects-data frame-id])
frame-data (get-in page-data [frame-id :objects-data frame-id])]
(-> page-data
(update-in [uuid/zero :x] (make-delete-tree-data root-data :x))
(update-in [uuid/zero :y] (make-delete-tree-data root-data :y))
(update-in [frame-id :x] (make-delete-tree-data frame-data :x))
(update-in [frame-id :y] (make-delete-tree-data frame-data :y))
(add-frame new-frame))))
(as-> page-data $
(update-in $ [uuid/zero :x] (make-delete-tree-data root-data :x))
(update-in $ [uuid/zero :y] (make-delete-tree-data root-data :y))
(update-in $ [frame-id :x] (make-delete-tree-data frame-data :x))
(update-in $ [frame-id :y] (make-delete-tree-data frame-data :y))
(add-frame objects $ new-frame))))
(defn- update-shape
[page-data [old-shape new-shape]]
(-> page-data
(remove-shape old-shape)
(add-shape new-shape)))
[objects page-data [old-shape new-shape]]
(as-> page-data $
(remove-shape $ old-shape)
(add-shape objects $ new-shape)))
(defn- update-guide
[objects page-data [old-guide new-guide]]
@ -205,8 +209,8 @@
page-data
(as-> {} $
(add-root-frame $)
(reduce add-frame $ frames)
(reduce add-shape $ shapes)
(reduce (partial add-frame objects) $ frames)
(reduce (partial add-shape objects) $ shapes)
(reduce (partial add-guide objects) $ guides))]
(assoc snap-data (:id page) page-data)))
@ -233,16 +237,16 @@
(diff/calculate-page-diff old-page page snap-attrs)]
(as-> page-data $
(reduce update-shape $ change-frame-shapes)
(reduce (partial update-shape objects) $ change-frame-shapes)
(reduce remove-frame $ removed-frames)
(reduce remove-shape $ removed-shapes)
(reduce update-frame $ updated-frames)
(reduce update-shape $ updated-shapes)
(reduce add-frame $ new-frames)
(reduce add-shape $ new-shapes)
(reduce remove-guide $ removed-guides)
(reduce (partial update-frame objects) $ updated-frames)
(reduce (partial update-shape objects) $ updated-shapes)
(reduce (partial add-frame objects) $ new-frames)
(reduce (partial add-shape objects) $ new-shapes)
;; Guides functions. Need objects to get its frame data
(reduce remove-guide $ removed-guides)
(reduce (partial update-guide objects) $ change-frame-guides)
(reduce (partial update-guide objects) $ updated-guides)
(reduce (partial add-guide objects) $ new-guides)))))