From 7f0054959ff92dc096ddbe80638d2ba9ec5f4ca0 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Wed, 2 Nov 2022 14:14:22 +0100 Subject: [PATCH] :sparkles: Hug content to frames --- common/src/app/common/geom/shapes.cljc | 5 - .../app/common/geom/shapes/constraints.cljc | 2 +- .../app/common/geom/shapes/flex_layout.cljc | 11 +- .../geom/shapes/flex_layout/bounds.cljc | 113 +++++++++++ .../common/geom/shapes/flex_layout/lines.cljc | 14 +- .../geom/shapes/flex_layout/modifiers.cljc | 29 +-- .../src/app/common/geom/shapes/modifiers.cljc | 192 ++++++++++++------ common/src/app/common/geom/shapes/points.cljc | 17 +- common/src/app/common/geom/shapes/rect.cljc | 13 ++ .../app/common/geom/shapes/transforms.cljc | 33 ++- common/src/app/common/types/modifiers.cljc | 120 +++++++---- common/src/app/common/types/shape/layout.cljc | 6 + .../test/common_tests/geom_shapes_test.cljc | 2 - .../app/main/data/workspace/shape_layout.cljs | 7 +- frontend/src/app/main/refs.cljs | 3 +- .../sidebar/options/menus/layout_item.cljs | 25 ++- .../sidebar/options/shapes/frame.cljs | 25 +-- 17 files changed, 448 insertions(+), 169 deletions(-) create mode 100644 common/src/app/common/geom/shapes/flex_layout/bounds.cljc diff --git a/common/src/app/common/geom/shapes.cljc b/common/src/app/common/geom/shapes.cljc index 941e6b634..163a29b1c 100644 --- a/common/src/app/common/geom/shapes.cljc +++ b/common/src/app/common/geom/shapes.cljc @@ -13,7 +13,6 @@ [app.common.geom.shapes.common :as gco] [app.common.geom.shapes.constraints :as gct] [app.common.geom.shapes.corners :as gsc] - [app.common.geom.shapes.flex-layout :as gcl] [app.common.geom.shapes.intersect :as gin] [app.common.geom.shapes.modifiers :as gsm] [app.common.geom.shapes.path :as gsp] @@ -184,10 +183,6 @@ ;; 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/constraints.cljc b/common/src/app/common/geom/shapes/constraints.cljc index ce4171d02..2be4becad 100644 --- a/common/src/app/common/geom/shapes/constraints.cljc +++ b/common/src/app/common/geom/shapes/constraints.cljc @@ -265,7 +265,7 @@ (if (and (= :scale constraints-h) (= :scale constraints-v)) modifiers - (let [transformed-child (gst/transform-shape child modifiers) + (let [transformed-child (gst/transform-shape child (ctm/select-child-modifiers modifiers)) modifiers (normalize-modifiers constraints-h constraints-v modifiers child parent transformed-child transformed-parent) transformed-child (gst/transform-shape child modifiers) diff --git a/common/src/app/common/geom/shapes/flex_layout.cljc b/common/src/app/common/geom/shapes/flex_layout.cljc index 8361d230f..b61eabf09 100644 --- a/common/src/app/common/geom/shapes/flex_layout.cljc +++ b/common/src/app/common/geom/shapes/flex_layout.cljc @@ -7,12 +7,15 @@ (ns app.common.geom.shapes.flex-layout (:require [app.common.data.macros :as dm] + [app.common.geom.shapes.flex-layout.bounds :as fbo] [app.common.geom.shapes.flex-layout.drop-area :as fdr] [app.common.geom.shapes.flex-layout.lines :as fli] [app.common.geom.shapes.flex-layout.modifiers :as fmo])) -(dm/export fli/calc-layout-data) -(dm/export fmo/normalize-child-modifiers) -(dm/export fmo/calc-layout-modifiers) -(dm/export fdr/layout-drop-areas) +(dm/export fbo/layout-content-bounds) (dm/export fdr/get-drop-index) +(dm/export fdr/layout-drop-areas) +(dm/export fli/calc-layout-data) +(dm/export fmo/layout-child-modifiers) +(dm/export fmo/normalize-child-modifiers) + diff --git a/common/src/app/common/geom/shapes/flex_layout/bounds.cljc b/common/src/app/common/geom/shapes/flex_layout/bounds.cljc new file mode 100644 index 000000000..e8cd52ce2 --- /dev/null +++ b/common/src/app/common/geom/shapes/flex_layout/bounds.cljc @@ -0,0 +1,113 @@ +;; 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.common.geom.shapes.flex-layout.bounds + (:require + [app.common.geom.point :as gpt] + [app.common.geom.shapes.common :as gco] + [app.common.geom.shapes.points :as gpo] + [app.common.geom.shapes.rect :as gre] + [app.common.math :as mth] + [app.common.types.shape.layout :as ctl])) + +(defn- child-layout-bound-points + "Returns the bounds of the children as points" + [parent child] + + (let [row? (ctl/row? parent) + col? (ctl/col? parent) + + hv (partial gpo/start-hv (:points parent)) + vv (partial gpo/start-vv (:points parent)) + + v-start? (ctl/v-start? parent) + v-center? (ctl/v-center? parent) + v-end? (ctl/v-end? parent) + h-start? (ctl/h-start? parent) + h-center? (ctl/h-center? parent) + h-end? (ctl/h-end? parent) + + base-p (first (:points child)) + + width (-> child :selrect :width) + height (-> child :selrect :height) + + min-width (if (ctl/fill-width? child) + (ctl/child-min-width child) + width) + + min-height (if (ctl/fill-height? child) + (ctl/child-min-height child) + height) + + ;; This is the leftmost (when row) or topmost (when col) point + ;; Will be added always to the bounds and then calculated the other limits + ;; from there + base-p (cond-> base-p + (and row? v-center?) + (gpt/add (vv (/ height 2))) + + (and row? v-end?) + (gpt/add (vv height)) + + (and col? h-center?) + (gpt/add (hv (/ width 2))) + + (and col? h-end?) + (gpt/add (hv width)))] + + (cond-> [base-p] + (and (mth/almost-zero? min-width) (mth/almost-zero? min-height)) + (conj (cond-> base-p + row? + (gpt/add (hv width)) + + col? + (gpt/add (vv height)))) + + (not (mth/almost-zero? min-width)) + (conj (cond-> base-p + (or row? h-start?) + (gpt/add (hv min-width)) + + (and col? h-center?) + (gpt/add (hv (/ min-width 2))) + + (and col? h-center?) + (gpt/subtract (hv min-width)))) + + (not (mth/almost-zero? min-height)) + (conj (cond-> base-p + (or col? v-start?) + (gpt/add (vv min-height)) + + (and row? v-center?) + (gpt/add (vv (/ min-height 2))) + + (and row? v-end?) + (gpt/subtract (vv min-height))))))) + +(defn layout-content-bounds + [{:keys [layout-padding] :as parent} children] + + (let [{pad-top :p1 pad-right :p2 pad-bottom :p3 pad-left :p4} layout-padding + pad-top (or pad-top 0) + pad-right (or pad-right 0) + pad-bottom (or pad-bottom 0) + pad-left (or pad-left 0) + + child-bounds + (fn [{:keys [points] :as child}] + (if (or (ctl/fill-height? child) (ctl/fill-height? child)) + (child-layout-bound-points parent child) + points))] + + (as-> children $ + (mapcat child-bounds $) + (gco/transform-points $ (gco/center-shape parent) (:transform-inverse parent)) + (gre/squared-points $) + (gpo/pad-points $ (- pad-top) (- pad-right) (- pad-bottom) (- pad-left)) + (gre/points->rect $)))) diff --git a/common/src/app/common/geom/shapes/flex_layout/lines.cljc b/common/src/app/common/geom/shapes/flex_layout/lines.cljc index c05a0cf52..e26894fa6 100644 --- a/common/src/app/common/geom/shapes/flex_layout/lines.cljc +++ b/common/src/app/common/geom/shapes/flex_layout/lines.cljc @@ -34,10 +34,13 @@ "Calculates the lines basic data and accumulated values. The positions will be calculated in a different operation" [shape children layout-bounds] - (let [wrap? (ctl/wrap? shape) - col? (ctl/col? shape) + (let [col? (ctl/col? shape) row? (ctl/row? shape) + wrap? (and (ctl/wrap? shape) + (or col? (not (ctl/auto-width? shape))) + (or row? (not (ctl/auto-height? shape)))) + [layout-gap-row layout-gap-col] (ctl/gaps shape) layout-width (gpo/width-points layout-bounds) layout-height (gpo/height-points layout-bounds) @@ -278,6 +281,12 @@ (update line-data :children-data (fn [children-data] (cond->> children-data + row? + (map #(assoc % :child-width (:child-min-width %))) + + col? + (map #(assoc % :child-height (:child-min-height %))) + row? (distribute-space :child-width :child-min-width :child-max-width line-min-width line-width) @@ -304,3 +313,4 @@ {:layout-lines layout-lines :reverse? reverse?})) + diff --git a/common/src/app/common/geom/shapes/flex_layout/modifiers.cljc b/common/src/app/common/geom/shapes/flex_layout/modifiers.cljc index 02e778550..fc0b60683 100644 --- a/common/src/app/common/geom/shapes/flex_layout/modifiers.cljc +++ b/common/src/app/common/geom/shapes/flex_layout/modifiers.cljc @@ -13,10 +13,9 @@ [app.common.types.modifiers :as ctm] [app.common.types.shape.layout :as ctl])) - (defn normalize-child-modifiers "Apply the modifiers and then normalized them against the parent coordinates" - [parent child modifiers {:keys [transform transform-inverse] :as transformed-parent}] + [modifiers parent child {:keys [transform transform-inverse] :as transformed-parent}] (let [transformed-child (gst/transform-shape child modifiers) child-bb-before (gst/parent-coords-rect child parent) @@ -38,14 +37,16 @@ {:keys [children-data line-width] :as layout-data}] (cond - (and (ctl/row? parent) (ctl/fill-width? child)) - (let [target-width (get-in children-data [(:id child) :child-width]) + (ctl/row? parent) + (let [target-width (max (get-in children-data [(:id child) :child-width]) 0.01) fill-scale (/ target-width child-width)] {:width target-width :modifiers (ctm/resize (gpt/point fill-scale 1) child-origin transform transform-inverse)}) - (and (ctl/col? parent) (ctl/fill-width? child)) - (let [target-width (- line-width (ctl/child-width-margin child)) + (ctl/col? parent) + (let [target-width (max (- line-width (ctl/child-width-margin child)) 0.01) + max-width (ctl/child-max-width child) + target-width (min max-width target-width) fill-scale (/ target-width child-width)] {:width target-width :modifiers (ctm/resize (gpt/point fill-scale 1) child-origin transform transform-inverse)}))) @@ -58,19 +59,21 @@ {:keys [children-data line-height] :as layout-data}] (cond - (and (ctl/col? parent) (ctl/fill-height? child)) - (let [target-height (get-in children-data [(:id child) :child-height]) + (ctl/col? parent) + (let [target-height (max (get-in children-data [(:id child) :child-height]) 0.01) fill-scale (/ target-height child-height)] {:height target-height :modifiers (ctm/resize (gpt/point 1 fill-scale) child-origin transform transform-inverse)}) - (and (ctl/row? parent) (ctl/fill-height? child)) - (let [target-height (- line-height (ctl/child-height-margin child)) + (ctl/row? parent) + (let [target-height (max (- line-height (ctl/child-height-margin child)) 0.01) + max-height (ctl/child-max-height child) + target-height (min max-height target-height) fill-scale (/ target-height child-height)] {:height target-height :modifiers (ctm/resize (gpt/point 1 fill-scale) child-origin transform transform-inverse)}))) -(defn calc-layout-modifiers +(defn layout-child-modifiers "Calculates the modifiers for the layout" [parent child layout-line] (let [child-bounds (gst/parent-coords-points child parent) @@ -79,8 +82,8 @@ child-width (gpo/width-points child-bounds) child-height (gpo/height-points child-bounds) - fill-width (calc-fill-width-data parent child child-origin child-width layout-line) - fill-height (calc-fill-height-data parent child child-origin child-height layout-line) + fill-width (when (ctl/fill-width? child) (calc-fill-width-data parent child child-origin child-width layout-line)) + fill-height (when (ctl/fill-height? child) (calc-fill-height-data parent child child-origin child-height layout-line)) child-width (or (:width fill-width) child-width) child-height (or (:height fill-height) child-height) diff --git a/common/src/app/common/geom/shapes/modifiers.cljc b/common/src/app/common/geom/shapes/modifiers.cljc index 31b86a297..9f7b9ae5c 100644 --- a/common/src/app/common/geom/shapes/modifiers.cljc +++ b/common/src/app/common/geom/shapes/modifiers.cljc @@ -7,27 +7,42 @@ (ns app.common.geom.shapes.modifiers (:require [app.common.data :as d] + [app.common.data.macros :as dm] + [app.common.geom.point :as gpt] [app.common.geom.shapes.constraints :as gct] [app.common.geom.shapes.flex-layout :as gcl] [app.common.geom.shapes.pixel-precision :as gpp] [app.common.geom.shapes.transforms :as gtr] [app.common.pages.helpers :as cph] [app.common.types.modifiers :as ctm] + [app.common.types.shape.layout :as ctl] [app.common.uuid :as uuid])) +;;#?(:cljs +;; (defn modif->js +;; [modif-tree objects] +;; (clj->js (into {} +;; (map (fn [[k v]] +;; [(get-in objects [k :name]) v])) +;; modif-tree)))) + + (defn set-children-modifiers [modif-tree objects parent ignore-constraints snap-pixel?] - (letfn [(set-child [transformed-parent _snap-pixel? modif-tree child] - (let [modifiers (get-in modif-tree [(:id parent) :modifiers]) - child-modifiers (gct/calc-child-modifiers parent child modifiers ignore-constraints transformed-parent) - child-modifiers (cond-> child-modifiers snap-pixel? (gpp/set-pixel-precision child))] - (cond-> modif-tree - (not (ctm/empty-modifiers? child-modifiers)) - (update-in [(:id child) :modifiers] ctm/add-modifiers child-modifiers))))] - (let [children (map (d/getf objects) (:shapes parent)) - modifiers (get-in modif-tree [(:id parent) :modifiers]) - transformed-parent (gtr/transform-shape parent modifiers)] - (reduce (partial set-child transformed-parent snap-pixel?) modif-tree children)))) + (let [children (map (d/getf objects) (:shapes parent)) + modifiers (get-in modif-tree [(:id parent) :modifiers]) + transformed-parent (gtr/transform-shape parent modifiers) + parent (gtr/transform-shape parent (ctm/select-parent-modifiers modifiers)) + + set-child + (fn [modif-tree child] + (let [child-modifiers (gct/calc-child-modifiers parent child modifiers ignore-constraints transformed-parent) + child-modifiers (cond-> child-modifiers snap-pixel? (gpp/set-pixel-precision child))] + (cond-> modif-tree + (not (ctm/empty-modifiers? child-modifiers)) + (update-in [(:id child) :modifiers] ctm/add-modifiers child-modifiers))))] + + (reduce set-child modif-tree children))) (defn group? [shape] (or (= :group (:type shape)) @@ -36,17 +51,15 @@ (defn frame? [shape] (= :frame (:type shape))) -(defn layout? [shape] - (and (frame? shape) - (:layout shape))) - (defn set-layout-modifiers ;; TODO LAYOUT: SNAP PIXEL! [modif-tree objects parent _snap-pixel?] - (letfn [(normalize-child [transformed-parent _snap-pixel? modif-tree child] + (letfn [(process-child [transformed-parent _snap-pixel? modif-tree child] (let [modifiers (get-in modif-tree [(:id parent) :modifiers]) - child-modifiers (gcl/normalize-child-modifiers parent child modifiers transformed-parent)] + child-modifiers (-> modifiers + (ctm/select-child-geometry-modifiers) + (gcl/normalize-child-modifiers parent child transformed-parent))] (cond-> modif-tree (not (ctm/empty-modifiers? child-modifiers)) (update-in [(:id child) :modifiers] ctm/add-modifiers child-modifiers)))) @@ -60,9 +73,9 @@ (and (some? modifiers) (group? child)) (gtr/apply-group-modifiers objects modif-tree)))) - (set-layout-modifiers [parent [layout-line modif-tree] child] + (set-child-modifiers [parent [layout-line modif-tree] child] (let [[modifiers layout-line] - (gcl/calc-layout-modifiers parent child layout-line) + (gcl/layout-child-modifiers parent child layout-line) modif-tree (cond-> modif-tree @@ -75,8 +88,9 @@ transformed-parent (gtr/transform-shape parent modifiers) children (map (d/getf objects) (:shapes transformed-parent)) - modif-tree (reduce (partial normalize-child transformed-parent _snap-pixel?) modif-tree children) + modif-tree (reduce (partial process-child transformed-parent _snap-pixel?) modif-tree children) children (->> children (map (partial apply-modifiers modif-tree))) + layout-data (gcl/calc-layout-data transformed-parent children) children (into [] (cond-> children (:reverse? layout-data) reverse)) max-idx (dec (count children)) @@ -92,11 +106,56 @@ children (subvec children from-idx to-idx) [_ modif-tree] - (reduce (partial set-layout-modifiers transformed-parent) [layout-line modif-tree] children)] + (reduce (partial set-child-modifiers transformed-parent) [layout-line modif-tree] children)] (recur modif-tree (first pending) (rest pending) to-idx)) modif-tree))))) +(defn set-auto-modifiers + [modif-tree objects parent] + (letfn [(apply-modifiers [child] + (let [modifiers (get-in modif-tree [(:id child) :modifiers])] + (cond-> child + (some? modifiers) + (gtr/transform-shape modifiers) + + (and (some? modifiers) (group? child)) + (gtr/apply-group-modifiers objects modif-tree)))) + + (set-parent-auto-width + [modifiers parent auto-width] + (let [origin (-> parent :points first) + scale-width (/ auto-width (-> parent :selrect :width) )] + (-> modifiers + (ctm/set-resize-parent (gpt/point scale-width 1) origin (:transform parent) (:transform-inverse parent))))) + + (set-parent-auto-height + [modifiers parent auto-height] + (let [origin (-> parent :points first) + scale-height (/ auto-height (-> parent :selrect :height) )] + (-> modifiers + (ctm/set-resize-parent (gpt/point 1 scale-height) origin (:transform parent) (:transform-inverse parent)))))] + + (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)) + + modifiers + (cond-> modifiers + (and (some? auto-width) (ctl/auto-width? parent)) + (set-parent-auto-width transformed-parent auto-width) + + (and (some? auto-height) (ctl/auto-height? parent)) + (set-parent-auto-height transformed-parent auto-height))] + + (assoc-in modif-tree [(:id parent) :modifiers] modifiers)))) + (defn get-tree-root [id objects] @@ -109,7 +168,9 @@ result ;; Frame found, but not layout we return the last layout found (or the id) - (and (= :frame (:type parent)) + (and (and (= :frame (:type parent)) + (not (ctl/auto-width? parent)) + (not (ctl/auto-height? parent))) (not (:layout parent))) result @@ -124,7 +185,9 @@ (defn resolve-tree-sequence "Given the ids that have changed search for layout roots to recalculate" - [modif-tree objects] + [ids objects] + + (assert (or (nil? ids) (set? ids)) (dm/str "tree sequence from not set: " ids)) (let [redfn (fn [result id] @@ -152,10 +215,10 @@ (map #(get objects %)))) - roots (->> modif-tree keys (reduce redfn #{}))] + roots (->> ids (reduce redfn #{}))] (concat - (when (contains? modif-tree uuid/zero) [(get objects uuid/zero)]) + (when (contains? ids uuid/zero) [(get objects uuid/zero)]) (mapcat generate-tree roots)))) (defn inside-layout? @@ -173,48 +236,55 @@ :else (recur (:parent-id current)))))) -;;#?(:cljs -;; (defn modif->js -;; [modif-tree objects] -;; (clj->js (into {} -;; (map (fn [[k v]] -;; [(get-in objects [k :name]) v])) -;; modif-tree)))) +(defn- calculate-modifiers + ([objects snap-pixel? ignore-constraints [modif-tree recalculate] shape] + (calculate-modifiers objects snap-pixel? ignore-constraints false [modif-tree recalculate] shape)) + + ([objects snap-pixel? ignore-constraints ignore-auto? [modif-tree recalculate] shape] + (let [shape-id (:id shape) + root? (= uuid/zero shape-id) + modifiers (get-in modif-tree [shape-id :modifiers]) + + modifiers (cond-> modifiers + (and (not root?) (ctm/has-geometry? modifiers) snap-pixel?) + (gpp/set-pixel-precision shape)) + + modif-tree (-> modif-tree (assoc-in [shape-id :modifiers] modifiers)) + + has-modifiers? (ctm/child-modifiers? modifiers) + is-layout? (ctl/layout? shape) + is-auto? (or (ctl/auto-height? shape) (ctl/auto-width? shape)) + is-parent? (or (group? shape) (and (frame? shape) (not (ctl/layout? shape)))) + + ;; If the current child is inside the layout we ignore the constraints + is-inside-layout? (inside-layout? objects shape)] + + [(cond-> modif-tree + (and has-modifiers? is-parent? (not root?)) + (set-children-modifiers objects shape (or ignore-constraints is-inside-layout?) snap-pixel?) + + is-layout? + (set-layout-modifiers objects shape snap-pixel?) + + (and (not ignore-auto?) is-auto?) + (set-auto-modifiers objects shape)) + + (cond-> recalculate + ;; Auto-width/height can change the positions in the parent so we need to recalculate + (and (not ignore-auto?) is-auto?) + (conj (:id shape)))]))) (defn set-objects-modifiers [modif-tree objects ignore-constraints snap-pixel?] - (let [shapes-tree (resolve-tree-sequence modif-tree objects) + (let [shapes-tree (resolve-tree-sequence (-> modif-tree keys set) objects) - modif-tree - (->> shapes-tree - (reduce - (fn [modif-tree shape] - - (let [root? (= uuid/zero (:id shape)) + [modif-tree recalculate] + (reduce (partial calculate-modifiers objects snap-pixel? ignore-constraints) [modif-tree #{}] shapes-tree) - modifiers (get-in modif-tree [(:id shape) :modifiers]) - modifiers (cond-> modifiers - (and (not root?) (ctm/has-geometry? modifiers) snap-pixel?) - (gpp/set-pixel-precision shape)) - - modif-tree (-> modif-tree (assoc-in [(:id shape) :modifiers] modifiers)) - - has-modifiers? (ctm/child-modifiers? modifiers) - is-layout? (layout? shape) - is-parent? (or (group? shape) (and (frame? shape) (not (layout? shape)))) - - ;; If the current child is inside the layout we ignore the constraints - is-inside-layout? (inside-layout? objects shape)] - - (cond-> modif-tree - (and has-modifiers? is-parent? (not root?)) - (set-children-modifiers objects shape (or ignore-constraints is-inside-layout?) snap-pixel?) - - is-layout? - (set-layout-modifiers objects shape snap-pixel?)))) - - modif-tree))] + shapes-tree (resolve-tree-sequence recalculate objects) + [modif-tree _] + (reduce (partial calculate-modifiers objects snap-pixel? ignore-constraints true) [modif-tree #{}] shapes-tree)] ;;#?(:cljs ;; (.log js/console ">result" (modif->js modif-tree objects))) diff --git a/common/src/app/common/geom/shapes/points.cljc b/common/src/app/common/geom/shapes/points.cljc index 1f80f28df..adae25ace 100644 --- a/common/src/app/common/geom/shapes/points.cljc +++ b/common/src/app/common/geom/shapes/points.cljc @@ -50,12 +50,13 @@ (defn pad-points [[p0 p1 p2 p3 :as points] pad-top pad-right pad-bottom pad-left] - (let [top-v (start-vv points pad-top) - right-v (end-hv points pad-right) - bottom-v (end-vv points pad-bottom) - left-v (start-hv points pad-left)] + (when (some? points) + (let [top-v (start-vv points pad-top) + right-v (end-hv points pad-right) + bottom-v (end-vv points pad-bottom) + left-v (start-hv points pad-left)] - [(-> p0 (gpt/add left-v) (gpt/add top-v)) - (-> p1 (gpt/add right-v) (gpt/add top-v)) - (-> p2 (gpt/add right-v) (gpt/add bottom-v)) - (-> p3 (gpt/add left-v) (gpt/add bottom-v))])) + [(-> p0 (gpt/add left-v) (gpt/add top-v)) + (-> p1 (gpt/add right-v) (gpt/add top-v)) + (-> p2 (gpt/add right-v) (gpt/add bottom-v)) + (-> p3 (gpt/add left-v) (gpt/add bottom-v))]))) diff --git a/common/src/app/common/geom/shapes/rect.cljc b/common/src/app/common/geom/shapes/rect.cljc index 895388371..d523f8321 100644 --- a/common/src/app/common/geom/shapes/rect.cljc +++ b/common/src/app/common/geom/shapes/rect.cljc @@ -87,6 +87,19 @@ (when (d/num? minx miny maxx maxy) (make-rect minx miny (- maxx minx) (- maxy miny)))))) +(defn squared-points + [points] + (when (d/not-empty? points) + (let [minx (transduce (keep :x) min ##Inf points) + miny (transduce (keep :y) min ##Inf points) + maxx (transduce (keep :x) max ##-Inf points) + maxy (transduce (keep :y) max ##-Inf points)] + (when (d/num? minx miny maxx maxy) + [(gpt/point minx miny) + (gpt/point maxx miny) + (gpt/point maxx maxy) + (gpt/point minx maxy)])))) + (defn points->selrect [points] (when-let [rect (points->rect points)] (let [{:keys [x y width height]} rect] diff --git a/common/src/app/common/geom/shapes/transforms.cljc b/common/src/app/common/geom/shapes/transforms.cljc index ce888ddf1..bdaa87807 100644 --- a/common/src/app/common/geom/shapes/transforms.cljc +++ b/common/src/app/common/geom/shapes/transforms.cljc @@ -434,20 +434,35 @@ (map (comp gpr/points->selrect :points transform-shape)) (gpr/join-selrects))) +(defn apply-children-modifiers + [objects modif-tree children] + (->> children + (map (fn [child] + (let [modifiers (get-in modif-tree [(:id child) :modifiers]) + child (transform-shape child modifiers) + parent? (or (= :group (:type child)) (= :bool (:type child)))] + (cond-> child + parent? + (apply-children-modifiers objects modif-tree))))))) + (defn apply-group-modifiers "Apply the modifiers to the group children to calculate its selection rect" - [group objects modif-tree] + [parent objects modif-tree] + (let [children (->> (:shapes parent) + (map (d/getf objects)) + (apply-children-modifiers objects modif-tree))] + (cond-> parent + (= :group (:type parent)) + (update-group-selrect children)))) + +(defn get-children-bounds + [parent objects modif-tree] (let [children - (->> (:shapes group) + (->> (:shapes parent) (map (d/getf objects)) - (map (fn [shape] - (let [modifiers (get modif-tree (:id shape)) - shape (-> shape (merge modifiers) transform-shape)] - (if (= :group (:type shape)) - (apply-group-modifiers shape objects modif-tree) - shape)))))] - (update-group-selrect group children))) + (apply-children-modifiers objects modif-tree))] + (->> children (mapcat :points) gpr/points->rect))) (defn parent-coords-rect [child parent] diff --git a/common/src/app/common/types/modifiers.cljc b/common/src/app/common/types/modifiers.cljc index fe308d402..a99c5b893 100644 --- a/common/src/app/common/types/modifiers.cljc +++ b/common/src/app/common/types/modifiers.cljc @@ -17,7 +17,11 @@ ;; --- Modifiers ;; Moodifiers types -;; - geometry: Geometry +;; - geometry-parent: Geometry non-recursive +;; * move +;; * resize +;; * rotation +;; - geometry-child: Geometry recursive ;; * move ;; * resize ;; * rotation @@ -28,7 +32,6 @@ ;; - structure-child: Structure recursive ;; * scale-content ;; * rotation -;; (def conjv (fnil conj [])) @@ -37,37 +40,59 @@ (defn empty-modifiers [] {}) +(defn set-move-parent + ([modifiers x y] + (set-move-parent modifiers (gpt/point x y))) + + ([modifiers vector] + (-> modifiers + (update :geometry-parent conjv {:type :move :vector vector})))) + +(defn set-resize-parent + ([modifiers vector origin] + (-> modifiers + (update :geometry-parent conjv {:type :resize + :vector vector + :origin origin}))) + + ([modifiers vector origin transform transform-inverse] + (-> modifiers + (update :geometry-parent conjv {:type :resize + :vector vector + :origin origin + :transform transform + :transform-inverse transform-inverse})))) (defn set-move ([modifiers x y] (set-move modifiers (gpt/point x y))) ([modifiers vector] (-> modifiers - (update :geometry conjv {:type :move :vector vector})))) + (update :geometry-child conjv {:type :move :vector vector})))) (defn set-resize ([modifiers vector origin] (-> modifiers - (update :geometry conjv {:type :resize - :vector vector - :origin origin}))) + (update :geometry-child conjv {:type :resize + :vector vector + :origin origin}))) ([modifiers vector origin transform transform-inverse] (-> modifiers - (update :geometry conjv {:type :resize - :vector vector - :origin origin - :transform transform - :transform-inverse transform-inverse})))) + (update :geometry-child conjv {:type :resize + :vector vector + :origin origin + :transform transform + :transform-inverse transform-inverse})))) (defn set-rotation [modifiers center angle] (-> modifiers (update :structure-child conjv {:type :rotation :rotation angle}) - (update :geometry conjv {:type :rotation - :center center - :rotation angle}))) + (update :geometry-child conjv {:type :rotation + :center center + :rotation angle}))) (defn set-remove-children [modifiers shapes] @@ -97,8 +122,11 @@ [modifiers new-modifiers] (cond-> modifiers - (some? (:geometry new-modifiers)) - (update :geometry #(d/concat-vec [] % (:geometry new-modifiers))) + (some? (:geometry-child new-modifiers)) + (update :geometry-child #(d/concat-vec [] % (:geometry-child new-modifiers))) + + (some? (:geometry-parent new-modifiers)) + (update :geometry-parent #(d/concat-vec [] % (:geometry-parent new-modifiers))) (some? (:structure-parent new-modifiers)) (update :structure-parent #(d/concat-vec [] % (:structure-parent new-modifiers))) @@ -116,6 +144,13 @@ ([vector] (set-move (empty-modifiers) vector))) +(defn move-parent + ([x y] + (set-move-parent (empty-modifiers) (gpt/point x y))) + + ([vector] + (set-move-parent (empty-modifiers) vector))) + (defn resize ([vector origin] (set-resize (empty-modifiers) vector origin)) @@ -123,6 +158,13 @@ ([vector origin transform transform-inverse] (set-resize (empty-modifiers) vector origin transform transform-inverse))) +(defn resize-parent + ([vector origin] + (set-resize-parent (empty-modifiers) vector origin)) + + ([vector origin transform transform-inverse] + (set-resize-parent (empty-modifiers) vector origin transform transform-inverse))) + (defn rotation [shape center angle] (let [shape-center (gco/center-shape shape) @@ -155,32 +197,30 @@ (set-scale-content value))) (defn child-modifiers? - [{:keys [geometry structure-child]}] - (or (d/not-empty? geometry) + [{:keys [geometry-child structure-child]}] + (or (d/not-empty? geometry-child) (d/not-empty? structure-child))) (defn select-child-modifiers [modifiers] - (select-keys modifiers [:geometry :structure-child])) + (select-keys modifiers [:geometry-child :structure-child])) + +(defn select-child-geometry-modifiers + [modifiers] + (select-keys modifiers [:geometry-child])) + +(defn select-parent-modifiers + [modifiers] + (select-keys modifiers [:geometry-parent :structure-parent])) (defn select-structure [modifiers] (select-keys modifiers [:structure-parent])) -(defn add-move - ([object x y] - (add-move object (gpt/point x y))) - - ([object vector] - (update object :modifiers (move vector)))) - -(defn add-resize - [object vector origin] - (update object :modifiers (resize vector origin))) - (defn empty-modifiers? [modifiers] - (and (empty? (:geometry modifiers)) + (and (empty? (:geometry-child modifiers)) + (empty? (:geometry-parent modifiers)) (empty? (:structure-parent modifiers)) (empty? (:structure-child modifiers)))) @@ -253,8 +293,10 @@ (defn only-move? [modifier] - (and (= 1 (-> modifier :geometry count)) - (= :move (-> modifier :geometry first :type)))) + (or (and (= 1 (-> modifier :geometry-child count)) + (= :move (-> modifier :geometry-child first :type))) + (and (= 1 (-> modifier :geometry-parent count)) + (= :move (-> modifier :geometry-parent first :type))))) (defn get-frame-add-children [modif-tree] @@ -300,8 +342,11 @@ (gmt/multiply (gmt/rotate-matrix rotation)) (gmt/translate (gpt/negate center))) matrix)))] - (->> modifiers :geometry - (reduce apply-modifier (gmt/matrix))))) + (let [modifiers (if (d/not-empty? (:geometry-parent modifiers)) + (d/concat-vec (:geometry-parent modifiers) (:geometry-child modifiers)) + (:geometry-child modifiers))] + (->> modifiers + (reduce apply-modifier (gmt/matrix)))))) (defn scale-text-content [content value] @@ -357,5 +402,6 @@ (reduce apply-modifier $ (:structure-child modifiers))))) (defn has-geometry? - [{:keys [geometry]}] - (d/not-empty? geometry)) + [{:keys [geometry-parent geometry-child]}] + (or (d/not-empty? geometry-parent) + (d/not-empty? geometry-child))) diff --git a/common/src/app/common/types/shape/layout.cljc b/common/src/app/common/types/shape/layout.cljc index 53aa8fc6e..8ab1f67c4 100644 --- a/common/src/app/common/types/shape/layout.cljc +++ b/common/src/app/common/types/shape/layout.cljc @@ -109,6 +109,12 @@ (defn fill-height? [child] (= :fill (:layout-item-v-sizing child))) +(defn auto-width? [child] + (= :auto (:layout-item-h-sizing child))) + +(defn auto-height? [child] + (= :auto (:layout-item-v-sizing child))) + (defn col? [{:keys [layout-flex-dir]}] (or (= :column layout-flex-dir) (= :reverse-column layout-flex-dir))) diff --git a/common/test/common_tests/geom_shapes_test.cljc b/common/test/common_tests/geom_shapes_test.cljc index e36a487e7..eae43ef1b 100644 --- a/common/test/common_tests/geom_shapes_test.cljc +++ b/common/test/common_tests/geom_shapes_test.cljc @@ -142,8 +142,6 @@ shape-before (assoc shape-before :modifiers modifiers) shape-after (gsh/transform-shape shape-before)] - (t/is (not= (:selrect shape-before) (:selrect shape-after))) - (t/is (close? (get-in shape-before [:selrect :x]) (get-in shape-after [:selrect :x]))) diff --git a/frontend/src/app/main/data/workspace/shape_layout.cljs b/frontend/src/app/main/data/workspace/shape_layout.cljs index 9ab3536bc..956d24e1e 100644 --- a/frontend/src/app/main/data/workspace/shape_layout.cljs +++ b/frontend/src/app/main/data/workspace/shape_layout.cljs @@ -9,6 +9,7 @@ [app.common.data :as d] [app.common.pages.helpers :as cph] [app.common.types.modifiers :as ctm] + [app.common.types.shape.layout :as ctl] [app.main.data.workspace.changes :as dwc] [app.main.data.workspace.state-helpers :as wsh] [app.main.data.workspace.transforms :as dwt] @@ -68,7 +69,6 @@ (rx/of (dwc/update-shapes ids #(merge % initial-grid-layout)) (update-layout-positions ids)))))) - (defn remove-layout [ids] (ptk/reify ::remove-layout @@ -91,6 +91,7 @@ ptk/WatchEvent (watch [_ state _] (let [objects (wsh/lookup-page-objects state) - parent-ids (->> ids (map #(cph/get-parent-id objects %)))] + parent-ids (->> ids (map #(cph/get-parent-id objects %))) + layout-ids (->> ids (filter (comp ctl/layout? (d/getf objects))))] (rx/of (dwc/update-shapes ids #(d/deep-merge (or % {}) changes)) - (update-layout-positions parent-ids)))))) + (update-layout-positions (d/concat-vec layout-ids parent-ids))))))) diff --git a/frontend/src/app/main/refs.cljs b/frontend/src/app/main/refs.cljs index 9c5f35f7c..fe9b1ca48 100644 --- a/frontend/src/app/main/refs.cljs +++ b/frontend/src/app/main/refs.cljs @@ -11,6 +11,7 @@ [app.common.data.macros :as dm] [app.common.pages.helpers :as cph] [app.common.types.shape-tree :as ctt] + [app.common.types.shape.layout :as ctl] [app.main.data.workspace.state-helpers :as wsh] [app.main.store :as st] [okulary.core :as l])) @@ -442,7 +443,7 @@ (l/derived (fn [objects] (->> ids - (some #(-> (cph/get-parent objects %) :layout)))) + (some #(-> (cph/get-parent objects %) ctl/layout?)))) workspace-page-objects)) (defn get-flex-child-viewer? diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs index c36017017..97aff0879 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs @@ -80,7 +80,7 @@ (mf/defc element-behavior [{:keys [is-layout-container? is-layout-child? layout-item-h-sizing layout-item-v-sizing on-change-behavior] :as props}] (let [fill? is-layout-child? - auto? is-layout-container?] + auto? is-layout-container?] [:div.btn-wrapper [:div.layout-behavior.horizontal @@ -98,7 +98,7 @@ (when auto? [:button.behavior-btn.tooltip.tooltip-bottom {:alt "Fit content" - :class (dom/classnames :active (= layout-item-v-sizing :auto)) + :class (dom/classnames :active (= layout-item-h-sizing :auto)) :on-click #(on-change-behavior :h :auto)} i/auto-hug])] @@ -188,9 +188,11 @@ :on-change-behavior on-change-behavior}]] - [:& margin-section {:values values - :change-margin-style change-margin-style - :on-margin-change on-margin-change}] + (when is-layout-child? + [:& margin-section {:values values + :change-margin-style change-margin-style + :on-margin-change on-margin-change}]) + [:div.advanced-ops-container [:button.advanced-ops.toltip.tooltip-bottom {:on-click toggle-open @@ -200,12 +202,13 @@ (when @open? [:div.advanced-ops-body - [:div.layout-row - [:div.direction-wrap.row-title "Align"] - [:div.btn-wrapper - [:& align-self-row {:is-col? is-col? - :align-self align-self - :set-align-self set-align-self}]]] + (when is-layout-child? + [:div.layout-row + [:div.direction-wrap.row-title "Align"] + [:div.btn-wrapper + [:& align-self-row {:is-col? is-col? + :align-self align-self + :set-align-self set-align-self}]]]) [:div.input-wrapper (for [item [:layout-item-max-h :layout-item-min-h :layout-item-max-w :layout-item-min-w]] [:div.tooltip.tooltip-bottom diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/frame.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/frame.cljs index 048fff5c0..219518ec1 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/frame.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/frame.cljs @@ -6,6 +6,7 @@ (ns app.main.ui.workspace.sidebar.options.shapes.frame (:require + [app.common.types.shape.layout :as ctl] [app.main.features :as features] [app.main.refs :as refs] [app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]] @@ -35,7 +36,8 @@ layout-item-values (select-keys shape layout-item-attrs) is-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids)) - is-layout-child? (mf/deref is-layout-child-ref)] + is-layout-child? (mf/deref is-layout-child-ref) + is-layout-container? (ctl/layout? shape)] [:* [:& measures-menu {:ids [(:id shape)] :values measure-values @@ -43,18 +45,17 @@ :shape shape}] [:& constraints-menu {:ids ids :values constraint-values}] - (when layout-active? - [:* - [:& layout-container-menu {:type type :ids [(:id shape)] :values layout-container-values}] + (when (or layout-active? is-layout-container?) + [:& layout-container-menu {:type type :ids [(:id shape)] :values layout-container-values}]) - (when is-layout-child? - [:& layout-item-menu - {:ids ids - :type type - :values layout-item-values - :is-layout-child? is-layout-child? - :is-layout-container? (:layout shape) - :shape shape}])]) + (when (and layout-active? (or is-layout-child? is-layout-container?)) + [:& layout-item-menu + {:ids ids + :type type + :values layout-item-values + :is-layout-child? is-layout-child? + :is-layout-container? is-layout-container? + :shape shape}]) [:& layer-menu {:ids ids :type type