diff --git a/common/src/app/common/geom/shapes/grid_layout.cljc b/common/src/app/common/geom/shapes/grid_layout.cljc index 8483b8dde9..5de5a5ce32 100644 --- a/common/src/app/common/geom/shapes/grid_layout.cljc +++ b/common/src/app/common/geom/shapes/grid_layout.cljc @@ -7,6 +7,7 @@ (ns app.common.geom.shapes.grid-layout (:require [app.common.data.macros :as dm] + [app.common.geom.shapes.grid-layout.bounds :as glpb] [app.common.geom.shapes.grid-layout.layout-data :as glld] [app.common.geom.shapes.grid-layout.positions :as glp])) @@ -16,3 +17,5 @@ (dm/export glp/get-position-grid-coord) (dm/export glp/get-drop-cell) (dm/export glp/cell-bounds) +(dm/export glpb/layout-content-points) +(dm/export glpb/layout-content-bounds) diff --git a/common/src/app/common/geom/shapes/grid_layout/bounds.cljc b/common/src/app/common/geom/shapes/grid_layout/bounds.cljc new file mode 100644 index 0000000000..57ee718682 --- /dev/null +++ b/common/src/app/common/geom/shapes/grid_layout/bounds.cljc @@ -0,0 +1,53 @@ +;; 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.grid-layout.bounds + (:require + [app.common.data :as d] + [app.common.geom.point :as gpt] + [app.common.geom.shapes.grid-layout.layout-data :as ld] + [app.common.geom.shapes.points :as gpo])) + +(defn layout-content-points + [bounds parent children] + (let [parent-id (:id parent) + parent-bounds @(get bounds parent-id) + + hv #(gpo/start-hv parent-bounds %) + vv #(gpo/start-vv parent-bounds %) + + children (->> children + (map #(vector @(get bounds (:id %)) %))) + + {:keys [row-tracks column-tracks]} (ld/calc-layout-data parent children parent-bounds)] + (d/concat-vec + (->> row-tracks + (mapcat #(vector (:start-p %) + (gpt/add (:start-p %) (vv (:size %)))))) + (->> column-tracks + (mapcat #(vector (:start-p %) + (gpt/add (:start-p %) (hv (:size %))))))))) + +(defn layout-content-bounds + [bounds {:keys [layout-padding] :as parent} children] + + (let [parent-id (:id parent) + parent-bounds @(get bounds parent-id) + + {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) + + layout-points (layout-content-points bounds parent children)] + + (if (d/not-empty? layout-points) + (-> layout-points + (gpo/merge-parent-coords-bounds parent-bounds) + (gpo/pad-points (- pad-top) (- pad-right) (- pad-bottom) (- pad-left))) + ;; Cannot create some bounds from the children so we return the parent's + parent-bounds))) diff --git a/common/src/app/common/geom/shapes/grid_layout/layout_data.cljc b/common/src/app/common/geom/shapes/grid_layout/layout_data.cljc index a377d48d33..1d035998c5 100644 --- a/common/src/app/common/geom/shapes/grid_layout/layout_data.cljc +++ b/common/src/app/common/geom/shapes/grid_layout/layout_data.cljc @@ -52,6 +52,7 @@ [app.common.data :as d] [app.common.geom.point :as gpt] [app.common.geom.shapes.points :as gpo] + [app.common.math :as mth] [app.common.types.shape.layout :as ctl])) (defn layout-bounds @@ -100,7 +101,8 @@ idx (dec (get cell prop)) track (get tracks idx)] (cond-> tracks - (and (= (get cell prop-span) 1) (= :auto (:type track))) + (and (= (get cell prop-span) 1) + (contains? #{:flex :auto} (:type track))) (update-in [idx :size] max (size-fn child-shape child-bounds))))) track-list children))) @@ -129,13 +131,68 @@ (cond-> acc (= type :auto) (inc)))] (->> track-list (reduce calc-tracks-total-autos 0)))) + (defn set-fr-value - [track-list fr-value] - (->> track-list - (mapv (fn [{:keys [type value max-size] :as track}] - (cond-> track - (= :flex type) - (assoc :size (min (* value fr-value) max-size))))))) + "Tries to assign the fr value distributing the excess between the free spaces" + [track-list fr-value auto?] + + (let [flex? #(= :flex (:type (second %))) + + ;; Fixes the assignments so they respect the min size constraint + ;; returns pending with the necessary space to allocate and free-frs + ;; are the addition of the fr tracks with free space + assign-fn + (fn [[assign-fr pending free-frs] [idx t]] + (let [fr (:value t) + current (get assign-fr idx (* fr-value fr)) + full? (<= current (:size t)) + cur-pending (if full? (- (:size t) current) 0)] + [(assoc assign-fr idx (if full? (:size t) current)) + (+ pending cur-pending) + (cond-> free-frs (not full?) (+ fr))])) + + ;; Sets the assigned-fr map removing the pending/free-frs + change-fn + (fn [delta] + (fn [assign-fr [idx t]] + (let [fr (:value t) + current (get assign-fr idx) + full? (<= current (:size t))] + (cond-> assign-fr + (not full?) + (update idx - (* delta fr)))))) + + assign-fr + (loop [assign-fr {}] + (let [[assign-fr pending free-frs] + (->> (d/enumerate track-list) + (filter flex?) + (reduce assign-fn [assign-fr 0 0]))] + + ;; When auto, we don't need to remove the excess + (if (or auto? + (= free-frs 0) + (mth/almost-zero? pending)) + assign-fr + + (let [delta (/ pending free-frs) + assign-fr + (->> (d/enumerate track-list) + (filter flex?) + (reduce (change-fn delta) assign-fr))] + + (recur assign-fr))))) + + ;; Apply assign-fr to the track-list + track-list + (reduce + (fn [track-list [idx assignment] ] + (-> track-list + (update-in [idx :size] max assignment))) + track-list + assign-fr)] + + track-list)) (defn add-auto-size [track-list add-size] @@ -305,6 +362,15 @@ track-list)] track-list)) +(defn min-fr-value + [tracks] + (loop [tracks (seq tracks) + min-fr 0.01] + (if (empty? tracks) + min-fr + (let [{:keys [size type value]} (first tracks) + min-fr (if (= type :flex) (max min-fr (/ size value)) min-fr)] + (recur (rest tracks) min-fr))))) (defn calc-layout-data [parent children transformed-parent-bounds] @@ -319,13 +385,22 @@ bound-corner (gpo/origin layout-bounds) [row-gap column-gap] (ctl/gaps parent) + auto-height? (ctl/auto-height? parent) + auto-width? (ctl/auto-width? parent) + + {:keys [layout-grid-columns layout-grid-rows layout-grid-cells]} parent + num-columns (count layout-grid-columns) + num-rows (count layout-grid-rows) + + column-total-gap (* column-gap (dec num-columns)) + row-total-gap (* row-gap (dec num-rows)) ;; Map shape->cell shape-cells (into {} (mapcat (fn [[_ cell]] (->> (:shapes cell) (map #(vector % cell))))) - (:layout-grid-cells parent)) + layout-grid-cells) children (->> children (remove #(ctl/layout-absolute? (second %)))) children-map @@ -335,11 +410,11 @@ ;; Initialize tracks column-tracks - (->> (:layout-grid-columns parent) + (->> layout-grid-columns (mapv (partial calculate-initial-track-size bound-width))) row-tracks - (->> (:layout-grid-rows parent) + (->> layout-grid-rows (mapv (partial calculate-initial-track-size bound-height))) ;; Go through cells to adjust auto sizes for span=1. Base is the max of its children @@ -351,11 +426,8 @@ row-tracks (set-auto-multi-span parent row-tracks children-map shape-cells :row) ;; Calculate the `fr` unit and adjust the size - column-total-size (tracks-total-size column-tracks) - row-total-size (tracks-total-size row-tracks) - - column-total-gap (* column-gap (dec (count column-tracks))) - row-total-gap (* row-gap (dec (count row-tracks))) + column-total-size-nofr (tracks-total-size (->> column-tracks (remove #(= :flex (:type %))))) + row-total-size-nofr (tracks-total-size (->> row-tracks (remove #(= :flex (:type %))))) column-frs (tracks-total-frs column-tracks) row-frs (tracks-total-frs row-tracks) @@ -367,22 +439,28 @@ row-tracks (set-flex-multi-span parent row-tracks children-map shape-cells :row) ;; Once auto sizes have been calculated we get calculate the `fr` unit with the remainining size and adjust the size - free-column-space (- bound-width (+ column-total-size column-total-gap)) - free-row-space (- bound-height (+ row-total-size row-total-gap)) - column-fr (/ free-column-space column-frs) - row-fr (/ free-row-space row-frs) + free-column-space (max 0 (- bound-width (+ column-total-size-nofr column-total-gap))) + free-row-space (max 0 (- bound-height (+ row-total-size-nofr row-total-gap))) - column-tracks (set-fr-value column-tracks column-fr) - row-tracks (set-fr-value row-tracks row-fr) + ;; Get the minimum values for fr's + min-column-fr (min-fr-value column-tracks) + min-row-fr (min-fr-value row-tracks) + + column-fr (if auto-width? min-column-fr (mth/finite (/ free-column-space column-frs) 0)) + row-fr (if auto-height? min-row-fr (mth/finite (/ free-row-space row-frs) 0)) + + column-tracks (set-fr-value column-tracks column-fr auto-width?) + row-tracks (set-fr-value row-tracks row-fr auto-height?) ;; Distribute free space between `auto` tracks column-total-size (tracks-total-size column-tracks) row-total-size (tracks-total-size row-tracks) - free-column-space (- bound-width (+ column-total-size column-total-gap)) - free-row-space (- bound-height (+ row-total-size row-total-gap)) + free-column-space (max 0 (if auto-width? 0 (- bound-width (+ column-total-size column-total-gap)))) + free-row-space (max 0 (if auto-height? 0 (- bound-height (+ row-total-size row-total-gap)))) column-autos (tracks-total-autos column-tracks) row-autos (tracks-total-autos row-tracks) + column-add-auto (/ free-column-space column-autos) row-add-auto (/ free-row-space row-autos) @@ -394,6 +472,9 @@ column-gap (case (:layout-align-content parent) + auto-width? + column-gap + :space-evenly (max column-gap (/ (- bound-width column-total-size) (inc (count column-tracks)))) @@ -407,6 +488,9 @@ row-gap (case (:layout-justify-content parent) + auto-height? + row-gap + :space-evenly (max row-gap (/ (- bound-height row-total-size) (inc (count row-tracks)))) @@ -420,29 +504,28 @@ start-p (cond-> bound-corner - (= :end (:layout-align-content parent)) + (and (not auto-width?) (= :end (:layout-align-content parent))) (gpt/add (hv (- bound-width (+ column-total-size column-total-gap)))) - (= :center (:layout-align-content parent)) + (and (not auto-width?) (= :center (:layout-align-content parent))) (gpt/add (hv (/ (- bound-width (+ column-total-size column-total-gap)) 2))) - (= :end (:layout-justify-content parent)) + (and (not auto-height?) (= :end (:layout-justify-content parent))) (gpt/add (vv (- bound-height (+ row-total-size row-total-gap)))) - (= :center (:layout-justify-content parent)) + (and (not auto-height?) (= :center (:layout-justify-content parent))) (gpt/add (vv (/ (- bound-height (+ row-total-size row-total-gap)) 2))) - - (= :space-around (:layout-align-content parent)) + (and (not auto-width?) (= :space-around (:layout-align-content parent))) (gpt/add (hv (/ column-gap 2))) - (= :space-evenly (:layout-align-content parent)) + (and (not auto-width?) (= :space-evenly (:layout-align-content parent))) (gpt/add (hv column-gap)) - (= :space-around (:layout-justify-content parent)) + (and (not auto-height?) (= :space-around (:layout-justify-content parent))) (gpt/add (vv (/ row-gap 2))) - (= :space-evenly (:layout-justify-content parent)) + (and (not auto-height?) (= :space-evenly (:layout-justify-content parent))) (gpt/add (vv row-gap))) column-tracks diff --git a/common/src/app/common/geom/shapes/modifiers.cljc b/common/src/app/common/geom/shapes/modifiers.cljc index 3fdb5aec00..f0eb8f320c 100644 --- a/common/src/app/common/geom/shapes/modifiers.cljc +++ b/common/src/app/common/geom/shapes/modifiers.cljc @@ -256,7 +256,12 @@ content-bounds (when (and (d/not-empty? children) (or (ctl/auto-height? parent) (ctl/auto-width? parent))) - (gcfl/layout-content-bounds bounds parent children)) + (cond + (ctl/flex-layout? parent) + (gcfl/layout-content-bounds bounds parent children) + + (ctl/grid-layout? parent) + (gcgl/layout-content-bounds bounds parent children))) auto-width (when content-bounds (gpo/width-points content-bounds)) auto-height (when content-bounds (gpo/height-points content-bounds))] diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index 2213e8f94a..3c65902aab 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -597,6 +597,6 @@ {:zoom zoom :objects base-objects :modifiers modifiers - :shape (or (get objects-modified edition) + :shape (or (get base-objects edition) (get base-objects @hover-top-frame-id)) :view-only (not show-grid-editor?)}]])]]])) diff --git a/frontend/src/app/main/ui/workspace/viewport/debug.cljs b/frontend/src/app/main/ui/workspace/viewport/debug.cljs index 7f686435a8..67116f6244 100644 --- a/frontend/src/app/main/ui/workspace/viewport/debug.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/debug.cljs @@ -40,8 +40,19 @@ (let [children (->> (cph/get-immediate-children objects (:id shape)) (remove :hidden)) bounds (d/lazy-map (keys objects) #(dm/get-in objects [% :points])) - layout-bounds (gsl/layout-content-bounds bounds shape children) - layout-points (flatten (gsl/layout-content-points bounds shape children))] + layout-bounds + (cond (ctl/flex-layout? shape) + (gsl/layout-content-bounds bounds shape children) + + (ctl/grid-layout? shape) + (gsg/layout-content-bounds bounds shape children)) + layout-points + (cond (ctl/flex-layout? shape) + (flatten (gsl/layout-content-points bounds shape children)) + + (ctl/grid-layout? shape) + (flatten (gsg/layout-content-points bounds shape children)))] + [:g.debug-layout {:pointer-events "none"} [:polygon {:points (->> layout-bounds (map #(dm/fmt "%, %" (:x %) (:y %))) (str/join " ")) :style {:stroke "red" :fill "none"}}] diff --git a/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.cljs b/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.cljs index 9afcae0e52..72dbd278de 100644 --- a/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.cljs @@ -665,11 +665,12 @@ zoom (unchecked-get props "zoom") view-only (unchecked-get props "view-only") - shape (mf/use-memo - (mf/deps modifiers base-shape) - #(gsh/transform-shape - base-shape - (dm/get-in modifiers [(:id base-shape) :modifiers]))) + shape + (mf/use-memo + (mf/deps modifiers base-shape) + #(gsh/transform-shape + base-shape + (dm/get-in modifiers [(:id base-shape) :modifiers]))) snap-pixel? (mf/deref refs/snap-pixel?)