0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-02-15 11:38:24 -05:00

Add min/max width/height for elements

This commit is contained in:
alonso.torres 2022-10-25 15:23:01 +02:00
parent b8c90fdcf3
commit 4b61e3228f
5 changed files with 203 additions and 129 deletions

View file

@ -6,11 +6,15 @@
(ns app.common.geom.shapes.flex-layout.lines (ns app.common.geom.shapes.flex-layout.lines
(:require (:require
[app.common.data :as d]
[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.geom.shapes.transforms :as gst] [app.common.geom.shapes.transforms :as gst]
[app.common.math :as mth]
[app.common.types.shape.layout :as ctl])) [app.common.types.shape.layout :as ctl]))
(def conjv (fnil conj []))
(defn layout-bounds (defn layout-bounds
[{:keys [layout-padding layout-padding-type] :as shape}] [{:keys [layout-padding layout-padding-type] :as shape}]
(let [;; Add padding to the bounds (let [;; Add padding to the bounds
@ -38,53 +42,65 @@
layout-width (gpo/width-points layout-bounds) layout-width (gpo/width-points layout-bounds)
layout-height (gpo/height-points layout-bounds) layout-height (gpo/height-points layout-bounds)
reduce-fn calculate-line-data
(fn [[{:keys [line-width line-height num-children line-fill? child-fill? num-child-fill] :as line-data} result] child] (fn [[{:keys [line-min-width line-min-height
line-max-width line-max-height
num-children
children-data] :as line-data} result] child]
(let [child-bounds (gst/parent-coords-points child shape) (let [child-bounds (gst/parent-coords-points child shape)
child-width (gpo/width-points child-bounds) child-width (gpo/width-points child-bounds)
child-height (gpo/height-points child-bounds) child-height (gpo/height-points child-bounds)
child-min-width (ctl/child-min-width child) child-min-width (ctl/child-min-width child)
child-min-height (ctl/child-min-height child) child-min-height (ctl/child-min-height child)
child-max-width (ctl/child-max-width child)
child-max-height (ctl/child-max-height child)
fill-width? (ctl/fill-width? child) fill-width? (ctl/fill-width? child)
fill-height? (ctl/fill-height? child) fill-height? (ctl/fill-height? child)
cur-child-fill? (or (and row? fill-width?) (and col? fill-height?)) ;; We need this info later to calculate the child resizes when fill
cur-line-fill? (or (and col? fill-width?) (and row? fill-height?)) child-data {:id (:id child)
:child-min-width (if fill-width? child-min-width child-width)
:child-min-height (if fill-height? child-min-height child-height)
:child-max-width (if fill-width? child-max-width child-width)
:child-max-height (if fill-height? child-max-height child-height)}
next-width (if fill-width? child-min-width child-width) next-min-width (if fill-width? child-min-width child-width)
next-height (if fill-height? child-min-height child-height) next-min-height (if fill-height? child-min-height child-height)
next-max-width (if fill-width? child-max-width child-width)
next-line-width (+ line-width next-width (* layout-gap-row (dec num-children))) next-max-height (if fill-height? child-max-height child-height)
next-line-height (+ line-height next-height (* layout-gap-col (dec num-children)))]
next-line-min-width (+ line-min-width next-min-width (* layout-gap-row num-children))
next-line-min-height (+ line-min-height next-min-height (* layout-gap-col num-children))]
(if (and (some? line-data) (if (and (some? line-data)
(or (not wrap?) (or (not wrap?)
(and row? (<= next-line-width layout-width)) (and row? (<= next-line-min-width layout-width))
(and col? (<= next-line-height layout-height)))) (and col? (<= next-line-min-height layout-height))))
[{:line-width (if row? (+ line-width next-width) (max line-width next-width)) [{:line-min-width (if row? (+ line-min-width next-min-width) (max line-min-width next-min-width))
:line-height (if col? (+ line-height next-height) (max line-height next-height)) :line-max-width (if row? (+ line-max-width next-max-width) (max line-max-width next-max-width))
:line-min-height (if col? (+ line-min-height next-min-height) (max line-min-height next-min-height))
:line-max-height (if col? (+ line-max-height next-max-height) (max line-max-height next-max-height))
:num-children (inc num-children) :num-children (inc num-children)
:child-fill? (or cur-child-fill? child-fill?) :children-data (conjv children-data child-data)}
:line-fill? (or cur-line-fill? line-fill?)
:num-child-fill (cond-> num-child-fill cur-child-fill? inc)}
result] result]
[{:line-width next-width [{:line-min-width next-min-width
:line-height next-height :line-min-height next-min-height
:line-max-width next-max-width
:line-max-height next-max-height
:num-children 1 :num-children 1
:child-fill? cur-child-fill? :children-data [child-data]}
:line-fill? cur-line-fill?
:num-child-fill (if cur-child-fill? 1 0)}
(cond-> result (some? line-data) (conj line-data))]))) (cond-> result (some? line-data) (conj line-data))])))
[line-data layout-lines] (reduce reduce-fn [nil []] children)] [line-data layout-lines] (reduce calculate-line-data [nil []] children)]
(cond-> layout-lines (some? line-data) (conj line-data)))) (cond-> layout-lines (some? line-data) (conj line-data))))
(defn get-base-line (defn get-base-line
[parent layout-bounds total-width total-height] "Main axis line"
[parent layout-bounds total-width total-height num-lines]
(let [layout-width (gpo/width-points layout-bounds) (let [layout-width (gpo/width-points layout-bounds)
layout-height (gpo/height-points layout-bounds) layout-height (gpo/height-points layout-bounds)
@ -95,20 +111,25 @@
v-center? (ctl/v-center? parent) v-center? (ctl/v-center? parent)
v-end? (ctl/v-end? parent) v-end? (ctl/v-end? parent)
hv (partial gpo/start-hv layout-bounds) hv (partial gpo/start-hv layout-bounds)
vv (partial gpo/start-vv layout-bounds)] vv (partial gpo/start-vv layout-bounds)
;; Adjust the totals so it takes into account the gaps
[layout-gap-row layout-gap-col] (ctl/gaps parent)
lines-gap-row (* (dec num-lines) layout-gap-row)
lines-gap-col (* (dec num-lines) layout-gap-col)]
(cond-> (gpo/origin layout-bounds) (cond-> (gpo/origin layout-bounds)
(and col? h-center?)
(gpt/add (hv (/ (- layout-width total-width) 2)))
(and col? h-end?)
(gpt/add (hv (- layout-width total-width)))
(and row? v-center?) (and row? v-center?)
(gpt/add (vv (/ (- layout-height total-height) 2))) (gpt/add (vv (/ (- layout-height total-height lines-gap-col) 2)))
(and row? v-end?) (and row? v-end?)
(gpt/add (vv (- layout-height total-height)))))) (gpt/add (vv (- layout-height total-height lines-gap-col)))
(and col? h-center?)
(gpt/add (hv (/ (- layout-width total-width lines-gap-row) 2)))
(and col? h-end?)
(gpt/add (hv (- layout-width total-width lines-gap-row))))))
(defn get-next-line (defn get-next-line
[parent layout-bounds {:keys [line-width line-height]} base-p] [parent layout-bounds {:keys [line-width line-height]} base-p]
@ -129,6 +150,7 @@
(gpt/add (vv (+ line-height layout-gap-col)))))) (gpt/add (vv (+ line-height layout-gap-col))))))
(defn get-start-line (defn get-start-line
"Cross axis line. It's position is fixed along the different lines"
[parent layout-bounds {:keys [line-width line-height num-children child-fill? ]} base-p] [parent layout-bounds {:keys [line-width line-height num-children child-fill? ]} base-p]
(let [layout-width (gpo/width-points layout-bounds) (let [layout-width (gpo/width-points layout-bounds)
@ -150,14 +172,6 @@
children-gap-width (* layout-gap-row (dec num-children)) children-gap-width (* layout-gap-row (dec num-children))
children-gap-height (* layout-gap-col (dec num-children)) children-gap-height (* layout-gap-col (dec num-children))
line-width (if (and row? child-fill?)
(- layout-width (* layout-gap-row (dec num-children)))
line-width)
line-height (if (and col? child-fill?)
(- layout-height (* layout-gap-col (dec num-children)))
line-height)
start-p start-p
(cond-> base-p (cond-> base-p
;; X AXIS ;; X AXIS
@ -192,20 +206,60 @@
start-p)) start-p))
(defn add-space-to-items
;; Distributes the remainder space between the lines
[prop prop-min prop-max to-share items]
(let [num-items (->> items (remove #(mth/close? (get % prop) (get % prop-max))) count)
per-line-target (/ to-share num-items)]
(loop [current (first items)
items (rest items)
remainder to-share
result []]
(if (nil? current)
[result remainder]
(let [cur-val (or (get current prop) (get current prop-min) 0)
max-val (get current prop-max)
cur-inc (if (> (+ cur-val per-line-target) max-val)
(- max-val cur-val)
per-line-target)
current (assoc current prop (+ cur-val cur-inc))
remainder (- remainder cur-inc)
result (conj result current)]
(recur (first items) (rest items) remainder result))))))
(defn distribute-space
[prop prop-min prop-max min-value bound-value items]
(loop [to-share (- bound-value min-value)
items items]
(if (<= to-share 0)
items
(let [[items remainder] (add-space-to-items prop prop-min prop-max to-share items)]
(assert (<= remainder to-share) (str remainder ">" to-share))
(if (or (<= remainder 0) (= remainder to-share))
items
(recur remainder items))))))
(defn add-lines-positions (defn add-lines-positions
[parent layout-bounds layout-lines] [parent layout-bounds layout-lines]
(let [layout-width (gpo/width-points layout-bounds) (let [row? (ctl/row? parent)
layout-height (gpo/height-points layout-bounds) col? (ctl/col? parent)
[layout-gap-row layout-gap-col] (ctl/gaps parent) [layout-gap-row layout-gap-col] (ctl/gaps parent)
row? (ctl/row? parent) layout-width (gpo/width-points layout-bounds)
col? (ctl/col? parent)] layout-height (gpo/height-points layout-bounds)]
(letfn [(add-lines [[total-width total-height] {:keys [line-width line-height]}] (letfn [(add-lines [[total-width total-height]
[(+ total-width line-width) {:keys [line-width line-height]}]
(+ total-height line-height)]) [(+ total-width line-width) (+ total-height line-height)])
(add-ranges [[total-min-width total-min-height total-max-width total-max-height]
{:keys [line-min-width line-min-height line-max-width line-max-height]}]
[(+ total-min-width line-min-width)
(+ total-min-height line-min-height)
(+ total-max-width line-max-width)
(+ total-max-height line-max-height)])
(add-starts [[result base-p] layout-line] (add-starts [[result base-p] layout-line]
(let [start-p (get-start-line parent layout-bounds layout-line base-p) (let [start-p (get-start-line parent layout-bounds layout-line base-p)
@ -215,29 +269,44 @@
(assoc layout-line :start-p start-p)) (assoc layout-line :start-p start-p))
next-p]))] next-p]))]
(let [[total-width total-height] (->> layout-lines (reduce add-lines [0 0])) (let [[total-min-width total-min-height total-max-width total-max-height]
(->> layout-lines (reduce add-ranges [0 0 0 0]))
total-width (+ total-width (* layout-gap-row (dec (count layout-lines)))) get-layout-width (fn [{:keys [num-children]}] (- layout-width (* layout-gap-row (dec num-children))))
total-height (+ total-height (* layout-gap-col (dec (count layout-lines)))) get-layout-height (fn [{:keys [num-children]}] (- layout-height (* layout-gap-col (dec num-children))))
vertical-fill-space (- layout-height total-height) num-lines (count layout-lines)
horizontal-fill-space (- layout-width total-width)
num-line-fill (count (->> layout-lines (filter :line-fill?)))
;; Distributes the space between the layout lines based on its max/min constraints
layout-lines layout-lines
(->> layout-lines (cond->> layout-lines
(mapv #(cond-> % row?
(and row? (:line-fill? %)) (map #(assoc % :line-width (max (:line-min-width %) (min (get-layout-width %) (:line-max-width %)))))
(update :line-height + (/ vertical-fill-space num-line-fill))
(and col? (:line-fill? %)) col?
(update :line-width + (/ horizontal-fill-space num-line-fill))))) (map #(assoc % :line-height (max (:line-min-height %) (min (get-layout-height %) (:line-max-height %)))))
total-width (if (and col? (> num-line-fill 0)) layout-width total-width) (and row? (>= total-min-height layout-height))
total-height (if (and row? (> num-line-fill 0)) layout-height total-height) (map #(assoc % :line-height (:line-min-height %)))
base-p (get-base-line parent layout-bounds total-width total-height)] (and row? (<= total-max-height layout-height))
(map #(assoc % :line-height (:line-max-height %)))
(and row? (< total-min-height layout-height total-max-height))
(distribute-space :line-height :line-min-height :line-max-height total-min-height (- layout-height (* (dec num-lines) layout-gap-col)))
(and col? (>= total-min-width layout-width))
(map #(assoc % :line-width (:line-min-width %)))
(and col? (<= total-max-width layout-width))
(map #(assoc % :line-width (:line-max-width %)))
(and col? (< total-min-width layout-width total-max-width))
(distribute-space :line-width :line-min-width :line-max-width total-min-width (- layout-width (* (dec num-lines) layout-gap-row))))
[total-width total-height] (->> layout-lines (reduce add-lines [0 0]))
base-p (get-base-line parent layout-bounds total-width total-height num-lines)]
(first (reduce add-starts [[] base-p] layout-lines)))))) (first (reduce add-starts [[] base-p] layout-lines))))))
@ -291,6 +360,23 @@
:margin-x margin-x :margin-x margin-x
:margin-y margin-y))) :margin-y margin-y)))
(defn add-children-resizes
[shape {:keys [line-min-width line-width line-min-height line-height] :as line-data}]
(let [row? (ctl/row? shape)
col? (ctl/col? shape)]
(update line-data :children-data
(fn [children-data]
(cond->> children-data
row?
(distribute-space :child-width :child-min-width :child-max-width line-min-width line-width)
col?
(distribute-space :child-height :child-min-height :child-max-height line-min-height line-height)
:always
(d/index-by :id))))))
(defn calc-layout-data (defn calc-layout-data
"Digest the layout data to pass it to the constrains" "Digest the layout data to pass it to the constrains"
[shape children] [shape children]
@ -303,7 +389,8 @@
layout-lines layout-lines
(->> (init-layout-lines shape children layout-bounds) (->> (init-layout-lines shape children layout-bounds)
(add-lines-positions shape layout-bounds) (add-lines-positions shape layout-bounds)
(mapv (partial add-line-spacing shape layout-bounds)))] (mapv (partial add-line-spacing shape layout-bounds))
(mapv (partial add-children-resizes shape)))]
{:layout-lines layout-lines {:layout-lines layout-lines
:reverse? reverse?})) :reverse? reverse?}))

View file

@ -33,47 +33,40 @@
(defn calc-fill-width-data (defn calc-fill-width-data
"Calculates the size and modifiers for the width of an auto-fill child" "Calculates the size and modifiers for the width of an auto-fill child"
[{:keys [transform transform-inverse] :as parent} [{:keys [transform transform-inverse] :as parent}
{:keys [layout-h-behavior] :as child} child
child-origin child-width child-origin child-width
{:keys [num-children line-width line-fill? child-fill? layout-bounds] :as layout-data}] {:keys [children-data line-width] :as layout-data}]
(let [[layout-gap-row _] (ctl/gaps parent)]
(cond (cond
(and (ctl/row? parent) (= :fill layout-h-behavior) child-fill?) (and (ctl/row? parent) (ctl/fill-width? child))
(let [layout-width (gpo/width-points layout-bounds) (let [target-width (get-in children-data [(:id child) :child-width])
fill-space (- layout-width line-width (* layout-gap-row (dec num-children))) fill-scale (/ target-width child-width)]
fill-width (/ fill-space (:num-child-fill layout-data)) {:width target-width
fill-scale (/ fill-width child-width)]
{:width fill-width
:modifiers (ctm/resize (gpt/point fill-scale 1) child-origin transform transform-inverse)}) :modifiers (ctm/resize (gpt/point fill-scale 1) child-origin transform transform-inverse)})
(and (ctl/col? parent) (= :fill layout-h-behavior) line-fill?) (and (ctl/col? parent) (ctl/fill-width? child))
(let [fill-scale (/ line-width child-width)] (let [fill-scale (/ line-width child-width)]
{:width line-width {:width line-width
:modifiers (ctm/resize (gpt/point fill-scale 1) child-origin transform transform-inverse)})))) :modifiers (ctm/resize (gpt/point fill-scale 1) child-origin transform transform-inverse)})))
(defn calc-fill-height-data (defn calc-fill-height-data
"Calculates the size and modifiers for the height of an auto-fill child" "Calculates the size and modifiers for the height of an auto-fill child"
[{:keys [transform transform-inverse] :as parent} [{:keys [transform transform-inverse] :as parent}
{:keys [layout-v-behavior] :as child} child
child-origin child-height child-origin child-height
{:keys [num-children line-height layout-bounds line-fill? child-fill?] :as layout-data}] {:keys [children-data line-height] :as layout-data}]
(let [[_ layout-gap-col] (ctl/gaps parent)]
(cond (cond
(and (ctl/col? parent) (= :fill layout-v-behavior) child-fill?) (and (ctl/col? parent) (ctl/fill-height? child))
(let [layout-height (gpo/height-points layout-bounds) (let [target-height (get-in children-data [(:id child) :child-height])
fill-space (- layout-height line-height (* layout-gap-col (dec num-children))) fill-scale (/ target-height child-height)]
fill-height (/ fill-space (:num-child-fill layout-data)) {:height target-height
fill-scale (/ fill-height child-height)]
{:height fill-height
:modifiers (ctm/resize (gpt/point 1 fill-scale) child-origin transform transform-inverse)}) :modifiers (ctm/resize (gpt/point 1 fill-scale) child-origin transform transform-inverse)})
(and (ctl/row? parent) (= :fill layout-v-behavior) line-fill?) (and (ctl/row? parent) (ctl/fill-height? child))
(let [fill-scale (/ line-height child-height)] (let [fill-scale (/ line-height child-height)]
{:height line-height {:height line-height
:modifiers (ctm/resize (gpt/point 1 fill-scale) child-origin transform transform-inverse)})))) :modifiers (ctm/resize (gpt/point 1 fill-scale) child-origin transform transform-inverse)})))
(defn calc-layout-modifiers (defn calc-layout-modifiers
"Calculates the modifiers for the layout" "Calculates the modifiers for the layout"

View file

@ -76,22 +76,17 @@
children (map (d/getf objects) (:shapes transformed-parent)) 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 normalize-child transformed-parent _snap-pixel?) modif-tree children)
children (->> children (map (partial apply-modifiers modif-tree))) children (->> children (map (partial apply-modifiers modif-tree)))
layout-data (gcl/calc-layout-data transformed-parent children) layout-data (gcl/calc-layout-data transformed-parent children)
children (into [] (cond-> children (:reverse? layout-data) reverse)) children (into [] (cond-> children (:reverse? layout-data) reverse))
max-idx (dec (count children)) max-idx (dec (count children))
layout-lines (:layout-lines layout-data)] layout-lines (:layout-lines layout-data)]
(loop [modif-tree modif-tree (loop [modif-tree modif-tree
layout-line (first layout-lines) layout-line (first layout-lines)
pending (rest layout-lines) pending (rest layout-lines)
from-idx 0] from-idx 0]
(if (and (some? layout-line) (<= from-idx max-idx)) (if (and (some? layout-line) (<= from-idx max-idx))
(let [to-idx (+ from-idx (:num-children layout-line)) (let [to-idx (+ from-idx (:num-children layout-line))
children (subvec children from-idx to-idx) children (subvec children from-idx to-idx)

View file

@ -80,7 +80,7 @@
(s/def ::layout-margin-type #{:simple :multiple}) (s/def ::layout-margin-type #{:simple :multiple})
(s/def ::layout-h-behavior #{:fill :fix :auto}) (s/def ::layout-h-behavior #{:fill :fix :auto})
(s/def ::layout-v-behavior #{:fill :fix :auto}) (s/def ::layout-v-behavior #{:fill :fix :auto})
(s/def ::layout-align-self #{:start :end :center :strech :baseline}) (s/def ::layout-align-self #{:start :end :center :strech})
(s/def ::layout-max-h ::us/safe-number) (s/def ::layout-max-h ::us/safe-number)
(s/def ::layout-min-h ::us/safe-number) (s/def ::layout-min-h ::us/safe-number)
(s/def ::layout-max-w ::us/safe-number) (s/def ::layout-max-w ::us/safe-number)
@ -126,30 +126,30 @@
(defn child-min-width (defn child-min-width
[child] [child]
(if (and (fill-width? child) (if (and (fill-width? child)
(some? (:layout-min-h child))) (some? (:layout-min-w child)))
(max 0 (:layout-min-h child)) (max 0 (:layout-min-w child))
0)) 0))
(defn child-max-width (defn child-max-width
[child] [child]
(if (and (fill-width? child) (if (and (fill-width? child)
(some? (:layout-max-w child)))
(max 0 (:layout-max-w child))
##Inf))
(defn child-min-height
[child]
(if (and (fill-height? child)
(some? (:layout-min-h child))) (some? (:layout-min-h child)))
(max 0 (:layout-min-h child)) (max 0 (:layout-min-h child))
0)) 0))
(defn child-min-height
[child]
(if (and (fill-width? child)
(some? (:layout-min-v child)))
(max 0 (:layout-min-v child))
0))
(defn child-max-height (defn child-max-height
[child] [child]
(if (and (fill-width? child) (if (and (fill-height? child)
(some? (:layout-min-v child))) (some? (:layout-max-h child)))
(max 0 (:layout-min-v child)) (max 0 (:layout-max-h child))
0)) ##Inf))
(defn h-start? (defn h-start?
[{:keys [layout-align-items layout-justify-content] :as shape}] [{:keys [layout-align-items layout-justify-content] :as shape}]

View file

@ -218,6 +218,5 @@
:placeholder "--" :placeholder "--"
:on-click #(dom/select-target %) :on-click #(dom/select-target %)
:on-change (partial on-size-change item) :on-change (partial on-size-change item)
;; :value (get values item) :value (get values item)
:value 100}]]])]])]] :nillable true}]]])]])]]))
))