0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-04-14 07:51:35 -05:00

🎉 Add packed basic layout positions

This commit is contained in:
alonso.torres 2022-07-01 13:19:27 +02:00 committed by Andrey Antukh
parent aeb8fa1896
commit 1c8aef6fa8
4 changed files with 212 additions and 68 deletions

View file

@ -11,31 +11,169 @@
[app.common.geom.shapes.rect :as gre]
[app.common.geom.shapes.transforms :as gtr]))
;; :layout ;; true if active, false if not
;; :layout-dir ;; :right, :left, :top, :bottom
;; :layout-gap ;; number could be negative
;; :layout-type ;; :packed, :space-between, :space-around
;; :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
;; :layout-h-orientation ;; :top, :center, :bottom
;; :layout-v-orientation ;; :left, :center, :right
(defn col?
[{:keys [layout-dir]}]
(or (= :right layout-dir) (= :left layout-dir)))
(defn row?
[{:keys [layout-dir]}]
(or (= :top layout-dir) (= :bottom layout-dir)))
(defn h-start?
[{:keys [layout-v-orientation]}]
(= layout-v-orientation :left))
(defn h-center?
[{:keys [layout-v-orientation]}]
(= layout-v-orientation :center))
(defn h-end?
[{:keys [layout-v-orientation]}]
(= layout-v-orientation :right))
(defn v-start?
[{:keys [layout-h-orientation]}]
(= layout-h-orientation :top))
(defn v-center?
[{:keys [layout-h-orientation]}]
(= layout-h-orientation :center))
(defn v-end?
[{:keys [layout-h-orientation]}]
(= layout-h-orientation :bottom))
(defn add-padding [transformed-rect {:keys [layout-padding-type layout-padding]}]
(let [{:keys [p1 p2 p3 p4]} layout-padding
[p1 p2 p3 p4]
(if (= layout-padding-type :multiple)
[p1 p2 p3 p4]
[p1 p1 p1 p1])]
(-> transformed-rect
(update :y + p1)
(update :width - p2 p3)
(update :x + p3)
(update :height - p1 p4))))
(defn calc-layout-data
"Digest the layout data to pass it to the constrains"
[_parent children transformed-rect]
[{:keys [layout-gap] :as shape} children modif-tree transformed-rect]
(let [[children-width children-height]
(->> children (reduce (fn [[acc-width acc-height] shape]
[(+ acc-width (-> shape :points gre/points->rect :width))
(+ acc-height (-> shape :points gre/points->rect :height))]) [0 0]))]
{:start-x (:x transformed-rect)
:start-y (:y transformed-rect)
:children-width children-width
:children-height children-height})
(let [transformed-rect (-> transformed-rect (add-padding shape))
num-children (count children)
children-gap (* layout-gap (dec num-children) )
[children-width children-height]
(->> children
(map #(-> (merge % (get modif-tree (:id %))) gtr/transform-shape))
(reduce (fn [[acc-width acc-height] shape]
[(+ acc-width (-> shape :points gre/points->rect :width))
(+ acc-height (-> shape :points gre/points->rect :height))]) [0 0]))
{:keys [x y width height]} transformed-rect
start-x
(cond
(and (row? shape) (h-center? shape))
(+ x (/ width 2))
(and (row? shape) (h-end? shape))
(+ x width)
(and (col? shape) (h-center? shape))
(- (+ x (/ width 2)) (/ (+ children-width children-gap) 2))
(and (col? shape) (h-end? shape))
(- (+ x width) (+ children-width children-gap))
:else
(:x transformed-rect))
start-y
(cond
(and (col? shape) (v-center? shape))
(+ y (/ height 2))
(and (col? shape) (v-end? shape))
(+ y height)
(and (row? shape) (v-center? shape))
(- (+ y (/ height 2)) (/ (+ children-height children-gap) 2))
(and (row? shape) (v-end? shape))
(- (+ y height) (+ children-height children-gap))
:else
(:y transformed-rect) )]
{:start-x start-x
:start-y start-y
:reverse? (or (= :left (:layout-dir shape)) (= :bottom (:layout-dir shape)))}))
(defn next-p
"Calculates the position for the current shape given the layout-data context"
[{:keys [layout-gap] :as shape} {:keys [width height]} {:keys [start-x start-y] :as layout-data}]
(let [pos-x
(cond
(and (row? shape) (h-center? shape))
(- start-x (/ width 2))
(and (row? shape) (h-end? shape))
(- start-x width)
:else
start-x)
pos-y
(cond
(and (col? shape) (v-center? shape))
(- start-y (/ height 2))
(and (col? shape) (v-end? shape))
(- start-y height)
:else
start-y)
corner-p (gpt/point pos-x pos-y)
next-x
(if (col? shape)
(+ start-x width layout-gap)
start-x)
next-y
(if (row? shape)
(+ start-y height layout-gap)
start-y)
layout-data
(assoc layout-data :start-x next-x :start-y next-y)]
[corner-p layout-data])
)
(defn calc-layout-modifiers
[_parent child current-modifier _modifiers _transformed-rect {:keys [start-x start-y] :as layout-data}]
"Calculates the modifiers for the layout"
[parent child current-modifier _modifiers _transformed-rect layout-data]
(let [current-modifier (dissoc current-modifier :displacement-after)
child' (-> child (assoc :modifiers current-modifier) gtr/transform-shape)
bounds' (-> child' :points gre/points->selrect)
corner-p (gpt/point start-x start-y)
displacement (gmt/translate-matrix (gpt/subtract corner-p (gpt/point bounds')))
modifiers (-> current-modifier
(assoc :displacement-after displacement))
(let [current-modifier (-> current-modifier (dissoc :displacement-after))
child (-> child (assoc :modifiers current-modifier) gtr/transform-shape)
bounds (-> child :points gre/points->selrect)
next-x (+ start-x (:width bounds'))]
[corner-p layout-data] (next-p parent bounds layout-data)
[modifiers (assoc layout-data :start-x next-x )]))
delta-p (-> corner-p (gpt/subtract (gpt/point bounds)))
modifiers (-> current-modifier (assoc :displacement-after (gmt/translate-matrix delta-p)))]
[modifiers layout-data]))

View file

@ -19,6 +19,7 @@
[app.main.data.workspace.changes :as dch]
[app.main.data.workspace.edition :as dwe]
[app.main.data.workspace.selection :as dws]
[app.main.data.workspace.shape-layout :as dwsl]
[app.main.data.workspace.state-helpers :as wsh]
[app.main.streams :as ms]
[app.util.names :as un]
@ -98,6 +99,7 @@
(rx/concat
(rx/of (dch/commit-changes changes)
(dwsl/update-layout-positions [(:parent-id shape)])
(when-not no-select?
(dws/select-shapes (d/ordered-set id))))
(when (= :text (:type attrs))
@ -239,7 +241,8 @@
flows
starting-flows)))))]
(rx/of (dch/commit-changes changes))))))
(rx/of (dch/commit-changes changes)
(dwsl/update-layout-positions all-parents))))))
(defn- viewport-center
[state]

View file

@ -415,12 +415,12 @@
(cond
(:layout shape)
(let [result
(->> children
(reduce (partial set-layout-child (and snap-pixel? resize-modif?))
(merge {:modif-tree modif-tree}
(gsh/calc-layout-data shape children transformed-rect))))]
(:modif-tree result))
(let [layout-data (gsh/calc-layout-data shape children modif-tree transformed-rect)
children (cond-> children (:reverse? layout-data) reverse)]
(->> children
(reduce (partial set-layout-child (and snap-pixel? resize-modif?))
(merge {:modif-tree modif-tree} layout-data))
:modif-tree))
:else
modif-tree)))]

View file

@ -88,9 +88,11 @@
type (:layout-type values)
is-col? (or (= dir :top)
(= dir :bottom))
saved-pos [(:layout-h-orientation values) (:layout-v-orientation values)]]
saved-pos [(:layout-h-orientation values)
(:layout-v-orientation values)]]
(if (= type :packed)
(cond
(= type :packed)
[:div.orientation-grid
[:div.button-wrapper
(for [[pv ph] grid-pos]
@ -110,47 +112,48 @@
:rotated is-col?)}
(get-layout-icon dir type pv ph)]])]]
(if is-col?
[:div.orientation-grid.col
[:div.button-wrapper
(for [[idx col] (d/enumerate grid-cols)]
[:button.orientation
{:key (dm/str idx col)
:on-click (partial on-change-orientation :top col type)
:class (dom/classnames
:active (= col (second saved-pos))
:top (= :left col)
:centered (= :center col)
:bottom (= :right col))}
[:span.icon
{:class (dom/classnames :rotated is-col?)}
(get-layout-icon dir type nil col)]
[:span.icon
{:class (dom/classnames :rotated is-col?)}
(get-layout-icon dir type nil col)]
[:span.icon
{:class (dom/classnames :rotated is-col?)}
(get-layout-icon dir type nil col)]])]]
is-col?
[:div.orientation-grid.col
[:div.button-wrapper
(for [[idx col] (d/enumerate grid-cols)]
[:button.orientation
{:key (dm/str idx col)
:on-click (partial on-change-orientation :top col type)
:class (dom/classnames
:active (= col (second saved-pos))
:top (= :left col)
:centered (= :center col)
:bottom (= :right col))}
[:span.icon
{:class (dom/classnames :rotated is-col?)}
(get-layout-icon dir type nil col)]
[:span.icon
{:class (dom/classnames :rotated is-col?)}
(get-layout-icon dir type nil col)]
[:span.icon
{:class (dom/classnames :rotated is-col?)}
(get-layout-icon dir type nil col)]])]]
[:div.orientation-grid.row
[:div.button-wrapper
(for [row grid-rows]
[:button.orientation
{:on-click (partial on-change-orientation row :left type)
:class (dom/classnames
:active (= row (first saved-pos))
:top (= :top row)
:centered (= :center row)
:bottom (= :bottom row))}
[:span.icon
{:class (dom/classnames :rotated is-col?)}
(get-layout-icon dir type row nil)]
[:span.icon
{:class (dom/classnames :rotated is-col?)}
(get-layout-icon dir type row nil)]
[:span.icon
{:class (dom/classnames :rotated is-col?)}
(get-layout-icon dir type row nil)]])]]))))
:else
[:div.orientation-grid.row
[:div.button-wrapper
(for [row grid-rows]
[:button.orientation
{:on-click (partial on-change-orientation row :left type)
:class (dom/classnames
:active (= row (first saved-pos))
:top (= :top row)
:centered (= :center row)
:bottom (= :bottom row))}
[:span.icon
{:class (dom/classnames :rotated is-col?)}
(get-layout-icon dir type row nil)]
[:span.icon
{:class (dom/classnames :rotated is-col?)}
(get-layout-icon dir type row nil)]
[:span.icon
{:class (dom/classnames :rotated is-col?)}
(get-layout-icon dir type row nil)]])]])))
(mf/defc padding-section
[{:keys [values on-change-style on-change] :as props}]