mirror of
https://github.com/penpot/penpot.git
synced 2025-02-13 02:28:18 -05:00
✨ Refactor flex layout namespace
This commit is contained in:
parent
58fd20094a
commit
b8c90fdcf3
7 changed files with 798 additions and 685 deletions
|
@ -6,687 +6,13 @@
|
|||
|
||||
(ns app.common.geom.shapes.flex-layout
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.matrix :as gmt]
|
||||
[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 gsr]
|
||||
[app.common.geom.shapes.transforms :as gst]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.types.modifiers :as ctm]))
|
||||
|
||||
;; :layout ;; :flex, :grid in the future
|
||||
;; :layout-flex-dir ;; :row, :reverse-row, :column, :reverse-column
|
||||
;; :layout-gap-type ;; :simple, :multiple
|
||||
;; :layout-gap ;; {:row-gap number , :column-gap number}
|
||||
;; :layout-align-items ;; :start :end :center :strech
|
||||
;; :layout-justify-content ;; :start :center :end :space-between :space-around
|
||||
;; :layout-align-content ;; :start :center :end :space-between :space-around :strech (by default)
|
||||
;; :layout-wrap-type ;; :wrap, :no-wrap
|
||||
;; :layout-padding-type ;; :simple, :multiple
|
||||
;; :layout-padding ;; {:p1 num :p2 num :p3 num :p4 num} number could be negative
|
||||
|
||||
;; ITEMS
|
||||
;; :layout-margin ;; {:m1 0 :m2 0 :m3 0 :m4 0}
|
||||
;; :layout-margin-type ;; :simple :multiple
|
||||
;; :layout-h-behavior ;; :fill :fix :auto
|
||||
;; :layout-v-behavior ;; :fill :fix :auto
|
||||
;; :layout-max-h ;; num
|
||||
;; :layout-min-h ;; num
|
||||
;; :layout-max-w ;; num
|
||||
;; :layout-min-w
|
||||
|
||||
(defn col?
|
||||
[{:keys [layout-flex-dir]}]
|
||||
(or (= :column layout-flex-dir) (= :reverse-column layout-flex-dir)))
|
||||
|
||||
(defn row?
|
||||
[{:keys [layout-flex-dir]}]
|
||||
(or (= :row layout-flex-dir) (= :reverse-row layout-flex-dir)))
|
||||
|
||||
(defn h-start?
|
||||
[{:keys [layout-align-items layout-justify-content] :as shape}]
|
||||
(or (and (col? shape)
|
||||
(= layout-align-items :start))
|
||||
(and (row? shape)
|
||||
(= layout-justify-content :start))))
|
||||
|
||||
(defn h-center?
|
||||
[{:keys [layout-align-items layout-justify-content] :as shape}]
|
||||
(or (and (col? shape)
|
||||
(= layout-align-items :center))
|
||||
(and (row? shape)
|
||||
(= layout-justify-content :center))))
|
||||
|
||||
(defn h-end?
|
||||
[{:keys [layout-align-items layout-justify-content] :as shape}]
|
||||
(or (and (col? shape)
|
||||
(= layout-align-items :end))
|
||||
(and (row? shape)
|
||||
(= layout-justify-content :end))))
|
||||
|
||||
(defn v-start?
|
||||
[{:keys [layout-align-items layout-justify-content] :as shape}]
|
||||
(or (and (row? shape)
|
||||
(= layout-align-items :start))
|
||||
(and (col? shape)
|
||||
(= layout-justify-content :start))))
|
||||
|
||||
(defn v-center?
|
||||
[{:keys [layout-align-items layout-justify-content] :as shape}]
|
||||
(or (and (row? shape)
|
||||
(= layout-align-items :center))
|
||||
(and (col? shape)
|
||||
(= layout-justify-content :center))))
|
||||
|
||||
(defn v-end?
|
||||
[{:keys [layout-align-items layout-justify-content] :as shape}]
|
||||
(or (and (row? shape)
|
||||
(= layout-align-items :end))
|
||||
(and (col? shape)
|
||||
(= layout-justify-content :end))))
|
||||
|
||||
(defn gaps
|
||||
[{:keys [layout-gap layout-gap-type]}]
|
||||
(let [layout-gap-row (or (-> layout-gap :row-gap) 0)
|
||||
layout-gap-col (if (= layout-gap-type :simple)
|
||||
layout-gap-row
|
||||
(or (-> layout-gap :column-gap) 0))]
|
||||
[layout-gap-row layout-gap-col]))
|
||||
|
||||
(defn calc-layout-lines
|
||||
"Calculates the lines basic data and accumulated values. The positions will be calculated in a different operation"
|
||||
[{:keys [layout-wrap-type] :as parent} children layout-bounds]
|
||||
|
||||
(let [wrap? (= layout-wrap-type :wrap)
|
||||
col? (col? parent)
|
||||
row? (row? parent)
|
||||
|
||||
[layout-gap-row layout-gap-col] (gaps parent)
|
||||
|
||||
layout-width (gpo/width-points layout-bounds)
|
||||
layout-height (gpo/height-points layout-bounds)
|
||||
|
||||
reduce-fn
|
||||
(fn [[{:keys [line-width line-height num-children line-fill? child-fill? num-child-fill] :as line-data} result] child]
|
||||
(let [child-bounds (gst/parent-coords-points child parent)
|
||||
child-width (gpo/width-points child-bounds)
|
||||
child-height (gpo/height-points child-bounds)
|
||||
|
||||
cur-child-fill?
|
||||
(or (and row? (= :fill (:layout-h-behavior child)))
|
||||
(and col? (= :fill (:layout-v-behavior child))))
|
||||
|
||||
cur-line-fill?
|
||||
(or (and col? (= :fill (:layout-h-behavior child)))
|
||||
(and row? (= :fill (:layout-v-behavior child))))
|
||||
|
||||
;; TODO LAYOUT: ADD MINWIDTH/HEIGHT
|
||||
next-width (if (or (and row? cur-child-fill?)
|
||||
(and col? cur-line-fill?))
|
||||
0
|
||||
child-width)
|
||||
|
||||
next-height (if (or (and col? cur-child-fill?)
|
||||
(and row? cur-line-fill?))
|
||||
0
|
||||
child-height)
|
||||
|
||||
next-total-width (+ line-width next-width (* layout-gap-row (dec num-children)))
|
||||
next-total-height (+ line-height next-height (* layout-gap-col (dec num-children)))]
|
||||
|
||||
(if (and (some? line-data)
|
||||
(or (not wrap?)
|
||||
(and row? (<= next-total-width layout-width))
|
||||
(and col? (<= next-total-height layout-height))))
|
||||
|
||||
;; When :fill we add min width (0 by default)
|
||||
[{:line-width (if row? (+ line-width next-width) (max line-width next-width))
|
||||
:line-height (if col? (+ line-height next-height) (max line-height next-height))
|
||||
:num-children (inc num-children)
|
||||
:child-fill? (or cur-child-fill? child-fill?)
|
||||
:line-fill? (or cur-line-fill? line-fill?)
|
||||
:num-child-fill (cond-> num-child-fill cur-child-fill? inc)}
|
||||
result]
|
||||
|
||||
[{:line-width next-width
|
||||
:line-height next-height
|
||||
:num-children 1
|
||||
:child-fill? cur-child-fill?
|
||||
:line-fill? cur-line-fill?
|
||||
:num-child-fill (if cur-child-fill? 1 0)}
|
||||
(cond-> result (some? line-data) (conj line-data))])))
|
||||
|
||||
[line-data layout-lines] (reduce reduce-fn [nil []] children)]
|
||||
|
||||
(cond-> layout-lines (some? line-data) (conj line-data))))
|
||||
|
||||
(defn calc-layout-lines-position
|
||||
[{:keys [layout-justify-content] :as parent} layout-bounds layout-lines]
|
||||
|
||||
(let [layout-width (gpo/width-points layout-bounds)
|
||||
layout-height (gpo/height-points layout-bounds)
|
||||
[layout-gap-row layout-gap-col] (gaps parent)
|
||||
|
||||
row? (row? parent)
|
||||
col? (col? parent)
|
||||
space-between? (= layout-justify-content :space-between)
|
||||
space-around? (= layout-justify-content :space-around)
|
||||
h-center? (h-center? parent)
|
||||
h-end? (h-end? parent)
|
||||
v-center? (v-center? parent)
|
||||
v-end? (v-end? parent)]
|
||||
|
||||
(letfn [;; short version to not repeat always with all arguments
|
||||
(xv [val]
|
||||
(gpo/start-hv layout-bounds val))
|
||||
|
||||
;; short version to not repeat always with all arguments
|
||||
(yv [val]
|
||||
(gpo/start-vv layout-bounds val))
|
||||
|
||||
(get-base-line
|
||||
[total-width total-height]
|
||||
|
||||
(cond-> (gpo/origin layout-bounds)
|
||||
(and col? h-center?)
|
||||
(gpt/add (xv (/ (- layout-width total-width) 2)))
|
||||
|
||||
(and col? h-end?)
|
||||
(gpt/add (xv (- layout-width total-width)))
|
||||
|
||||
(and row? v-center?)
|
||||
(gpt/add (yv (/ (- layout-height total-height) 2)))
|
||||
|
||||
(and row? v-end?)
|
||||
(gpt/add (yv (- layout-height total-height)))))
|
||||
|
||||
(get-start-line
|
||||
[{:keys [line-width line-height num-children child-fill?]} base-p]
|
||||
|
||||
(let [children-gap-width (* layout-gap-row (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
|
||||
(cond-> base-p
|
||||
;; X AXIS
|
||||
(and row? h-center? (not space-around?) (not space-between?))
|
||||
(-> (gpt/add (xv (/ layout-width 2)))
|
||||
(gpt/subtract (xv (/ (+ line-width children-gap-width) 2))))
|
||||
|
||||
(and row? h-end? (not space-around?) (not space-between?))
|
||||
(-> (gpt/add (xv layout-width))
|
||||
(gpt/subtract (xv (+ line-width children-gap-width))))
|
||||
|
||||
(and col? h-center?)
|
||||
(gpt/add (xv (/ line-width 2)))
|
||||
|
||||
(and col? h-end?)
|
||||
(gpt/add (xv line-width))
|
||||
|
||||
;; Y AXIS
|
||||
(and col? v-center? (not space-around?) (not space-between?))
|
||||
(-> (gpt/add (yv (/ layout-height 2)))
|
||||
(gpt/subtract (yv (/ (+ line-height children-gap-height) 2))))
|
||||
|
||||
(and col? v-end? (not space-around?) (not space-between?))
|
||||
(-> (gpt/add (yv layout-height))
|
||||
(gpt/subtract (yv (+ line-height children-gap-height))))
|
||||
|
||||
(and row? v-center?)
|
||||
(gpt/add (yv (/ line-height 2)))
|
||||
|
||||
(and row? v-end?)
|
||||
(gpt/add (yv line-height)))]
|
||||
|
||||
start-p))
|
||||
|
||||
(get-next-line
|
||||
[{:keys [line-width line-height]} base-p]
|
||||
|
||||
(cond-> base-p
|
||||
col?
|
||||
(gpt/add (xv (+ line-width layout-gap-row)))
|
||||
|
||||
row?
|
||||
(gpt/add (yv (+ line-height layout-gap-col)))))
|
||||
|
||||
(add-lines [[total-width total-height] {:keys [line-width line-height]}]
|
||||
[(+ total-width line-width)
|
||||
(+ total-height line-height)])
|
||||
|
||||
(add-starts [[result base-p] layout-line]
|
||||
(let [start-p (get-start-line layout-line base-p)
|
||||
next-p (get-next-line layout-line base-p)]
|
||||
[(conj result
|
||||
(assoc layout-line :start-p start-p))
|
||||
next-p]))]
|
||||
|
||||
(let [[total-width total-height] (->> layout-lines (reduce add-lines [0 0]))
|
||||
|
||||
total-width (+ total-width (* layout-gap-row (dec (count layout-lines))))
|
||||
total-height (+ total-height (* layout-gap-col (dec (count layout-lines))))
|
||||
|
||||
vertical-fill-space (- layout-height total-height)
|
||||
|
||||
horizontal-fill-space (- layout-width total-width)
|
||||
num-line-fill (count (->> layout-lines (filter :line-fill?)))
|
||||
|
||||
layout-lines
|
||||
(->> layout-lines
|
||||
(mapv #(cond-> %
|
||||
(and row? (:line-fill? %))
|
||||
(update :line-height + (/ vertical-fill-space num-line-fill))
|
||||
|
||||
(and col? (:line-fill? %))
|
||||
(update :line-width + (/ horizontal-fill-space num-line-fill)))))
|
||||
|
||||
total-width (if (and col? (> num-line-fill 0)) layout-width total-width)
|
||||
total-height (if (and row? (> num-line-fill 0)) layout-height total-height)
|
||||
|
||||
base-p (get-base-line total-width total-height)
|
||||
|
||||
[layout-lines _ _ _ _]
|
||||
(reduce add-starts [[] base-p] layout-lines)]
|
||||
layout-lines))))
|
||||
|
||||
(defn calc-layout-line-data
|
||||
"Calculates the baseline for a flex layout"
|
||||
[{:keys [layout-justify-content] :as shape}
|
||||
layout-bounds
|
||||
{:keys [num-children line-width line-height] :as line-data}]
|
||||
|
||||
(let [width (gpo/width-points layout-bounds)
|
||||
height (gpo/height-points layout-bounds)
|
||||
|
||||
row? (row? shape)
|
||||
col? (col? shape)
|
||||
space-between? (= layout-justify-content :space-between)
|
||||
space-around? (= layout-justify-content :space-around)
|
||||
|
||||
[layout-gap-row layout-gap-col] (gaps shape)
|
||||
|
||||
layout-gap-row
|
||||
(cond (and row? space-around?)
|
||||
0
|
||||
|
||||
(and row? space-between?)
|
||||
(/ (- width line-width) (dec num-children))
|
||||
|
||||
:else
|
||||
layout-gap-row)
|
||||
|
||||
layout-gap-col
|
||||
(cond (and col? space-around?)
|
||||
0
|
||||
|
||||
(and col? space-between?)
|
||||
(/ (- height line-height) (dec num-children))
|
||||
|
||||
:else
|
||||
layout-gap-col)
|
||||
|
||||
margin-x
|
||||
(if (and row? space-around?)
|
||||
(/ (- width line-width) (inc num-children))
|
||||
0)
|
||||
|
||||
margin-y
|
||||
(if (and col? space-around?)
|
||||
(/ (- height line-height) (inc num-children))
|
||||
0)]
|
||||
|
||||
(assoc line-data
|
||||
:layout-bounds layout-bounds
|
||||
:layout-gap-row layout-gap-row
|
||||
:layout-gap-col layout-gap-col
|
||||
:margin-x margin-x
|
||||
:margin-y margin-y)))
|
||||
|
||||
(defn next-p
|
||||
"Calculates the position for the current shape given the layout-data context"
|
||||
[parent
|
||||
child-width child-height
|
||||
{:keys [start-p layout-gap-row layout-gap-col margin-x margin-y] :as layout-data}]
|
||||
|
||||
(let [row? (row? parent)
|
||||
col? (col? parent)
|
||||
|
||||
h-center? (h-center? parent)
|
||||
h-end? (h-end? parent)
|
||||
v-center? (v-center? parent)
|
||||
v-end? (v-end? parent)
|
||||
points (:points parent)
|
||||
|
||||
xv (partial gpo/start-hv points)
|
||||
yv (partial gpo/start-vv points)
|
||||
|
||||
corner-p
|
||||
(cond-> start-p
|
||||
(and col? h-center?)
|
||||
(gpt/add (xv (- (/ child-width 2))))
|
||||
|
||||
(and col? h-end?)
|
||||
(gpt/add (xv (- child-width)))
|
||||
|
||||
(and row? v-center?)
|
||||
(gpt/add (yv (- (/ child-height 2))))
|
||||
|
||||
(and row? v-end?)
|
||||
(gpt/add (yv (- child-height)))
|
||||
|
||||
(some? margin-x)
|
||||
(gpt/add (xv margin-x))
|
||||
|
||||
(some? margin-y)
|
||||
(gpt/add (yv margin-y)))
|
||||
|
||||
next-p
|
||||
(cond-> start-p
|
||||
row?
|
||||
(gpt/add (xv (+ child-width layout-gap-row)))
|
||||
|
||||
col?
|
||||
(gpt/add (yv (+ child-height layout-gap-col)))
|
||||
|
||||
(some? margin-x)
|
||||
(gpt/add (xv margin-x))
|
||||
|
||||
(some? margin-y)
|
||||
(gpt/add (yv margin-y)))
|
||||
|
||||
layout-data
|
||||
(assoc layout-data :start-p next-p)]
|
||||
|
||||
[corner-p layout-data]))
|
||||
|
||||
(defn calc-fill-width-data
|
||||
"Calculates the size and modifiers for the width of an auto-fill child"
|
||||
[{:keys [transform transform-inverse] :as parent}
|
||||
{:keys [layout-h-behavior] :as child}
|
||||
child-origin child-width
|
||||
{:keys [num-children line-width line-fill? child-fill? layout-bounds] :as layout-data}]
|
||||
|
||||
(let [[layout-gap-row _] (gaps parent)]
|
||||
(cond
|
||||
(and (row? parent) (= :fill layout-h-behavior) child-fill?)
|
||||
(let [layout-width (gpo/width-points layout-bounds)
|
||||
fill-space (- layout-width line-width (* layout-gap-row (dec num-children)))
|
||||
fill-width (/ fill-space (:num-child-fill layout-data))
|
||||
fill-scale (/ fill-width child-width)]
|
||||
|
||||
{:width fill-width
|
||||
:modifiers (ctm/resize (gpt/point fill-scale 1) child-origin transform transform-inverse)})
|
||||
|
||||
(and (col? parent) (= :fill layout-h-behavior) line-fill?)
|
||||
(let [fill-scale (/ line-width child-width)]
|
||||
{:width line-width
|
||||
:modifiers (ctm/resize (gpt/point fill-scale 1) child-origin transform transform-inverse)}))))
|
||||
|
||||
(defn calc-fill-height-data
|
||||
"Calculates the size and modifiers for the height of an auto-fill child"
|
||||
[{:keys [transform transform-inverse] :as parent}
|
||||
{:keys [layout-v-behavior] :as child}
|
||||
child-origin child-height
|
||||
{:keys [num-children line-height layout-bounds line-fill? child-fill?] :as layout-data}]
|
||||
|
||||
(let [[_ layout-gap-col] (gaps parent)]
|
||||
(cond
|
||||
(and (col? parent) (= :fill layout-v-behavior) child-fill?)
|
||||
(let [layout-height (gpo/height-points layout-bounds)
|
||||
fill-space (- layout-height line-height (* layout-gap-col (dec num-children)))
|
||||
fill-height (/ fill-space (:num-child-fill layout-data))
|
||||
fill-scale (/ fill-height child-height)]
|
||||
{:height fill-height
|
||||
:modifiers (ctm/resize (gpt/point 1 fill-scale) child-origin transform transform-inverse)})
|
||||
|
||||
(and (row? parent) (= :fill layout-v-behavior) line-fill?)
|
||||
(let [fill-scale (/ line-height child-height)]
|
||||
{:height line-height
|
||||
:modifiers (ctm/resize (gpt/point 1 fill-scale) child-origin transform transform-inverse)}))))
|
||||
|
||||
(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}]
|
||||
|
||||
(let [transformed-child (gst/transform-shape child modifiers)
|
||||
child-bb-before (gst/parent-coords-rect child parent)
|
||||
child-bb-after (gst/parent-coords-rect transformed-child transformed-parent)
|
||||
scale-x (/ (:width child-bb-before) (:width child-bb-after))
|
||||
scale-y (/ (:height child-bb-before) (:height child-bb-after))
|
||||
|
||||
resize-origin (-> transformed-parent :points first) ;; TODO LAYOUT: IS always the origin?n
|
||||
resize-vector (gpt/point scale-x scale-y)]
|
||||
(-> modifiers
|
||||
(ctm/select-child-modifiers)
|
||||
(ctm/set-resize resize-vector resize-origin transform transform-inverse))))
|
||||
|
||||
(defn calc-layout-data
|
||||
"Digest the layout data to pass it to the constrains"
|
||||
[{:keys [layout-flex-dir layout-padding layout-padding-type] :as parent} children]
|
||||
|
||||
(let [;; Add padding to the bounds
|
||||
{pad-top :p1 pad-right :p2 pad-bottom :p3 pad-left :p4} layout-padding
|
||||
[pad-top pad-right pad-bottom pad-left]
|
||||
(if (= layout-padding-type :multiple)
|
||||
[pad-top pad-right pad-bottom pad-left]
|
||||
[pad-top pad-top pad-top pad-top])
|
||||
|
||||
;; Normalize the points to remove flips
|
||||
points (gst/parent-coords-points parent parent)
|
||||
|
||||
layout-bounds (gpo/pad-points points pad-top pad-right pad-bottom pad-left)
|
||||
|
||||
;; Reverse
|
||||
reverse? (or (= :reverse-row layout-flex-dir) (= :reverse-column layout-flex-dir))
|
||||
children (cond->> children reverse? reverse)
|
||||
|
||||
;; Creates the layout lines information
|
||||
layout-lines
|
||||
(->> (calc-layout-lines parent children layout-bounds)
|
||||
(calc-layout-lines-position parent layout-bounds)
|
||||
(map (partial calc-layout-line-data parent layout-bounds)))]
|
||||
|
||||
{:layout-lines layout-lines
|
||||
:reverse? reverse?}))
|
||||
|
||||
(defn calc-layout-modifiers
|
||||
"Calculates the modifiers for the layout"
|
||||
[parent child layout-line]
|
||||
(let [child-bounds (gst/parent-coords-points child parent)
|
||||
|
||||
child-origin (gpo/origin child-bounds)
|
||||
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)
|
||||
|
||||
child-width (or (:width fill-width) child-width)
|
||||
child-height (or (:height fill-height) child-height)
|
||||
|
||||
[corner-p layout-line] (next-p parent child-width child-height layout-line)
|
||||
|
||||
move-vec (gpt/to-vec child-origin corner-p)
|
||||
|
||||
modifiers
|
||||
(-> (ctm/empty-modifiers)
|
||||
(cond-> fill-width (ctm/add-modifiers (:modifiers fill-width)))
|
||||
(cond-> fill-height (ctm/add-modifiers (:modifiers fill-height)))
|
||||
(ctm/set-move move-vec))]
|
||||
|
||||
[modifiers layout-line]))
|
||||
|
||||
|
||||
(defn layout-drop-areas
|
||||
[{:keys [margin-x margin-y] :as frame} layout-data children]
|
||||
|
||||
(let [col? (col? frame)
|
||||
row? (row? frame)
|
||||
h-center? (and row? (h-center? frame))
|
||||
h-end? (and row? (h-end? frame))
|
||||
v-center? (and col? (v-center? frame))
|
||||
v-end? (and row? (v-end? frame))
|
||||
layout-gap-row (or (-> frame :layout-gap :row-gap) 0)
|
||||
;;layout-gap-col (or (-> frame :layout-gap :column-gap) 0)
|
||||
layout-gap layout-gap-row ;; TODO LAYOUT: FIXME
|
||||
reverse? (:reverse? layout-data)
|
||||
|
||||
children (vec (cond->> (d/enumerate children)
|
||||
reverse? reverse))
|
||||
|
||||
redfn-child
|
||||
(fn [[result parent-rect prev-x prev-y] [[index child] next]]
|
||||
(let [prev-x (or prev-x (:x parent-rect))
|
||||
prev-y (or prev-y (:y parent-rect))
|
||||
|
||||
last? (nil? next)
|
||||
|
||||
start-p (gpt/point (:selrect child))
|
||||
start-p (-> start-p
|
||||
(gmt/transform-point-center (gco/center-shape child) (:transform frame))
|
||||
(gmt/transform-point-center (gco/center-shape frame) (:transform-inverse frame)))
|
||||
|
||||
box-x (:x start-p)
|
||||
box-y (:y start-p)
|
||||
box-width (-> child :selrect :width)
|
||||
box-height (-> child :selrect :height)
|
||||
|
||||
x (if col? (:x parent-rect) prev-x)
|
||||
y (if row? (:y parent-rect) prev-y)
|
||||
|
||||
width (cond
|
||||
(and row? last?)
|
||||
(- (+ (:x parent-rect) (:width parent-rect)) x)
|
||||
|
||||
col?
|
||||
(:width parent-rect)
|
||||
|
||||
:else
|
||||
(+ box-width (- box-x prev-x) (/ layout-gap 2)))
|
||||
|
||||
height (cond
|
||||
(and col? last?)
|
||||
(- (+ (:y parent-rect) (:height parent-rect)) y)
|
||||
|
||||
row?
|
||||
(:height parent-rect)
|
||||
|
||||
:else
|
||||
(+ box-height (- box-y prev-y) (/ layout-gap 2)))
|
||||
|
||||
[line-area-1 line-area-2]
|
||||
(if row?
|
||||
(let [half-point-width (+ (- box-x x) (/ box-width 2))]
|
||||
[(-> (gsr/make-rect x y half-point-width height)
|
||||
(assoc :index (if reverse? (inc index) index)))
|
||||
(-> (gsr/make-rect (+ x half-point-width) y (- width half-point-width) height)
|
||||
(assoc :index (if reverse? index (inc index))))])
|
||||
(let [half-point-height (+ (- box-y y) (/ box-height 2))]
|
||||
[(-> (gsr/make-rect x y width half-point-height)
|
||||
(assoc :index (if reverse? (inc index) index)))
|
||||
(-> (gsr/make-rect x (+ y half-point-height) width (- height half-point-height))
|
||||
(assoc :index (if reverse? index (inc index))))]))
|
||||
|
||||
result (conj result line-area-1 line-area-2)
|
||||
|
||||
;;line-area
|
||||
;;(-> (gsr/make-rect x y width height)
|
||||
;; (assoc :index (if reverse? (inc index) index)))
|
||||
;;result (conj result line-area)
|
||||
;;result (conj result (gsr/make-rect box-x box-y box-width box-height))
|
||||
]
|
||||
|
||||
[result parent-rect (+ x width) (+ y height)]))
|
||||
|
||||
redfn-lines
|
||||
(fn [[result from-idx prev-x prev-y] [{:keys [start-p layout-gap num-children line-width line-height]} next]]
|
||||
(let [start-p (gmt/transform-point-center start-p (gco/center-shape frame) (:transform-inverse frame))
|
||||
|
||||
prev-x (or prev-x (:x frame))
|
||||
prev-y (or prev-y (:y frame))
|
||||
last? (nil? next)
|
||||
|
||||
line-width
|
||||
(if row?
|
||||
(:width frame)
|
||||
(+ line-width margin-x
|
||||
(if row? (* layout-gap (dec num-children)) 0)))
|
||||
|
||||
line-height
|
||||
(if col?
|
||||
(:height frame)
|
||||
(+ line-height margin-y
|
||||
(if col?
|
||||
(* layout-gap (dec num-children))
|
||||
0)))
|
||||
|
||||
box-x
|
||||
(- (:x start-p)
|
||||
(cond
|
||||
h-center? (/ line-width 2)
|
||||
h-end? line-width
|
||||
:else 0))
|
||||
|
||||
box-y
|
||||
(- (:y start-p)
|
||||
(cond
|
||||
v-center? (/ line-height 2)
|
||||
v-end? line-height
|
||||
:else 0))
|
||||
|
||||
x (if row? (:x frame) prev-x)
|
||||
y (if col? (:y frame) prev-y)
|
||||
|
||||
width (cond
|
||||
(and col? last?)
|
||||
(- (+ (:x frame) (:width frame)) x)
|
||||
|
||||
row?
|
||||
(:width frame)
|
||||
|
||||
:else
|
||||
(+ line-width (- box-x prev-x) (/ layout-gap 2)))
|
||||
|
||||
height (cond
|
||||
(and row? last?)
|
||||
(- (+ (:y frame) (:height frame)) y)
|
||||
|
||||
col?
|
||||
(:height frame)
|
||||
|
||||
:else
|
||||
(+ line-height (- box-y prev-y) (/ layout-gap 2)))
|
||||
|
||||
line-area (gsr/make-rect x y width height)
|
||||
|
||||
children (subvec children from-idx (+ from-idx num-children))
|
||||
|
||||
|
||||
;; To debug the lines
|
||||
;;result (conj result line-area)
|
||||
|
||||
result (first (reduce redfn-child [result line-area] (d/with-next children)))]
|
||||
|
||||
[result (+ from-idx num-children) (+ x width) (+ y height)]))]
|
||||
|
||||
(first (reduce redfn-lines [[] 0] (d/with-next (:layout-lines layout-data))))))
|
||||
|
||||
(defn get-drop-index
|
||||
[frame-id objects position]
|
||||
(let [frame (get objects frame-id)
|
||||
position (gmt/transform-point-center position (gco/center-shape frame) (:transform-inverse frame))
|
||||
children (cph/get-immediate-children objects frame-id)
|
||||
layout-data (calc-layout-data frame children)
|
||||
drop-areas (layout-drop-areas frame layout-data children)
|
||||
area (d/seek #(gsr/contains-point? % position) drop-areas)]
|
||||
(:index area)))
|
||||
[app.common.data.macros :as dm]
|
||||
[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 fdr/get-drop-index)
|
||||
|
|
179
common/src/app/common/geom/shapes/flex_layout/drop_area.cljc
Normal file
179
common/src/app/common/geom/shapes/flex_layout/drop_area.cljc
Normal file
|
@ -0,0 +1,179 @@
|
|||
;; 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.drop-area
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.matrix :as gmt]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes.common :as gco]
|
||||
[app.common.geom.shapes.flex-layout.lines :as fli]
|
||||
[app.common.geom.shapes.rect :as gsr]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.types.shape.layout :as ctl]))
|
||||
|
||||
(defn layout-drop-areas
|
||||
[{:keys [margin-x margin-y] :as frame} layout-data children]
|
||||
|
||||
(let [col? (ctl/col? frame)
|
||||
row? (ctl/row? frame)
|
||||
h-center? (and row? (ctl/h-center? frame))
|
||||
h-end? (and row? (ctl/h-end? frame))
|
||||
v-center? (and col? (ctl/v-center? frame))
|
||||
v-end? (and row? (ctl/v-end? frame))
|
||||
reverse? (:reverse? layout-data)
|
||||
|
||||
[layout-gap-row layout-gap-col] (ctl/gaps frame)
|
||||
|
||||
children (vec (cond->> (d/enumerate children)
|
||||
reverse? reverse))
|
||||
|
||||
redfn-child
|
||||
(fn [[result parent-rect prev-x prev-y] [[index child] next]]
|
||||
(let [prev-x (or prev-x (:x parent-rect))
|
||||
prev-y (or prev-y (:y parent-rect))
|
||||
|
||||
last? (nil? next)
|
||||
|
||||
start-p (gpt/point (:selrect child))
|
||||
start-p (-> start-p
|
||||
(gmt/transform-point-center (gco/center-shape child) (:transform frame))
|
||||
(gmt/transform-point-center (gco/center-shape frame) (:transform-inverse frame)))
|
||||
|
||||
box-x (:x start-p)
|
||||
box-y (:y start-p)
|
||||
box-width (-> child :selrect :width)
|
||||
box-height (-> child :selrect :height)
|
||||
|
||||
x (if col? (:x parent-rect) prev-x)
|
||||
y (if row? (:y parent-rect) prev-y)
|
||||
|
||||
width (cond
|
||||
(and row? last?)
|
||||
(- (+ (:x parent-rect) (:width parent-rect)) x)
|
||||
|
||||
col?
|
||||
(:width parent-rect)
|
||||
|
||||
:else
|
||||
(+ box-width (- box-x prev-x) (/ layout-gap-row 2)))
|
||||
|
||||
height (cond
|
||||
(and col? last?)
|
||||
(- (+ (:y parent-rect) (:height parent-rect)) y)
|
||||
|
||||
row?
|
||||
(:height parent-rect)
|
||||
|
||||
:else
|
||||
(+ box-height (- box-y prev-y) (/ layout-gap-col 2)))
|
||||
|
||||
[line-area-1 line-area-2]
|
||||
(if row?
|
||||
(let [half-point-width (+ (- box-x x) (/ box-width 2))]
|
||||
[(-> (gsr/make-rect x y half-point-width height)
|
||||
(assoc :index (if reverse? (inc index) index)))
|
||||
(-> (gsr/make-rect (+ x half-point-width) y (- width half-point-width) height)
|
||||
(assoc :index (if reverse? index (inc index))))])
|
||||
(let [half-point-height (+ (- box-y y) (/ box-height 2))]
|
||||
[(-> (gsr/make-rect x y width half-point-height)
|
||||
(assoc :index (if reverse? (inc index) index)))
|
||||
(-> (gsr/make-rect x (+ y half-point-height) width (- height half-point-height))
|
||||
(assoc :index (if reverse? index (inc index))))]))
|
||||
|
||||
result (conj result line-area-1 line-area-2)
|
||||
|
||||
;;line-area
|
||||
;;(-> (gsr/make-rect x y width height)
|
||||
;; (assoc :index (if reverse? (inc index) index)))
|
||||
;;result (conj result line-area)
|
||||
;;result (conj result (gsr/make-rect box-x box-y box-width box-height))
|
||||
]
|
||||
|
||||
[result parent-rect (+ x width) (+ y height)]))
|
||||
|
||||
redfn-lines
|
||||
(fn [[result from-idx prev-x prev-y] [{:keys [start-p layout-gap-row layout-gap-col num-children line-width line-height]} next]]
|
||||
(let [start-p (gmt/transform-point-center start-p (gco/center-shape frame) (:transform-inverse frame))
|
||||
|
||||
prev-x (or prev-x (:x frame))
|
||||
prev-y (or prev-y (:y frame))
|
||||
last? (nil? next)
|
||||
|
||||
line-width
|
||||
(if row?
|
||||
(:width frame)
|
||||
(+ line-width margin-x
|
||||
(if row? (* layout-gap-row (dec num-children)) 0)))
|
||||
|
||||
line-height
|
||||
(if col?
|
||||
(:height frame)
|
||||
(+ line-height margin-y
|
||||
(if col?
|
||||
(* layout-gap-col (dec num-children))
|
||||
0)))
|
||||
|
||||
box-x
|
||||
(- (:x start-p)
|
||||
(cond
|
||||
h-center? (/ line-width 2)
|
||||
h-end? line-width
|
||||
:else 0))
|
||||
|
||||
box-y
|
||||
(- (:y start-p)
|
||||
(cond
|
||||
v-center? (/ line-height 2)
|
||||
v-end? line-height
|
||||
:else 0))
|
||||
|
||||
x (if row? (:x frame) prev-x)
|
||||
y (if col? (:y frame) prev-y)
|
||||
|
||||
width (cond
|
||||
(and col? last?)
|
||||
(- (+ (:x frame) (:width frame)) x)
|
||||
|
||||
row?
|
||||
(:width frame)
|
||||
|
||||
:else
|
||||
(+ line-width (- box-x prev-x) (/ layout-gap-row 2)))
|
||||
|
||||
height (cond
|
||||
(and row? last?)
|
||||
(- (+ (:y frame) (:height frame)) y)
|
||||
|
||||
col?
|
||||
(:height frame)
|
||||
|
||||
:else
|
||||
(+ line-height (- box-y prev-y) (/ layout-gap-col 2)))
|
||||
|
||||
line-area (gsr/make-rect x y width height)
|
||||
|
||||
children (subvec children from-idx (+ from-idx num-children))
|
||||
|
||||
|
||||
;; To debug the lines
|
||||
;;result (conj result line-area)
|
||||
|
||||
result (first (reduce redfn-child [result line-area] (d/with-next children)))]
|
||||
|
||||
[result (+ from-idx num-children) (+ x width) (+ y height)]))]
|
||||
|
||||
(first (reduce redfn-lines [[] 0] (d/with-next (:layout-lines layout-data))))))
|
||||
|
||||
(defn get-drop-index
|
||||
[frame-id objects position]
|
||||
(let [frame (get objects frame-id)
|
||||
position (gmt/transform-point-center position (gco/center-shape frame) (:transform-inverse frame))
|
||||
children (cph/get-immediate-children objects frame-id)
|
||||
layout-data (fli/calc-layout-data frame children)
|
||||
drop-areas (layout-drop-areas frame layout-data children)
|
||||
area (d/seek #(gsr/contains-point? % position) drop-areas)]
|
||||
(:index area)))
|
309
common/src/app/common/geom/shapes/flex_layout/lines.cljc
Normal file
309
common/src/app/common/geom/shapes/flex_layout/lines.cljc
Normal file
|
@ -0,0 +1,309 @@
|
|||
;; 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.lines
|
||||
(:require
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes.points :as gpo]
|
||||
[app.common.geom.shapes.transforms :as gst]
|
||||
[app.common.types.shape.layout :as ctl]))
|
||||
|
||||
(defn layout-bounds
|
||||
[{:keys [layout-padding layout-padding-type] :as shape}]
|
||||
(let [;; Add padding to the bounds
|
||||
{pad-top :p1 pad-right :p2 pad-bottom :p3 pad-left :p4} layout-padding
|
||||
[pad-top pad-right pad-bottom pad-left]
|
||||
(if (= layout-padding-type :multiple)
|
||||
[pad-top pad-right pad-bottom pad-left]
|
||||
[pad-top pad-top pad-top pad-top])
|
||||
|
||||
;; Normalize the points to remove flips
|
||||
;; TODO LAYOUT: Need function to normalize the points
|
||||
points (gst/parent-coords-points shape shape)]
|
||||
|
||||
(gpo/pad-points points pad-top pad-right pad-bottom pad-left)))
|
||||
|
||||
(defn init-layout-lines
|
||||
"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)
|
||||
row? (ctl/row? shape)
|
||||
|
||||
[layout-gap-row layout-gap-col] (ctl/gaps shape)
|
||||
layout-width (gpo/width-points layout-bounds)
|
||||
layout-height (gpo/height-points layout-bounds)
|
||||
|
||||
reduce-fn
|
||||
(fn [[{:keys [line-width line-height num-children line-fill? child-fill? num-child-fill] :as line-data} result] child]
|
||||
(let [child-bounds (gst/parent-coords-points child shape)
|
||||
child-width (gpo/width-points child-bounds)
|
||||
child-height (gpo/height-points child-bounds)
|
||||
child-min-width (ctl/child-min-width child)
|
||||
child-min-height (ctl/child-min-height child)
|
||||
|
||||
fill-width? (ctl/fill-width? child)
|
||||
fill-height? (ctl/fill-height? child)
|
||||
|
||||
cur-child-fill? (or (and row? fill-width?) (and col? fill-height?))
|
||||
cur-line-fill? (or (and col? fill-width?) (and row? fill-height?))
|
||||
|
||||
next-width (if fill-width? child-min-width child-width)
|
||||
next-height (if fill-height? child-min-height child-height)
|
||||
|
||||
next-line-width (+ line-width next-width (* layout-gap-row (dec num-children)))
|
||||
next-line-height (+ line-height next-height (* layout-gap-col (dec num-children)))]
|
||||
|
||||
(if (and (some? line-data)
|
||||
(or (not wrap?)
|
||||
(and row? (<= next-line-width layout-width))
|
||||
(and col? (<= next-line-height layout-height))))
|
||||
|
||||
[{:line-width (if row? (+ line-width next-width) (max line-width next-width))
|
||||
:line-height (if col? (+ line-height next-height) (max line-height next-height))
|
||||
:num-children (inc num-children)
|
||||
:child-fill? (or cur-child-fill? child-fill?)
|
||||
:line-fill? (or cur-line-fill? line-fill?)
|
||||
:num-child-fill (cond-> num-child-fill cur-child-fill? inc)}
|
||||
result]
|
||||
|
||||
[{:line-width next-width
|
||||
:line-height next-height
|
||||
:num-children 1
|
||||
:child-fill? cur-child-fill?
|
||||
:line-fill? cur-line-fill?
|
||||
:num-child-fill (if cur-child-fill? 1 0)}
|
||||
(cond-> result (some? line-data) (conj line-data))])))
|
||||
|
||||
[line-data layout-lines] (reduce reduce-fn [nil []] children)]
|
||||
|
||||
(cond-> layout-lines (some? line-data) (conj line-data))))
|
||||
|
||||
(defn get-base-line
|
||||
[parent layout-bounds total-width total-height]
|
||||
|
||||
(let [layout-width (gpo/width-points layout-bounds)
|
||||
layout-height (gpo/height-points layout-bounds)
|
||||
row? (ctl/row? parent)
|
||||
col? (ctl/col? parent)
|
||||
h-center? (ctl/h-center? parent)
|
||||
h-end? (ctl/h-end? parent)
|
||||
v-center? (ctl/v-center? parent)
|
||||
v-end? (ctl/v-end? parent)
|
||||
hv (partial gpo/start-hv layout-bounds)
|
||||
vv (partial gpo/start-vv 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?)
|
||||
(gpt/add (vv (/ (- layout-height total-height) 2)))
|
||||
|
||||
(and row? v-end?)
|
||||
(gpt/add (vv (- layout-height total-height))))))
|
||||
|
||||
(defn get-next-line
|
||||
[parent layout-bounds {:keys [line-width line-height]} base-p]
|
||||
|
||||
(let [row? (ctl/row? parent)
|
||||
col? (ctl/col? parent)
|
||||
|
||||
[layout-gap-row layout-gap-col] (ctl/gaps parent)
|
||||
|
||||
hv #(gpo/start-hv layout-bounds %)
|
||||
vv #(gpo/start-vv layout-bounds %)]
|
||||
|
||||
(cond-> base-p
|
||||
col?
|
||||
(gpt/add (hv (+ line-width layout-gap-row)))
|
||||
|
||||
row?
|
||||
(gpt/add (vv (+ line-height layout-gap-col))))))
|
||||
|
||||
(defn get-start-line
|
||||
[parent layout-bounds {:keys [line-width line-height num-children child-fill? ]} base-p]
|
||||
|
||||
(let [layout-width (gpo/width-points layout-bounds)
|
||||
layout-height (gpo/height-points layout-bounds)
|
||||
[layout-gap-row layout-gap-col] (ctl/gaps parent)
|
||||
|
||||
row? (ctl/row? parent)
|
||||
col? (ctl/col? parent)
|
||||
space-between? (ctl/space-between? parent)
|
||||
space-around? (ctl/space-around? parent)
|
||||
h-center? (ctl/h-center? parent)
|
||||
h-end? (ctl/h-end? parent)
|
||||
v-center? (ctl/v-center? parent)
|
||||
v-end? (ctl/v-end? parent)
|
||||
|
||||
hv #(gpo/start-hv layout-bounds %)
|
||||
vv #(gpo/start-vv layout-bounds %)
|
||||
|
||||
children-gap-width (* layout-gap-row (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
|
||||
(cond-> base-p
|
||||
;; X AXIS
|
||||
(and row? h-center? (not space-around?) (not space-between?))
|
||||
(-> (gpt/add (hv (/ layout-width 2)))
|
||||
(gpt/subtract (hv (/ (+ line-width children-gap-width) 2))))
|
||||
|
||||
(and row? h-end? (not space-around?) (not space-between?))
|
||||
(-> (gpt/add (hv layout-width))
|
||||
(gpt/subtract (hv (+ line-width children-gap-width))))
|
||||
|
||||
(and col? h-center?)
|
||||
(gpt/add (hv (/ line-width 2)))
|
||||
|
||||
(and col? h-end?)
|
||||
(gpt/add (hv line-width))
|
||||
|
||||
;; Y AXIS
|
||||
(and col? v-center? (not space-around?) (not space-between?))
|
||||
(-> (gpt/add (vv (/ layout-height 2)))
|
||||
(gpt/subtract (vv (/ (+ line-height children-gap-height) 2))))
|
||||
|
||||
(and col? v-end? (not space-around?) (not space-between?))
|
||||
(-> (gpt/add (vv layout-height))
|
||||
(gpt/subtract (vv (+ line-height children-gap-height))))
|
||||
|
||||
(and row? v-center?)
|
||||
(gpt/add (vv (/ line-height 2)))
|
||||
|
||||
(and row? v-end?)
|
||||
(gpt/add (vv line-height)))]
|
||||
|
||||
start-p))
|
||||
|
||||
|
||||
(defn add-lines-positions
|
||||
[parent layout-bounds layout-lines]
|
||||
|
||||
(let [layout-width (gpo/width-points layout-bounds)
|
||||
layout-height (gpo/height-points layout-bounds)
|
||||
[layout-gap-row layout-gap-col] (ctl/gaps parent)
|
||||
|
||||
row? (ctl/row? parent)
|
||||
col? (ctl/col? parent)]
|
||||
|
||||
(letfn [(add-lines [[total-width total-height] {:keys [line-width line-height]}]
|
||||
[(+ total-width line-width)
|
||||
(+ total-height line-height)])
|
||||
|
||||
(add-starts [[result base-p] layout-line]
|
||||
(let [start-p (get-start-line parent layout-bounds layout-line base-p)
|
||||
next-p (get-next-line parent layout-bounds layout-line base-p)]
|
||||
|
||||
[(conj result
|
||||
(assoc layout-line :start-p start-p))
|
||||
next-p]))]
|
||||
|
||||
(let [[total-width total-height] (->> layout-lines (reduce add-lines [0 0]))
|
||||
|
||||
total-width (+ total-width (* layout-gap-row (dec (count layout-lines))))
|
||||
total-height (+ total-height (* layout-gap-col (dec (count layout-lines))))
|
||||
|
||||
vertical-fill-space (- layout-height total-height)
|
||||
horizontal-fill-space (- layout-width total-width)
|
||||
|
||||
num-line-fill (count (->> layout-lines (filter :line-fill?)))
|
||||
|
||||
layout-lines
|
||||
(->> layout-lines
|
||||
(mapv #(cond-> %
|
||||
(and row? (:line-fill? %))
|
||||
(update :line-height + (/ vertical-fill-space num-line-fill))
|
||||
|
||||
(and col? (:line-fill? %))
|
||||
(update :line-width + (/ horizontal-fill-space num-line-fill)))))
|
||||
|
||||
total-width (if (and col? (> num-line-fill 0)) layout-width total-width)
|
||||
total-height (if (and row? (> num-line-fill 0)) layout-height total-height)
|
||||
|
||||
base-p (get-base-line parent layout-bounds total-width total-height)]
|
||||
|
||||
(first (reduce add-starts [[] base-p] layout-lines))))))
|
||||
|
||||
(defn add-line-spacing
|
||||
"Calculates the baseline for a flex layout"
|
||||
[shape layout-bounds {:keys [num-children line-width line-height] :as line-data}]
|
||||
|
||||
(let [width (gpo/width-points layout-bounds)
|
||||
height (gpo/height-points layout-bounds)
|
||||
|
||||
row? (ctl/row? shape)
|
||||
col? (ctl/col? shape)
|
||||
space-between? (ctl/space-between? shape)
|
||||
space-around? (ctl/space-around? shape)
|
||||
|
||||
[layout-gap-row layout-gap-col] (ctl/gaps shape)
|
||||
|
||||
layout-gap-row
|
||||
(cond (and row? space-around?)
|
||||
0
|
||||
|
||||
(and row? space-between?)
|
||||
(/ (- width line-width) (dec num-children))
|
||||
|
||||
:else
|
||||
layout-gap-row)
|
||||
|
||||
layout-gap-col
|
||||
(cond (and col? space-around?)
|
||||
0
|
||||
|
||||
(and col? space-between?)
|
||||
(/ (- height line-height) (dec num-children))
|
||||
|
||||
:else
|
||||
layout-gap-col)
|
||||
|
||||
margin-x
|
||||
(if (and row? space-around?)
|
||||
(/ (- width line-width) (inc num-children))
|
||||
0)
|
||||
|
||||
margin-y
|
||||
(if (and col? space-around?)
|
||||
(/ (- height line-height) (inc num-children))
|
||||
0)]
|
||||
(assoc line-data
|
||||
:layout-bounds layout-bounds
|
||||
:layout-gap-row layout-gap-row
|
||||
:layout-gap-col layout-gap-col
|
||||
:margin-x margin-x
|
||||
:margin-y margin-y)))
|
||||
|
||||
(defn calc-layout-data
|
||||
"Digest the layout data to pass it to the constrains"
|
||||
[shape children]
|
||||
|
||||
(let [layout-bounds (layout-bounds shape)
|
||||
reverse? (ctl/reverse? shape)
|
||||
children (cond->> children reverse? reverse)
|
||||
|
||||
;; Creates the layout lines information
|
||||
layout-lines
|
||||
(->> (init-layout-lines shape children layout-bounds)
|
||||
(add-lines-positions shape layout-bounds)
|
||||
(mapv (partial add-line-spacing shape layout-bounds)))]
|
||||
|
||||
{:layout-lines layout-lines
|
||||
:reverse? reverse?}))
|
103
common/src/app/common/geom/shapes/flex_layout/modifiers.cljc
Normal file
103
common/src/app/common/geom/shapes/flex_layout/modifiers.cljc
Normal file
|
@ -0,0 +1,103 @@
|
|||
;; 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.modifiers
|
||||
(:require
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes.flex-layout.positions :as fpo]
|
||||
[app.common.geom.shapes.points :as gpo]
|
||||
[app.common.geom.shapes.transforms :as gst]
|
||||
[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}]
|
||||
|
||||
(let [transformed-child (gst/transform-shape child modifiers)
|
||||
child-bb-before (gst/parent-coords-rect child parent)
|
||||
child-bb-after (gst/parent-coords-rect transformed-child transformed-parent)
|
||||
scale-x (/ (:width child-bb-before) (:width child-bb-after))
|
||||
scale-y (/ (:height child-bb-before) (:height child-bb-after))
|
||||
|
||||
resize-origin (-> transformed-parent :points first) ;; TODO LAYOUT: IS always the origin?n
|
||||
resize-vector (gpt/point scale-x scale-y)]
|
||||
(-> modifiers
|
||||
(ctm/select-child-modifiers)
|
||||
(ctm/set-resize resize-vector resize-origin transform transform-inverse))))
|
||||
|
||||
(defn calc-fill-width-data
|
||||
"Calculates the size and modifiers for the width of an auto-fill child"
|
||||
[{:keys [transform transform-inverse] :as parent}
|
||||
{:keys [layout-h-behavior] :as child}
|
||||
child-origin child-width
|
||||
{:keys [num-children line-width line-fill? child-fill? layout-bounds] :as layout-data}]
|
||||
|
||||
(let [[layout-gap-row _] (ctl/gaps parent)]
|
||||
(cond
|
||||
(and (ctl/row? parent) (= :fill layout-h-behavior) child-fill?)
|
||||
(let [layout-width (gpo/width-points layout-bounds)
|
||||
fill-space (- layout-width line-width (* layout-gap-row (dec num-children)))
|
||||
fill-width (/ fill-space (:num-child-fill layout-data))
|
||||
fill-scale (/ fill-width child-width)]
|
||||
|
||||
{:width fill-width
|
||||
:modifiers (ctm/resize (gpt/point fill-scale 1) child-origin transform transform-inverse)})
|
||||
|
||||
(and (ctl/col? parent) (= :fill layout-h-behavior) line-fill?)
|
||||
(let [fill-scale (/ line-width child-width)]
|
||||
{:width line-width
|
||||
:modifiers (ctm/resize (gpt/point fill-scale 1) child-origin transform transform-inverse)}))))
|
||||
|
||||
(defn calc-fill-height-data
|
||||
"Calculates the size and modifiers for the height of an auto-fill child"
|
||||
[{:keys [transform transform-inverse] :as parent}
|
||||
{:keys [layout-v-behavior] :as child}
|
||||
child-origin child-height
|
||||
{:keys [num-children line-height layout-bounds line-fill? child-fill?] :as layout-data}]
|
||||
|
||||
(let [[_ layout-gap-col] (ctl/gaps parent)]
|
||||
(cond
|
||||
(and (ctl/col? parent) (= :fill layout-v-behavior) child-fill?)
|
||||
(let [layout-height (gpo/height-points layout-bounds)
|
||||
fill-space (- layout-height line-height (* layout-gap-col (dec num-children)))
|
||||
fill-height (/ fill-space (:num-child-fill layout-data))
|
||||
fill-scale (/ fill-height child-height)]
|
||||
{:height fill-height
|
||||
:modifiers (ctm/resize (gpt/point 1 fill-scale) child-origin transform transform-inverse)})
|
||||
|
||||
(and (ctl/row? parent) (= :fill layout-v-behavior) line-fill?)
|
||||
(let [fill-scale (/ line-height child-height)]
|
||||
{:height line-height
|
||||
:modifiers (ctm/resize (gpt/point 1 fill-scale) child-origin transform transform-inverse)}))))
|
||||
|
||||
(defn calc-layout-modifiers
|
||||
"Calculates the modifiers for the layout"
|
||||
[parent child layout-line]
|
||||
(let [child-bounds (gst/parent-coords-points child parent)
|
||||
|
||||
child-origin (gpo/origin child-bounds)
|
||||
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)
|
||||
|
||||
child-width (or (:width fill-width) child-width)
|
||||
child-height (or (:height fill-height) child-height)
|
||||
|
||||
[corner-p layout-line] (fpo/get-child-position parent child-width child-height layout-line)
|
||||
|
||||
move-vec (gpt/to-vec child-origin corner-p)
|
||||
|
||||
modifiers
|
||||
(-> (ctm/empty-modifiers)
|
||||
(cond-> fill-width (ctm/add-modifiers (:modifiers fill-width)))
|
||||
(cond-> fill-height (ctm/add-modifiers (:modifiers fill-height)))
|
||||
(ctm/set-move move-vec))]
|
||||
|
||||
[modifiers layout-line]))
|
68
common/src/app/common/geom/shapes/flex_layout/positions.cljc
Normal file
68
common/src/app/common/geom/shapes/flex_layout/positions.cljc
Normal file
|
@ -0,0 +1,68 @@
|
|||
;; 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.positions
|
||||
(:require
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes.points :as gpo]
|
||||
[app.common.types.shape.layout :as ctl]))
|
||||
|
||||
(defn get-child-position
|
||||
"Calculates the position for the current shape given the layout-data context"
|
||||
[parent
|
||||
child-width child-height
|
||||
{:keys [start-p layout-gap-row layout-gap-col margin-x margin-y] :as layout-data}]
|
||||
|
||||
(let [row? (ctl/row? parent)
|
||||
col? (ctl/col? parent)
|
||||
|
||||
h-center? (ctl/h-center? parent)
|
||||
h-end? (ctl/h-end? parent)
|
||||
v-center? (ctl/v-center? parent)
|
||||
v-end? (ctl/v-end? parent)
|
||||
points (:points parent)
|
||||
|
||||
hv (partial gpo/start-hv points)
|
||||
vv (partial gpo/start-vv points)
|
||||
|
||||
corner-p
|
||||
(cond-> start-p
|
||||
(and col? h-center?)
|
||||
(gpt/add (hv (- (/ child-width 2))))
|
||||
|
||||
(and col? h-end?)
|
||||
(gpt/add (hv (- child-width)))
|
||||
|
||||
(and row? v-center?)
|
||||
(gpt/add (vv (- (/ child-height 2))))
|
||||
|
||||
(and row? v-end?)
|
||||
(gpt/add (vv (- child-height)))
|
||||
|
||||
(some? margin-x)
|
||||
(gpt/add (hv margin-x))
|
||||
|
||||
(some? margin-y)
|
||||
(gpt/add (vv margin-y)))
|
||||
|
||||
next-p
|
||||
(cond-> start-p
|
||||
row?
|
||||
(gpt/add (hv (+ child-width layout-gap-row)))
|
||||
|
||||
col?
|
||||
(gpt/add (vv (+ child-height layout-gap-col)))
|
||||
|
||||
(some? margin-x)
|
||||
(gpt/add (hv margin-x))
|
||||
|
||||
(some? margin-y)
|
||||
(gpt/add (vv margin-y)))
|
||||
|
||||
layout-data
|
||||
(assoc layout-data :start-p next-p)]
|
||||
|
||||
[corner-p layout-data]))
|
|
@ -9,6 +9,27 @@
|
|||
[app.common.spec :as us]
|
||||
[clojure.spec.alpha :as s]))
|
||||
|
||||
;; :layout ;; :flex, :grid in the future
|
||||
;; :layout-flex-dir ;; :row, :reverse-row, :column, :reverse-column
|
||||
;; :layout-gap-type ;; :simple, :multiple
|
||||
;; :layout-gap ;; {:row-gap number , :column-gap number}
|
||||
;; :layout-align-items ;; :start :end :center :strech
|
||||
;; :layout-justify-content ;; :start :center :end :space-between :space-around
|
||||
;; :layout-align-content ;; :start :center :end :space-between :space-around :strech (by default)
|
||||
;; :layout-wrap-type ;; :wrap, :no-wrap
|
||||
;; :layout-padding-type ;; :simple, :multiple
|
||||
;; :layout-padding ;; {:p1 num :p2 num :p3 num :p4 num} number could be negative
|
||||
|
||||
;; ITEMS
|
||||
;; :layout-margin ;; {:m1 0 :m2 0 :m3 0 :m4 0}
|
||||
;; :layout-margin-type ;; :simple :multiple
|
||||
;; :layout-h-behavior ;; :fill :fix :auto
|
||||
;; :layout-v-behavior ;; :fill :fix :auto
|
||||
;; :layout-max-h ;; num
|
||||
;; :layout-min-h ;; num
|
||||
;; :layout-max-w ;; num
|
||||
;; :layout-min-w
|
||||
|
||||
(s/def ::layout #{:flex :grid})
|
||||
(s/def ::layout-flex-dir #{:row :reverse-row :column :reverse-column})
|
||||
(s/def ::layout-gap-type #{:simple :multiple})
|
||||
|
@ -75,3 +96,111 @@
|
|||
::layout-max-w
|
||||
::layout-min-w
|
||||
::layout-align-self]))
|
||||
|
||||
|
||||
(defn wrap? [{:keys [layout-wrap-type]}]
|
||||
(= layout-wrap-type :wrap))
|
||||
|
||||
(defn fill-width? [child]
|
||||
(= :fill (:layout-h-behavior child)))
|
||||
|
||||
(defn fill-height? [child]
|
||||
(= :fill (:layout-v-behavior child)))
|
||||
|
||||
(defn col?
|
||||
[{:keys [layout-flex-dir]}]
|
||||
(or (= :column layout-flex-dir) (= :reverse-column layout-flex-dir)))
|
||||
|
||||
(defn row?
|
||||
[{:keys [layout-flex-dir]}]
|
||||
(or (= :row layout-flex-dir) (= :reverse-row layout-flex-dir)))
|
||||
|
||||
(defn gaps
|
||||
[{:keys [layout-gap layout-gap-type]}]
|
||||
(let [layout-gap-row (or (-> layout-gap :row-gap) 0)
|
||||
layout-gap-col (if (= layout-gap-type :simple)
|
||||
layout-gap-row
|
||||
(or (-> layout-gap :column-gap) 0))]
|
||||
[layout-gap-row layout-gap-col]))
|
||||
|
||||
(defn child-min-width
|
||||
[child]
|
||||
(if (and (fill-width? child)
|
||||
(some? (:layout-min-h child)))
|
||||
(max 0 (:layout-min-h child))
|
||||
0))
|
||||
|
||||
(defn child-max-width
|
||||
[child]
|
||||
(if (and (fill-width? child)
|
||||
(some? (:layout-min-h child)))
|
||||
(max 0 (:layout-min-h child))
|
||||
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
|
||||
[child]
|
||||
(if (and (fill-width? child)
|
||||
(some? (:layout-min-v child)))
|
||||
(max 0 (:layout-min-v child))
|
||||
0))
|
||||
|
||||
(defn h-start?
|
||||
[{:keys [layout-align-items layout-justify-content] :as shape}]
|
||||
(or (and (col? shape)
|
||||
(= layout-align-items :start))
|
||||
(and (row? shape)
|
||||
(= layout-justify-content :start))))
|
||||
|
||||
(defn h-center?
|
||||
[{:keys [layout-align-items layout-justify-content] :as shape}]
|
||||
(or (and (col? shape)
|
||||
(= layout-align-items :center))
|
||||
(and (row? shape)
|
||||
(= layout-justify-content :center))))
|
||||
|
||||
(defn h-end?
|
||||
[{:keys [layout-align-items layout-justify-content] :as shape}]
|
||||
(or (and (col? shape)
|
||||
(= layout-align-items :end))
|
||||
(and (row? shape)
|
||||
(= layout-justify-content :end))))
|
||||
|
||||
(defn v-start?
|
||||
[{:keys [layout-align-items layout-justify-content] :as shape}]
|
||||
(or (and (row? shape)
|
||||
(= layout-align-items :start))
|
||||
(and (col? shape)
|
||||
(= layout-justify-content :start))))
|
||||
|
||||
(defn v-center?
|
||||
[{:keys [layout-align-items layout-justify-content] :as shape}]
|
||||
(or (and (row? shape)
|
||||
(= layout-align-items :center))
|
||||
(and (col? shape)
|
||||
(= layout-justify-content :center))))
|
||||
|
||||
(defn v-end?
|
||||
[{:keys [layout-align-items layout-justify-content] :as shape}]
|
||||
(or (and (row? shape)
|
||||
(= layout-align-items :end))
|
||||
(and (col? shape)
|
||||
(= layout-justify-content :end))))
|
||||
(defn reverse?
|
||||
[{:keys [layout-flex-dir]}]
|
||||
(or (= :reverse-row layout-flex-dir)
|
||||
(= :reverse-column layout-flex-dir)))
|
||||
|
||||
(defn space-between?
|
||||
[{:keys [layout-justify-content]}]
|
||||
(= layout-justify-content :space-between))
|
||||
|
||||
(defn space-around?
|
||||
[{:keys [layout-justify-content]}]
|
||||
(= layout-justify-content :space-around))
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
(ns app.main.ui.workspace.sidebar.options
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.types.modifiers :as ctm]
|
||||
[app.main.data.workspace :as udw]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
|
|
Loading…
Add table
Reference in a new issue