mirror of
https://github.com/penpot/penpot.git
synced 2025-01-10 08:50:57 -05:00
⚡ Improve flex layout data calculation
This commit is contained in:
parent
be68e45f65
commit
9d6e4c9e2f
2 changed files with 108 additions and 104 deletions
|
@ -7,6 +7,7 @@
|
||||||
(ns app.common.geom.shapes.flex-layout.bounds
|
(ns app.common.geom.shapes.flex-layout.bounds
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
|
[app.common.data.macros :as dm]
|
||||||
[app.common.geom.point :as gpt]
|
[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]))
|
[app.common.types.shape.layout :as ctl]))
|
||||||
|
@ -31,7 +32,6 @@
|
||||||
(child-layout-bound-points parent child parent-bounds child-bounds (gpt/point) bounds objects))
|
(child-layout-bound-points parent child parent-bounds child-bounds (gpt/point) bounds objects))
|
||||||
|
|
||||||
([parent child parent-bounds child-bounds correct-v bounds objects]
|
([parent child parent-bounds child-bounds correct-v bounds objects]
|
||||||
|
|
||||||
(let [row? (ctl/row? parent)
|
(let [row? (ctl/row? parent)
|
||||||
col? (ctl/col? parent)
|
col? (ctl/col? parent)
|
||||||
|
|
||||||
|
@ -56,18 +56,19 @@
|
||||||
;; This is the leftmost (when row) or topmost (when col) point
|
;; This is the leftmost (when row) or topmost (when col) point
|
||||||
;; Will be added always to the bounds and then calculated the other limits
|
;; Will be added always to the bounds and then calculated the other limits
|
||||||
;; from there
|
;; from there
|
||||||
base-p (cond-> base-p
|
base-p
|
||||||
(and row? v-center?)
|
(cond-> base-p
|
||||||
(gpt/add (vv (/ height 2)))
|
(and row? v-center?)
|
||||||
|
(gpt/add (vv (/ height 2)))
|
||||||
|
|
||||||
(and row? v-end?)
|
(and row? v-end?)
|
||||||
(gpt/add (vv height))
|
(gpt/add (vv height))
|
||||||
|
|
||||||
(and col? h-center?)
|
(and col? h-center?)
|
||||||
(gpt/add (hv (/ width 2)))
|
(gpt/add (hv (/ width 2)))
|
||||||
|
|
||||||
(and col? h-end?)
|
(and col? h-end?)
|
||||||
(gpt/add (hv width)))
|
(gpt/add (hv width)))
|
||||||
|
|
||||||
;; We need some height/width to calculate the bounds. We stablish the minimum
|
;; We need some height/width to calculate the bounds. We stablish the minimum
|
||||||
min-width (max min-width 0.01)
|
min-width (max min-width 0.01)
|
||||||
|
@ -76,10 +77,12 @@
|
||||||
base-p (gpt/add base-p correct-v)
|
base-p (gpt/add base-p correct-v)
|
||||||
|
|
||||||
result
|
result
|
||||||
(cond-> [base-p
|
[base-p
|
||||||
(gpt/add base-p (hv 0.01))
|
(gpt/add base-p (hv 0.01))
|
||||||
(gpt/add base-p (vv 0.01))]
|
(gpt/add base-p (vv 0.01))]
|
||||||
|
|
||||||
|
result
|
||||||
|
(cond-> result
|
||||||
col?
|
col?
|
||||||
(conj (gpt/add base-p (vv min-height)))
|
(conj (gpt/add base-p (vv min-height)))
|
||||||
|
|
||||||
|
@ -112,41 +115,42 @@
|
||||||
(gpt/subtract (hv (+ width min-width)))
|
(gpt/subtract (hv (+ width min-width)))
|
||||||
|
|
||||||
(and col? (ctl/fill-height? child))
|
(and col? (ctl/fill-height? child))
|
||||||
(gpt/subtract (vv (+ height min-height)))
|
(gpt/subtract (vv (+ height min-height))))]
|
||||||
)]
|
|
||||||
[result correct-v])))
|
[result correct-v])))
|
||||||
|
|
||||||
(defn layout-content-points
|
(defn layout-content-points
|
||||||
[bounds parent children objects]
|
[bounds parent children objects]
|
||||||
|
|
||||||
(let [parent-id (:id parent)
|
(let [parent-id (dm/get-prop parent :id)
|
||||||
parent-bounds @(get bounds parent-id)
|
parent-bounds @(get bounds parent-id)
|
||||||
get-child-bounds
|
|
||||||
(fn [[result correct-v] child]
|
|
||||||
(let [child-id (:id child)
|
|
||||||
child-bounds @(get bounds child-id)
|
|
||||||
[margin-top margin-right margin-bottom margin-left] (ctl/child-margins child)
|
|
||||||
|
|
||||||
[child-bounds correct-v]
|
|
||||||
(if (or (ctl/fill-width? child) (ctl/fill-height? child))
|
|
||||||
(child-layout-bound-points parent child parent-bounds child-bounds correct-v bounds objects)
|
|
||||||
[(->> child-bounds (map #(gpt/add % correct-v))) correct-v])
|
|
||||||
|
|
||||||
child-bounds
|
|
||||||
(when (d/not-empty? child-bounds)
|
|
||||||
(-> (gpo/parent-coords-bounds child-bounds parent-bounds)
|
|
||||||
(gpo/pad-points (- margin-top) (- margin-right) (- margin-bottom) (- margin-left))))]
|
|
||||||
|
|
||||||
[(cond-> result (some? child-bounds) (conj child-bounds))
|
|
||||||
correct-v]))
|
|
||||||
|
|
||||||
reverse? (ctl/reverse? parent)
|
reverse? (ctl/reverse? parent)
|
||||||
children (cond->> children (not reverse?) reverse)]
|
children (cond->> children (not reverse?) reverse)]
|
||||||
|
|
||||||
(->> children
|
(loop [children (seq children)
|
||||||
(remove ctl/layout-absolute?)
|
result (transient [])
|
||||||
(reduce get-child-bounds [[] (gpt/point 0)])
|
correct-v (gpt/point 0)]
|
||||||
(first))))
|
|
||||||
|
(if (not children)
|
||||||
|
(persistent! result)
|
||||||
|
|
||||||
|
(let [child (first children)
|
||||||
|
child-id (dm/get-prop child :id)
|
||||||
|
child-bounds @(get bounds child-id)
|
||||||
|
[margin-top margin-right margin-bottom margin-left] (ctl/child-margins child)
|
||||||
|
|
||||||
|
[child-bounds correct-v]
|
||||||
|
(if (or (ctl/fill-width? child) (ctl/fill-height? child))
|
||||||
|
(child-layout-bound-points parent child parent-bounds child-bounds correct-v bounds objects)
|
||||||
|
[(->> child-bounds (map #(gpt/add % correct-v))) correct-v])
|
||||||
|
|
||||||
|
child-bounds
|
||||||
|
(when (d/not-empty? child-bounds)
|
||||||
|
(-> (gpo/parent-coords-bounds child-bounds parent-bounds)
|
||||||
|
(gpo/pad-points (- margin-top) (- margin-right) (- margin-bottom) (- margin-left))))]
|
||||||
|
|
||||||
|
(recur (next children)
|
||||||
|
(cond-> result (some? child-bounds) (conj! child-bounds))
|
||||||
|
correct-v))))))
|
||||||
|
|
||||||
(defn layout-content-bounds
|
(defn layout-content-bounds
|
||||||
[bounds {:keys [layout-padding] :as parent} children objects]
|
[bounds {:keys [layout-padding] :as parent} children objects]
|
||||||
|
|
|
@ -53,11 +53,11 @@
|
||||||
layout-height (gpo/height-points layout-bounds)]
|
layout-height (gpo/height-points layout-bounds)]
|
||||||
|
|
||||||
(loop [line-data nil
|
(loop [line-data nil
|
||||||
result []
|
result (transient [])
|
||||||
children (seq children)]
|
children (seq children)]
|
||||||
|
|
||||||
(if (empty? children)
|
(if (not children)
|
||||||
(cond-> result (some? line-data) (conj line-data))
|
(persistent! (cond-> result (some? line-data) (conj! line-data)))
|
||||||
|
|
||||||
(let [[child-bounds child] (first children)
|
(let [[child-bounds child] (first children)
|
||||||
{:keys [line-min-width line-min-height
|
{:keys [line-min-width line-min-height
|
||||||
|
@ -91,25 +91,27 @@
|
||||||
next-max-width (+ child-margin-width (:child-max-width child-data))
|
next-max-width (+ child-margin-width (:child-max-width child-data))
|
||||||
next-max-height (+ child-margin-height (:child-max-height child-data))
|
next-max-height (+ child-margin-height (:child-max-height child-data))
|
||||||
|
|
||||||
total-gap-col (cond
|
total-gap-col
|
||||||
space-evenly?
|
(cond
|
||||||
(* layout-gap-col (+ num-children 2))
|
space-evenly?
|
||||||
|
(* layout-gap-col (+ num-children 2))
|
||||||
|
|
||||||
space-around?
|
space-around?
|
||||||
(* layout-gap-col (+ num-children 1))
|
(* layout-gap-col (+ num-children 1))
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(* layout-gap-col num-children))
|
(* layout-gap-col num-children))
|
||||||
|
|
||||||
total-gap-row (cond
|
total-gap-row
|
||||||
space-evenly?
|
(cond
|
||||||
(* layout-gap-row (+ num-children 2))
|
space-evenly?
|
||||||
|
(* layout-gap-row (+ num-children 2))
|
||||||
|
|
||||||
space-around?
|
space-around?
|
||||||
(* layout-gap-row (+ num-children 1))
|
(* layout-gap-row (+ num-children 1))
|
||||||
|
|
||||||
:else
|
:else
|
||||||
(* layout-gap-row num-children))
|
(* layout-gap-row num-children))
|
||||||
|
|
||||||
next-line-min-width (+ line-min-width next-min-width total-gap-col)
|
next-line-min-width (+ line-min-width next-min-width total-gap-col)
|
||||||
next-line-min-height (+ line-min-height next-min-height total-gap-row)]
|
next-line-min-height (+ line-min-height next-min-height total-gap-row)]
|
||||||
|
@ -128,7 +130,7 @@
|
||||||
:num-children (inc num-children)
|
:num-children (inc num-children)
|
||||||
:children-data (conjv children-data child-data)}
|
:children-data (conjv children-data child-data)}
|
||||||
result
|
result
|
||||||
(rest children))
|
(next children))
|
||||||
|
|
||||||
(recur {:line-min-width next-min-width
|
(recur {:line-min-width next-min-width
|
||||||
:line-min-height next-min-height
|
:line-min-height next-min-height
|
||||||
|
@ -136,29 +138,31 @@
|
||||||
:line-max-height next-max-height
|
:line-max-height next-max-height
|
||||||
:num-children 1
|
:num-children 1
|
||||||
:children-data [child-data]}
|
:children-data [child-data]}
|
||||||
(cond-> result (some? line-data) (conj line-data))
|
(cond-> result (some? line-data) (conj! line-data))
|
||||||
(rest children))))))))
|
(next children))))))))
|
||||||
|
|
||||||
(defn add-space-to-items
|
(defn add-space-to-items
|
||||||
;; Distributes the remainder space between the lines
|
;; Distributes the remainder space between the lines
|
||||||
[prop prop-min prop-max to-share items]
|
[prop prop-min prop-max to-share items]
|
||||||
(let [num-items (->> items (remove #(mth/close? (get % prop) (get % prop-max))) count)
|
(let [num-items (->> items (remove #(mth/close? (get % prop) (get % prop-max))) count)
|
||||||
per-line-target (/ to-share num-items)]
|
per-line-target (/ to-share num-items)]
|
||||||
(loop [current (first items)
|
|
||||||
items (rest items)
|
(loop [items (seq items)
|
||||||
remainder to-share
|
remainder to-share
|
||||||
result []]
|
result (transient [])]
|
||||||
(if (nil? current)
|
|
||||||
[result remainder]
|
(if (not items)
|
||||||
(let [cur-val (or (get current prop) (get current prop-min) 0)
|
[(persistent! result) remainder]
|
||||||
|
|
||||||
|
(let [current (first items)
|
||||||
|
cur-val (or (get current prop) (get current prop-min) 0)
|
||||||
max-val (get current prop-max)
|
max-val (get current prop-max)
|
||||||
cur-inc (if (> (+ cur-val per-line-target) max-val)
|
cur-inc (if (> (+ cur-val per-line-target) max-val)
|
||||||
(- max-val cur-val)
|
(- max-val cur-val)
|
||||||
per-line-target)
|
per-line-target)
|
||||||
current (assoc current prop (+ cur-val cur-inc))
|
current (assoc current prop (+ cur-val cur-inc))
|
||||||
remainder (- remainder cur-inc)
|
remainder (- remainder cur-inc)]
|
||||||
result (conj result current)]
|
(recur (next items) remainder (conj! result current)))))))
|
||||||
(recur (first items) (rest items) remainder result))))))
|
|
||||||
|
|
||||||
(defn distribute-space
|
(defn distribute-space
|
||||||
[prop prop-min prop-max min-value bound-value items]
|
[prop prop-min prop-max min-value bound-value items]
|
||||||
|
@ -200,36 +204,24 @@
|
||||||
(add-starts [total-width total-height num-lines [result base-p] layout-line]
|
(add-starts [total-width total-height num-lines [result base-p] layout-line]
|
||||||
(let [start-p (flp/get-start-line parent layout-bounds layout-line base-p total-width total-height num-lines)
|
(let [start-p (flp/get-start-line parent layout-bounds layout-line base-p total-width total-height num-lines)
|
||||||
next-p (flp/get-next-line parent layout-bounds layout-line base-p total-width total-height num-lines)]
|
next-p (flp/get-next-line parent layout-bounds layout-line base-p total-width total-height num-lines)]
|
||||||
|
[(-> result (conj! (assoc layout-line :start-p start-p)))
|
||||||
|
next-p]))
|
||||||
|
|
||||||
[(conj result (assoc layout-line :start-p start-p))
|
(get-layout-width [{:keys [num-children]}]
|
||||||
next-p]))]
|
(let [num-gap (cond space-evenly? (inc num-children)
|
||||||
|
space-around? num-children
|
||||||
|
:else (dec num-children))]
|
||||||
|
(- layout-width (* layout-gap-col num-gap))))
|
||||||
|
|
||||||
|
(get-layout-height [{:keys [num-children]}]
|
||||||
|
(let [num-gap (cond space-evenly? (inc num-children)
|
||||||
|
space-around? num-children
|
||||||
|
:else (dec num-children))]
|
||||||
|
(- layout-height (* layout-gap-row num-gap))))]
|
||||||
|
|
||||||
(let [[total-min-width total-min-height total-max-width total-max-height]
|
(let [[total-min-width total-min-height total-max-width total-max-height]
|
||||||
(->> layout-lines (reduce add-ranges [0 0 0 0]))
|
(->> layout-lines (reduce add-ranges [0 0 0 0]))
|
||||||
|
|
||||||
get-layout-width (fn [{:keys [num-children]}]
|
|
||||||
(let [num-gap (cond
|
|
||||||
space-evenly?
|
|
||||||
(inc num-children)
|
|
||||||
|
|
||||||
space-around?
|
|
||||||
num-children
|
|
||||||
|
|
||||||
:else
|
|
||||||
(dec num-children))]
|
|
||||||
(- layout-width (* layout-gap-col num-gap))))
|
|
||||||
get-layout-height (fn [{:keys [num-children]}]
|
|
||||||
(let [num-gap (cond
|
|
||||||
space-evenly?
|
|
||||||
(inc num-children)
|
|
||||||
|
|
||||||
space-around?
|
|
||||||
num-children
|
|
||||||
|
|
||||||
:else
|
|
||||||
(dec num-children))]
|
|
||||||
(- layout-height (* layout-gap-row num-gap))))
|
|
||||||
|
|
||||||
num-lines (count layout-lines)
|
num-lines (count layout-lines)
|
||||||
|
|
||||||
;; When align-items is stretch we need to adjust the main axis size to grow for the full content
|
;; When align-items is stretch we need to adjust the main axis size to grow for the full content
|
||||||
|
@ -247,6 +239,7 @@
|
||||||
rest-layout-width (- layout-width (* (dec num-lines) layout-gap-col))
|
rest-layout-width (- layout-width (* (dec num-lines) layout-gap-col))
|
||||||
|
|
||||||
;; Distributes the space between the layout lines based on its max/min constraints
|
;; Distributes the space between the layout lines based on its max/min constraints
|
||||||
|
|
||||||
layout-lines
|
layout-lines
|
||||||
(cond->> layout-lines
|
(cond->> layout-lines
|
||||||
row?
|
row?
|
||||||
|
@ -267,14 +260,16 @@
|
||||||
(and row? (<= total-max-height rest-layout-height) (not auto-height?))
|
(and row? (<= total-max-height rest-layout-height) (not auto-height?))
|
||||||
(map #(assoc % :line-height (+ (:line-max-height %) stretch-height-fix)))
|
(map #(assoc % :line-height (+ (:line-max-height %) stretch-height-fix)))
|
||||||
|
|
||||||
(and row? (< total-min-height rest-layout-height total-max-height) (not auto-height?))
|
|
||||||
(distribute-space :line-height :line-min-height :line-max-height total-min-height rest-layout-height)
|
|
||||||
|
|
||||||
(and col? (or (>= total-min-width rest-layout-width) auto-width?))
|
(and col? (or (>= total-min-width rest-layout-width) auto-width?))
|
||||||
(map #(assoc % :line-width (:line-min-width %)))
|
(map #(assoc % :line-width (:line-min-width %)))
|
||||||
|
|
||||||
(and col? (<= total-max-width rest-layout-width) (not auto-width?))
|
(and col? (<= total-max-width rest-layout-width) (not auto-width?))
|
||||||
(map #(assoc % :line-width (+ (:line-max-width %) stretch-width-fix)))
|
(map #(assoc % :line-width (+ (:line-max-width %) stretch-width-fix))))
|
||||||
|
|
||||||
|
layout-lines
|
||||||
|
(cond->> layout-lines
|
||||||
|
(and row? (< total-min-height rest-layout-height total-max-height) (not auto-height?))
|
||||||
|
(distribute-space :line-height :line-min-height :line-max-height total-min-height rest-layout-height)
|
||||||
|
|
||||||
(and col? (< total-min-width rest-layout-width total-max-width) (not auto-width?))
|
(and col? (< total-min-width rest-layout-width total-max-width) (not auto-width?))
|
||||||
(distribute-space :line-width :line-min-width :line-max-width total-min-width rest-layout-width))
|
(distribute-space :line-width :line-min-width :line-max-width total-min-width rest-layout-width))
|
||||||
|
@ -286,19 +281,21 @@
|
||||||
(->> layout-lines
|
(->> layout-lines
|
||||||
(reduce
|
(reduce
|
||||||
(fn [[result rest-layout-height] {:keys [line-height] :as line}]
|
(fn [[result rest-layout-height] {:keys [line-height] :as line}]
|
||||||
[(conj result (assoc line :to-bound-height rest-layout-height))
|
[(conj! result (assoc line :to-bound-height rest-layout-height))
|
||||||
(- rest-layout-height line-height layout-gap-row)])
|
(- rest-layout-height line-height layout-gap-row)])
|
||||||
[[] layout-height])
|
[(transient []) layout-height])
|
||||||
(first))
|
(first)
|
||||||
|
(persistent!))
|
||||||
|
|
||||||
col?
|
col?
|
||||||
(->> layout-lines
|
(->> layout-lines
|
||||||
(reduce
|
(reduce
|
||||||
(fn [[result rest-layout-width] {:keys [line-width] :as line}]
|
(fn [[result rest-layout-width] {:keys [line-width] :as line}]
|
||||||
[(conj result (assoc line :to-bound-width rest-layout-width))
|
[(conj! result (assoc line :to-bound-width rest-layout-width))
|
||||||
(- rest-layout-width line-width layout-gap-col)])
|
(- rest-layout-width line-width layout-gap-col)])
|
||||||
[[] layout-width])
|
[(transient []) layout-width])
|
||||||
(first))
|
(first)
|
||||||
|
(persistent!))
|
||||||
|
|
||||||
:else
|
:else
|
||||||
layout-lines)
|
layout-lines)
|
||||||
|
@ -307,7 +304,10 @@
|
||||||
|
|
||||||
base-p (flp/get-base-line parent layout-bounds total-width total-height num-lines)]
|
base-p (flp/get-base-line parent layout-bounds total-width total-height num-lines)]
|
||||||
|
|
||||||
(first (reduce (partial add-starts total-width total-height num-lines) [[] base-p] layout-lines))))))
|
(->> layout-lines
|
||||||
|
(reduce (partial add-starts total-width total-height num-lines) [(transient []) base-p])
|
||||||
|
(first)
|
||||||
|
(persistent!))))))
|
||||||
|
|
||||||
(defn add-line-spacing
|
(defn add-line-spacing
|
||||||
"Calculates the baseline for a flex layout"
|
"Calculates the baseline for a flex layout"
|
||||||
|
|
Loading…
Reference in a new issue