Merge pull request #2659 from penpot/alotor-flex-layout-features

Flex layout features
This commit is contained in:
Andrey Antukh 2022-12-14 09:07:40 +01:00 committed by GitHub
commit 507800ae4e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 195 additions and 58 deletions

View file

@ -8,7 +8,6 @@
(:require (:require
[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.math :as mth]
[app.common.types.shape.layout :as ctl])) [app.common.types.shape.layout :as ctl]))
(defn child-layout-bound-points (defn child-layout-bound-points
@ -55,38 +54,32 @@
(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)))
(cond-> [base-p] ;; We need some height/width to calculate the bounds. We stablish the minimum
(and (mth/almost-zero? min-width) (mth/almost-zero? min-height)) min-width (max min-width 0.01)
(conj (cond-> base-p min-height (max min-height 0.01)]
row?
(gpt/add (hv width))
col? (-> [base-p]
(gpt/add (vv height)))) (conj (cond-> base-p
(or row? h-start?)
(gpt/add (hv min-width))
(not (mth/almost-zero? min-width)) (and col? h-center?)
(conj (cond-> base-p (gpt/add (hv (/ min-width 2)))
(or row? h-start?)
(gpt/add (hv min-width))
(and col? h-center?) (and col? h-center?)
(gpt/add (hv (/ min-width 2))) (gpt/subtract (hv min-width))))
(and col? h-center?) (conj (cond-> base-p
(gpt/subtract (hv min-width)))) (or col? v-start?)
(gpt/add (vv min-height))
(not (mth/almost-zero? min-height)) (and row? v-center?)
(conj (cond-> base-p (gpt/add (vv (/ min-height 2)))
(or col? v-start?)
(gpt/add (vv min-height))
(and row? v-center?) (and row? v-end?)
(gpt/add (vv (/ min-height 2))) (gpt/subtract (vv min-height)))))))
(and row? v-end?)
(gpt/subtract (vv min-height)))))))
(defn layout-content-bounds (defn layout-content-bounds
[bounds {:keys [layout-padding] :as parent} children] [bounds {:keys [layout-padding] :as parent} children]
@ -107,8 +100,11 @@
child-bounds child-bounds
(if (or (ctl/fill-height? child) (ctl/fill-height? child)) (if (or (ctl/fill-height? child) (ctl/fill-height? child))
(child-layout-bound-points parent child parent-bounds child-bounds) (child-layout-bound-points parent child parent-bounds child-bounds)
child-bounds)] child-bounds)
(gpo/parent-coords-bounds child-bounds parent-bounds)))]
[margin-top margin-right margin-bottom margin-left] (ctl/child-margins child)]
(-> (gpo/parent-coords-bounds child-bounds parent-bounds)
(gpo/pad-points (- margin-top) (- margin-right) (- margin-bottom) (- margin-left)))))]
(as-> children $ (as-> children $
(map child-bounds $) (map child-bounds $)

View file

@ -32,6 +32,7 @@
ratio-width (/ target-width curr-width) ratio-width (/ target-width curr-width)
ratio-height (/ target-height curr-height) ratio-height (/ target-height curr-height)
scalev (gpt/point ratio-width ratio-height)] scalev (gpt/point ratio-width ratio-height)]
(-> modifiers (-> modifiers
(ctm/resize scalev origin transform transform-inverse)))) (ctm/resize scalev origin transform transform-inverse))))
@ -41,7 +42,6 @@
corner (gpt/point bounds) corner (gpt/point bounds)
target-corner (gpt/round corner) target-corner (gpt/round corner)
deltav (gpt/to-vec corner target-corner)] deltav (gpt/to-vec corner target-corner)]
(ctm/move modifiers deltav))) (ctm/move modifiers deltav)))
(defn set-pixel-precision (defn set-pixel-precision
@ -56,8 +56,10 @@
has-resize? (size-pixel-precision shape points)) has-resize? (size-pixel-precision shape points))
points points
(cond-> (:points shape) (if has-resize?
has-resize? (gco/transform-points (ctm/modifiers->transform modifiers)))] (-> (:points shape)
(gco/transform-points (ctm/modifiers->transform modifiers)) )
points)]
[modifiers points])] [modifiers points])]
(position-pixel-precision modifiers shape points))) (position-pixel-precision modifiers shape points)))

View file

@ -76,7 +76,6 @@
:layout :layout-container :layout :layout-container
:layout-dir :layout-container :layout-dir :layout-container
:layout-gap :layout-container :layout-gap :layout-container
:layout-type :layout-container
:layout-wrap-type :layout-container :layout-wrap-type :layout-container
:layout-padding-type :layout-container :layout-padding-type :layout-container
:layout-padding :layout-container :layout-padding :layout-container

View file

@ -50,7 +50,6 @@
(s/def ::row-gap ::us/safe-number) (s/def ::row-gap ::us/safe-number)
(s/def ::column-gap ::us/safe-number) (s/def ::column-gap ::us/safe-number)
(s/def ::layout-type #{:flex :grid})
(s/def ::layout-gap (s/def ::layout-gap
(s/keys :opt-un [::row-gap ::column-gap])) (s/keys :opt-un [::row-gap ::column-gap]))
@ -60,7 +59,6 @@
::layout-flex-dir ::layout-flex-dir
::layout-gap ::layout-gap
::layout-gap-type ::layout-gap-type
::layout-type
::layout-wrap-type ::layout-wrap-type
::layout-padding-type ::layout-padding-type
::layout-padding ::layout-padding

View file

@ -28,6 +28,7 @@
[app.main.data.workspace.undo :as dwu] [app.main.data.workspace.undo :as dwu]
[app.main.snap :as snap] [app.main.snap :as snap]
[app.main.streams :as ms] [app.main.streams :as ms]
[app.util.dom :as dom]
[beicon.core :as rx] [beicon.core :as rx]
[cljs.spec.alpha :as s] [cljs.spec.alpha :as s]
[potok.core :as ptk])) [potok.core :as ptk]))
@ -415,6 +416,14 @@
(rx/take 1) (rx/take 1)
(rx/map #(start-move from-position)))))) (rx/map #(start-move from-position))))))
(defn set-ghost-displacement
[move-vector]
(ptk/reify ::set-ghost-displacement
ptk/EffectEvent
(effect [_ _ _]
(when-let [node (dom/get-element-by-class "ghost-outline")]
(dom/set-property! node "transform" (gmt/translate-matrix move-vector))))))
(defn- start-move (defn- start-move
([from-position] (start-move from-position nil)) ([from-position] (start-move from-position nil))
([from-position ids] ([from-position ids]
@ -501,6 +510,9 @@
(dwm/build-change-frame-modifiers objects selected target-frame drop-index) (dwm/build-change-frame-modifiers objects selected target-frame drop-index)
(dwm/set-modifiers))))) (dwm/set-modifiers)))))
(->> move-stream
(rx/map (comp set-ghost-displacement first)))
;; Last event will write the modifiers creating the changes ;; Last event will write the modifiers creating the changes
(->> move-stream (->> move-stream
(rx/last) (rx/last)
@ -508,10 +520,10 @@
(fn [[_ target-frame drop-index]] (fn [[_ target-frame drop-index]]
(let [undo-id (uuid/next)] (let [undo-id (uuid/next)]
(rx/of (dwu/start-undo-transaction undo-id) (rx/of (dwu/start-undo-transaction undo-id)
(move-shapes-to-frame ids target-frame drop-index) (move-shapes-to-frame ids target-frame drop-index)
(dwm/apply-modifiers {:undo-transation? false}) (dwm/apply-modifiers {:undo-transation? false})
(finish-transform) (finish-transform)
(dwu/commit-undo-transaction undo-id)))))))))))))) (dwu/commit-undo-transaction undo-id))))))))))))))
(s/def ::direction #{:up :down :right :left}) (s/def ::direction #{:up :down :right :left})

View file

@ -332,16 +332,84 @@
:penpot:preserve-scroll ((d/nilf str) (:preserve-scroll interaction))}])]))) :penpot:preserve-scroll ((d/nilf str) (:preserve-scroll interaction))}])])))
(defn- export-layout-container-data
[{:keys [layout
layout-flex-dir
layout-gap
layout-gap-type
layout-wrap-type
layout-padding-type
layout-padding
layout-justify-content
layout-align-items
layout-align-content]}]
(when layout
(mf/html
[:> "penpot:layout"
#js {:penpot:layout (d/name layout)
:penpot:layout-flex-dir (d/name layout-flex-dir)
:penpot:layout-gap-type (d/name layout-gap-type)
:penpot:layout-gap-row (:row-gap layout-gap)
:penpot:layout-gap-column (:column-gap layout-gap)
:penpot:layout-wrap-type (d/name layout-wrap-type)
:penpot:layout-padding-type (d/name layout-padding-type)
:penpot:layout-padding-p1 (:p1 layout-padding)
:penpot:layout-padding-p2 (:p2 layout-padding)
:penpot:layout-padding-p3 (:p3 layout-padding)
:penpot:layout-padding-p4 (:p4 layout-padding)
:penpot:layout-justify-content (d/name layout-justify-content)
:penpot:layout-align-items (d/name layout-align-items)
:penpot:layout-align-content (d/name layout-align-content)}])))
(defn- export-layout-item-data
[{:keys [layout-item-margin
layout-item-margin-type
layout-item-h-sizing
layout-item-v-sizing
layout-item-max-h
layout-item-min-h
layout-item-max-w
layout-item-min-w
layout-item-align-self]}]
(when (or layout-item-margin
layout-item-margin-type
layout-item-h-sizing
layout-item-v-sizing
layout-item-max-h
layout-item-min-h
layout-item-max-w
layout-item-min-w
layout-item-align-self)
(mf/html
[:> "penpot:layout-item"
#js {:penpot:layout-item-margin-m1 (:m1 layout-item-margin)
:penpot:layout-item-margin-m2 (:m2 layout-item-margin)
:penpot:layout-item-margin-m3 (:m3 layout-item-margin)
:penpot:layout-item-margin-m4 (:m4 layout-item-margin)
:penpot:layout-item-margin-type (d/name layout-item-margin-type)
:penpot:layout-item-h-sizing (d/name layout-item-h-sizing)
:penpot:layout-item-v-sizing (d/name layout-item-v-sizing)
:penpot:layout-item-max-h layout-item-max-h
:penpot:layout-item-min-h layout-item-min-h
:penpot:layout-item-max-w layout-item-max-w
:penpot:layout-item-min-w layout-item-min-w
:penpot:layout-item-align-self (d/name layout-item-align-self)}])))
(mf/defc export-data (mf/defc export-data
[{:keys [shape]}] [{:keys [shape]}]
(let [props (-> (obj/create) (add-data shape) (add-library-refs shape))] (let [props (-> (obj/create) (add-data shape) (add-library-refs shape))]
[:> "penpot:shape" props [:> "penpot:shape" props
(export-shadow-data shape) (export-shadow-data shape)
(export-blur-data shape) (export-blur-data shape)
(export-exports-data shape) (export-exports-data shape)
(export-svg-data shape) (export-svg-data shape)
(export-interactions-data shape) (export-interactions-data shape)
(export-fills-data shape) (export-fills-data shape)
(export-strokes-data shape) (export-strokes-data shape)
(export-grid-data shape)])) (export-grid-data shape)
(export-layout-container-data shape)
(export-layout-item-data shape)]))

View file

@ -382,7 +382,6 @@
on-padding-change on-padding-change
(fn [type prop val] (fn [type prop val]
(prn "??" type prop val)
(cond (cond
(and (= type :simple) (= prop :p1)) (and (= type :simple) (= prop :p1))
(st/emit! (dwsl/update-layout ids {:layout-padding {:p1 val :p3 val}})) (st/emit! (dwsl/update-layout ids {:layout-padding {:p1 val :p3 val}}))

View file

@ -11,6 +11,7 @@
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.geom.shapes :as gsh] [app.common.geom.shapes :as gsh]
[app.common.pages.helpers :as cph] [app.common.pages.helpers :as cph]
[app.common.types.shape.layout :as ctl]
[app.main.refs :as refs] [app.main.refs :as refs]
[app.main.ui.context :as ctx] [app.main.ui.context :as ctx]
[app.main.ui.hooks :as ui-hooks] [app.main.ui.hooks :as ui-hooks]
@ -293,14 +294,25 @@
:modifiers modifiers}]) :modifiers modifiers}])
(when show-frame-outline? (when show-frame-outline?
[:& outline/shape-outlines (let [outlined-frame-id
{:objects objects-modified (->> @hover-ids
:hover #{(->> @hover-ids (filter #(cph/frame-shape? (get base-objects %)))
(filter #(cph/frame-shape? (get base-objects %))) (remove selected)
(remove selected) (first))
(first))} outlined-frame (get objects outlined-frame-id)]
:zoom zoom [:*
:modifiers modifiers}]) [:& outline/shape-outlines
{:objects objects-modified
:hover #{outlined-frame-id}
:zoom zoom
:modifiers modifiers}]
(when (ctl/layout? outlined-frame)
[:g.ghost-outline
[:& outline/shape-outlines
{:objects base-objects
:selected selected
:zoom zoom}]])]))
(when show-outlines? (when show-outlines?
[:& outline/shape-outlines [:& outline/shape-outlines

View file

@ -75,8 +75,8 @@
points [start-p points [start-p
(-> start-p (gpt/add (xv line-width))) (-> start-p (gpt/add (xv line-width)))
(-> start-p (gpt/add (xv line-width)) (gpt/add (yv line-height))) (-> start-p (gpt/add (xv line-width)) (gpt/add (yv line-height)))
(-> start-p (gpt/add (yv line-height))) (-> start-p (gpt/add (yv line-height)))]]
]]
[:g.layout-line {:key (dm/str "line-" idx)} [:g.layout-line {:key (dm/str "line-" idx)}
[:polygon {:points (->> points (map #(dm/fmt "%, %" (:x %) (:y %))) (str/join " ")) [:polygon {:points (->> points (map #(dm/fmt "%, %" (:x %) (:y %))) (str/join " "))
:style {:stroke "red" :stroke-width (/ 2 zoom) :stroke-dasharray (dm/str (/ 10 zoom) " " (/ 5 zoom))}}]]))])))) :style {:stroke "red" :stroke-width (/ 2 zoom) :stroke-dasharray (dm/str (/ 10 zoom) " " (/ 5 zoom))}}]]))]))))

View file

@ -869,6 +869,54 @@
:style :style
parse-style)))) parse-style))))
(defn add-layout-container-data [props node]
(if-let [data (get-data node :penpot:layout)]
(merge props
(d/without-nils
{:layout (get-meta data :layout keyword)
:layout-flex-dir (get-meta data :layout-flex-dir keyword)
:layout-wrap-type (get-meta data :layout-wrap-type keyword)
:layout-gap-type (get-meta data :layout-gap-type keyword)
:layout-gap
(d/without-nils
{:row-gap (get-meta data :layout-gap-row d/parse-double)
:column-gap (get-meta data :layout-gap-column d/parse-double)})
:layout-padding-type (get-meta data :layout-padding-type keyword)
:layout-padding
(d/without-nils
{:p1 (get-meta data :layout-padding-p1 d/parse-double)
:p2 (get-meta data :layout-padding-p2 d/parse-double)
:p3 (get-meta data :layout-padding-p3 d/parse-double)
:p4 (get-meta data :layout-padding-p4 d/parse-double)})
:layout-justify-content (get-meta data :layout-justify-content keyword)
:layout-align-items (get-meta data :layout-align-items keyword)
:layout-align-content (get-meta data :layout-align-content keyword)}))
props))
(defn add-layout-item-data [props node]
(if-let [data (get-data node :penpot:layout-item)]
(merge props
(d/without-nils
{:layout-item-margin
(d/without-nils
{:m1 (get-meta data :layout-item-margin-m1 d/parse-double)
:m2 (get-meta data :layout-item-margin-m2 d/parse-double)
:m3 (get-meta data :layout-item-margin-m3 d/parse-double)
:m4 (get-meta data :layout-item-margin-m4 d/parse-double)})
:layout-item-margin-type (get-meta data :layout-item-margin-type keyword)
:layout-item-h-sizing (get-meta data :layout-item-h-sizing keyword)
:layout-item-v-sizing (get-meta data :layout-item-v-sizing keyword)
:layout-item-max-h (get-meta data :layout-item-max-h d/parse-double)
:layout-item-min-h (get-meta data :layout-item-min-h d/parse-double)
:layout-item-max-w (get-meta data :layout-item-max-w d/parse-double)
:layout-item-min-w (get-meta data :layout-item-min-w d/parse-double)
:layout-item-align-self (get-meta data :layout-item-align-self keyword)}))
props))
(defn parse-data (defn parse-data
[type node] [type node]
@ -894,7 +942,10 @@
(add-svg-content node)) (add-svg-content node))
(cond-> (= :frame type) (cond-> (= :frame type)
(add-frame-data node)) (-> (add-frame-data node)
(add-layout-container-data node)))
(add-layout-item-data node)
(cond-> (= :group type) (cond-> (= :group type)
(add-group-data node)) (add-group-data node))