Adds grid column/row sizing without spanned tracks

This commit is contained in:
alonso.torres 2023-04-12 16:26:40 +02:00
parent 4bfe81f771
commit 2df40ad767
5 changed files with 424 additions and 123 deletions

View file

@ -15,9 +15,8 @@
(def conjv (fnil conj []))
(defn layout-bounds
[{:keys [layout-padding] :as shape} shape-bounds]
(let [;; Add padding to the bounds
{pad-top :p1 pad-right :p2 pad-bottom :p3 pad-left :p4} layout-padding]
[parent shape-bounds]
(let [[pad-top pad-right pad-bottom pad-left] (ctl/paddings parent)]
(gpo/pad-points shape-bounds pad-top pad-right pad-bottom pad-left)))
(defn init-layout-lines

View file

@ -4,10 +4,63 @@
;;
;; Copyright (c) KALEIDOS INC
;; Each track has specified minimum and maximum sizing functions (which may be the same)
;; - Fixed
;; - Percent
;; - Auto
;; - Flex
;;
;;
;;
;; Min functions:
;; - Fixed: value
;; - Percent: value to pixels
;; - Auto: auto
;; - Flex: auto
;;
;; Max functions:
;; - Fixed: value
;; - Percent: value to pixels
;; - Auto: max-content
;; - Flex: flex
;; Algorithm
;; - Initialize tracks:
;; * base = size or 0
;; * max = size or INF
;;
;; - Resolve intrinsic sizing
;; 1. Shim baseline-aligned items so their intrinsic size contributions reflect their baseline alignment
;;
;; 2. Size tracks to fit non-spanning items
;; base-size = max (children min contribution) floored 0
;;
;; 3. Increase sizes to accommodate spanning items crossing content-sized tracks
;;
;; 4. Increase sizes to accommodate spanning items crossing flexible tracks:
;;
;; 5. If any track still has an infinite growth limit set its growth limit to its base size.
;; - Distribute extra space accross spaned tracks
;; - Maximize tracks
;;
;; - Expand flexible tracks
;; - Find `fr` size
;;
;; - Stretch auto tracks
(ns app.common.geom.shapes.grid-layout.layout-data
(:require
[app.common.geom.point :as gpt]
[app.common.geom.shapes.points :as gpo]))
[app.common.geom.shapes.points :as gpo]
[app.common.types.shape.layout :as ctl]))
(defn layout-bounds
[parent shape-bounds]
(let [[pad-top pad-right pad-bottom pad-left] (ctl/paddings parent)]
(gpo/pad-points shape-bounds pad-top pad-right pad-bottom pad-left)))
#_(defn set-sample-data
[parent children]
@ -50,91 +103,218 @@
[parent children]))
(defn calculate-initial-track-values
[{:keys [type value]} total-value]
(defn calculate-initial-track-size
[total-value {:keys [type value] :as track}]
(case type
:percent
(let [value (/ (* total-value value) 100) ]
value)
(let [[size max-size]
(case type
:percent
(let [value (/ (* total-value value) 100) ]
[value value])
:fixed
value
:fixed
[value value]
:auto
0
))
;; flex, auto
[0.01 ##Inf])]
(assoc track :size size :max-size max-size)))
(defn set-auto-base-size
[track-list children shape-cells type]
(let [[prop prop-span size-fn]
(if (= type :column)
[:column :column-span gpo/width-points]
[:row :row-span gpo/height-points])]
(reduce (fn [tracks [child-bounds child-shape]]
(let [cell (get shape-cells (:id child-shape))
idx (dec (get cell prop))
track (nth tracks idx)]
(cond-> tracks
(and (= (get cell prop-span) 1) (= :auto (:type track)))
(update-in [idx :size] max (size-fn child-bounds)))))
track-list
children)))
(defn tracks-total-size
[track-list]
(let [calc-tracks-total-size
(fn [acc {:keys [size]}]
(+ acc size))]
(->> track-list (reduce calc-tracks-total-size 0))))
(defn tracks-total-frs
[track-list]
(let [calc-tracks-total-frs
(fn [acc {:keys [type value]}]
(let [value (max 1 value)]
(cond-> acc
(= type :flex)
(+ value))))]
(->> track-list (reduce calc-tracks-total-frs 0))))
(defn tracks-total-autos
[track-list]
(let [calc-tracks-total-autos
(fn [acc {:keys [type]}]
(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)))))))
(defn add-auto-size
[track-list add-size]
(->> track-list
(mapv (fn [{:keys [type size max-size] :as track}]
(cond-> track
(= :auto type)
(assoc :size (min (+ size add-size) max-size)))))))
(defn calc-layout-data
[parent _children transformed-parent-bounds]
[parent children transformed-parent-bounds]
(let [height (gpo/height-points transformed-parent-bounds)
width (gpo/width-points transformed-parent-bounds)
(let [hv #(gpo/start-hv transformed-parent-bounds %)
vv #(gpo/start-vv transformed-parent-bounds %)
;; Initialize tracks
column-tracks
(->> (:layout-grid-columns parent)
(map (fn [track]
(let [initial (calculate-initial-track-values track width)]
(assoc track :value initial)))))
layout-bounds (layout-bounds parent transformed-parent-bounds)
row-tracks
(->> (:layout-grid-rows parent)
(map (fn [track]
(let [initial (calculate-initial-track-values track height)]
(assoc track :value initial)))))
bound-height (gpo/height-points layout-bounds)
bound-width (gpo/width-points layout-bounds)
bound-corner (gpo/origin layout-bounds)
;; Go through cells to adjust auto sizes
grid-columns (:layout-grid-columns parent)
grid-rows (:layout-grid-rows parent)
[row-gap column-gap] (ctl/gaps parent)
;; Once auto sizes have been calculated we get calculate the `fr` with the remainining size and adjust the size
;; Adjust final distances
acc-track-distance
(fn [[result next-distance] data]
(let [result (conj result (assoc data :distance next-distance))
next-distance (+ next-distance (:value data))]
[result next-distance]))
column-tracks
(->> column-tracks
(reduce acc-track-distance [[] 0])
first)
row-tracks
(->> row-tracks
(reduce acc-track-distance [[] 0])
first)
;; Map shape->cell
shape-cells
(into {}
(mapcat (fn [[_ cell]]
(->> (:shapes cell)
(map #(vector % cell)))))
(->> (:shapes cell) (map #(vector % cell)))))
(:layout-grid-cells parent))
;; Initialize tracks
column-tracks
(->> grid-columns
(mapv (partial calculate-initial-track-size bound-width)))
row-tracks
(->> 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
column-tracks (set-auto-base-size column-tracks children shape-cells :column)
row-tracks (set-auto-base-size row-tracks children shape-cells :row)
;; Adjust multi-spaned cells with no flex columns
;; TODO
;; 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-frs (tracks-total-frs column-tracks)
row-frs (tracks-total-frs row-tracks)
;; 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)
column-tracks (set-fr-value column-tracks column-fr)
row-tracks (set-fr-value row-tracks row-fr)
;; 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))
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)
column-tracks (add-auto-size column-tracks column-add-auto)
row-tracks (add-auto-size row-tracks row-add-auto)
start-p
(cond-> bound-corner
(= :end (:layout-align-content parent))
(gpt/add (hv (- bound-width (+ column-total-size column-total-gap))))
(= :center (:layout-align-content parent))
(gpt/add (hv (/ (- bound-width (+ column-total-size column-total-gap)) 2)))
(= :end (:layout-justify-content parent))
(gpt/add (vv (- bound-height (+ row-total-size row-total-gap))))
(= :center (:layout-justify-content parent))
(gpt/add (vv (/ (- bound-height (+ row-total-size row-total-gap)) 2))))
column-tracks
(->> column-tracks
(reduce (fn [[tracks start-p] {:keys [size] :as track}]
[(conj tracks (assoc track :start-p start-p))
(gpt/add start-p (hv (+ size column-gap)))])
[[] start-p])
(first))
row-tracks
(->> row-tracks
(reduce (fn [[tracks start-p] {:keys [size] :as track}]
[(conj tracks (assoc track :start-p start-p))
(gpt/add start-p (vv (+ size row-gap)))])
[[] start-p])
(first))
]
{:row-tracks row-tracks
{:origin start-p
:layout-bounds layout-bounds
:row-tracks row-tracks
:column-tracks column-tracks
:shape-cells shape-cells}))
:shape-cells shape-cells
;; Convenient informaton for visualization
:column-total-size column-total-size
:column-total-gap column-total-gap
:row-total-size row-total-size
:row-total-gap row-total-gap
}))
(defn get-cell-data
[{:keys [row-tracks column-tracks shape-cells]} transformed-parent-bounds [_ child]]
[{:keys [origin row-tracks column-tracks shape-cells]} _transformed-parent-bounds [_ child]]
(let [origin (gpo/origin transformed-parent-bounds)
hv #(gpo/start-hv transformed-parent-bounds %)
vv #(gpo/start-vv transformed-parent-bounds %)
grid-cell (get shape-cells (:id child))]
(let [grid-cell (get shape-cells (:id child))]
(when (some? grid-cell)
(let [column (nth column-tracks (dec (:column grid-cell)) nil)
row (nth row-tracks (dec (:row grid-cell)) nil)
start-p (-> origin
(gpt/add (hv (:distance column)))
(gpt/add (vv (:distance row))))]
column-start-p (:start-p column)
row-start-p (:start-p row)
start-p (gpt/add origin
(gpt/add
(gpt/to-vec origin column-start-p)
(gpt/to-vec origin row-start-p)))
]
(assoc grid-cell :start-p start-p)))))

View file

@ -221,8 +221,7 @@
bound+child (first children)
pending (rest children)]
(if (some? bound+child)
(let [[_ child] bound+child
cell-data (gcgl/get-cell-data grid-data @transformed-parent-bounds bound+child)
(let [cell-data (gcgl/get-cell-data grid-data @transformed-parent-bounds bound+child)
modif-tree (cond-> modif-tree
(some? cell-data)
(set-child-modifiers cell-data bound+child))]

View file

@ -298,6 +298,13 @@
layout-gap-col (or (-> layout-gap :column-gap (mth/finite 0)) 0)]
[layout-gap-row layout-gap-col]))
(defn paddings
[{:keys [layout-padding-type layout-padding]}]
(let [{pad-top :p1 pad-right :p2 pad-bottom :p3 pad-left :p4} layout-padding]
(if (= :simple layout-padding-type)
[pad-top pad-right pad-top pad-right]
[pad-top pad-right pad-bottom pad-left])))
(defn child-min-width
[child]
(if (and (fill-width? child)
@ -556,8 +563,7 @@
(declare assign-cells)
(def default-track-value
{:type :fixed
:value 100})
{:type :auto})
(def grid-cell-defaults
{:row-span 1
@ -740,21 +746,21 @@
to-add-tracks
(if (= (:layout-grid-dir parent) :row)
(mth/ceil (/ (- (count no-cell-shapes) (count free-cells)) (count (:layout-grid-columns parent))))
(mth/ceil (/ (- (count no-cell-shapes) (count free-cells)) (count (:layout-grid-rows parent)))))
(mth/ceil (/ (- (count no-cell-shapes) (count free-cells)) (count (:layout-grid-rows parent))))
(mth/ceil (/ (- (count no-cell-shapes) (count free-cells)) (count (:layout-grid-columns parent)))))
add-track (if (= (:layout-grid-dir parent) :row) add-grid-row add-grid-column)
add-track (if (= (:layout-grid-dir parent) :row) add-grid-column add-grid-row)
parent
(->> (range to-add-tracks)
(reduce #(add-track %1 default-track-value) parent))
(reduce (fn [parent _] (add-track parent default-track-value)) parent))
[pending-shapes cells]
cells
(loop [cells (:layout-grid-cells parent)
free-cells (get-free-cells parent {:sort? true})
pending no-cell-shapes]
(if (or (empty? free-cells) (empty? pending))
[pending cells]
cells
(let [next-free (first free-cells)
current (first pending)
cells (update-in cells [next-free :shapes] conj current)]