mirror of
https://github.com/penpot/penpot.git
synced 2025-03-18 10:41:29 -05:00
Merge pull request #2537 from penpot/alotor-autolayout-v2
Autolayout & refactor transformations
This commit is contained in:
commit
3de217a52e
110 changed files with 4700 additions and 2443 deletions
|
@ -4,8 +4,11 @@
|
|||
|
||||
### :boom: Breaking changes & Deprecations
|
||||
### :sparkles: New features
|
||||
|
||||
- Adds layout flex functionality for boards
|
||||
- Better overlays interactions on boards inside boards [Taiga #4386](https://tree.taiga.io/project/penpot/us/4386)
|
||||
- Show board miniature in manual overlay setting [Taiga #4475](https://tree.taiga.io/project/penpot/issue/4475)
|
||||
|
||||
### :bug: Bugs fixed
|
||||
|
||||
- Add title to color bullets [Taiga #4218](https://tree.taiga.io/project/penpot/task/4218)
|
||||
|
|
|
@ -252,3 +252,21 @@
|
|||
(update :d mth/precision 4)
|
||||
(update :e mth/precision 4)
|
||||
(update :f mth/precision 4)))
|
||||
|
||||
(defn transform-point-center
|
||||
"Transform a point around the shape center"
|
||||
[point center matrix]
|
||||
(if (and (some? point) (some? matrix) (some? center))
|
||||
(gpt/transform
|
||||
point
|
||||
(multiply (translate-matrix center)
|
||||
matrix
|
||||
(translate-matrix (gpt/negate center))))
|
||||
point))
|
||||
|
||||
(defn move?
|
||||
[{:keys [a b c d _ _]}]
|
||||
(and (mth/almost-zero? (- a 1))
|
||||
(mth/almost-zero? b)
|
||||
(mth/almost-zero? c)
|
||||
(mth/almost-zero? (- d 1))))
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
;; Copyright (c) KALEIDOS INC
|
||||
|
||||
(ns app.common.geom.point
|
||||
(:refer-clojure :exclude [divide min max])
|
||||
(:refer-clojure :exclude [divide min max abs])
|
||||
(:require
|
||||
#?(:cljs [cljs.pprint :as pp]
|
||||
:clj [clojure.pprint :as pp])
|
||||
|
@ -328,6 +328,13 @@
|
|||
(update :x #(if (mth/almost-zero? %) 0.001 %))
|
||||
(update :y #(if (mth/almost-zero? %) 0.001 %))))
|
||||
|
||||
|
||||
(defn abs
|
||||
[point]
|
||||
(-> point
|
||||
(update :x mth/abs)
|
||||
(update :y mth/abs)))
|
||||
|
||||
;; --- Debug
|
||||
|
||||
(defmethod pp/simple-dispatch Point [obj] (pr obj))
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
[app.common.geom.shapes.constraints :as gct]
|
||||
[app.common.geom.shapes.corners :as gsc]
|
||||
[app.common.geom.shapes.intersect :as gin]
|
||||
[app.common.geom.shapes.layout :as gcl]
|
||||
[app.common.geom.shapes.modifiers :as gsm]
|
||||
[app.common.geom.shapes.path :as gsp]
|
||||
[app.common.geom.shapes.rect :as gpr]
|
||||
|
@ -28,7 +27,7 @@
|
|||
rotation of each shape. Mainly used for multiple selection."
|
||||
[shapes]
|
||||
(->> shapes
|
||||
(map (comp gpr/points->selrect :points gtr/transform-shape))
|
||||
(map (comp gpr/points->selrect :points))
|
||||
(gpr/join-selrects)))
|
||||
|
||||
(defn translate-to-frame
|
||||
|
@ -160,37 +159,31 @@
|
|||
(dm/export gpr/join-rects)
|
||||
(dm/export gpr/join-selrects)
|
||||
(dm/export gpr/contains-selrect?)
|
||||
(dm/export gpr/contains-point?)
|
||||
(dm/export gpr/close-selrect?)
|
||||
|
||||
(dm/export gtr/move)
|
||||
(dm/export gtr/absolute-move)
|
||||
(dm/export gtr/transform-matrix)
|
||||
(dm/export gtr/transform-str)
|
||||
(dm/export gtr/inverse-transform-matrix)
|
||||
(dm/export gtr/transform-point-center)
|
||||
(dm/export gtr/transform-rect)
|
||||
(dm/export gtr/calculate-adjust-matrix)
|
||||
(dm/export gtr/update-group-selrect)
|
||||
(dm/export gtr/update-mask-selrect)
|
||||
(dm/export gtr/resize-modifiers)
|
||||
(dm/export gtr/change-orientation-modifiers)
|
||||
(dm/export gtr/rotation-modifiers)
|
||||
(dm/export gtr/merge-modifiers)
|
||||
(dm/export gtr/update-bool-selrect)
|
||||
(dm/export gtr/transform-shape)
|
||||
(dm/export gtr/transform-selrect)
|
||||
(dm/export gtr/transform-selrect-matrix)
|
||||
(dm/export gtr/transform-bounds)
|
||||
(dm/export gtr/modifiers->transform)
|
||||
(dm/export gtr/empty-modifiers?)
|
||||
(dm/export gtr/move-position-data)
|
||||
(dm/export gtr/apply-transform)
|
||||
(dm/export gtr/apply-objects-modifiers)
|
||||
(dm/export gtr/parent-coords-rect)
|
||||
(dm/export gtr/parent-coords-points)
|
||||
|
||||
;; Constratins
|
||||
(dm/export gct/calc-child-modifiers)
|
||||
|
||||
;; Layout
|
||||
(dm/export gcl/calc-layout-data)
|
||||
(dm/export gcl/calc-layout-modifiers)
|
||||
|
||||
;; PATHS
|
||||
(dm/export gsp/content->selrect)
|
||||
(dm/export gsp/transform-content)
|
||||
|
@ -203,7 +196,7 @@
|
|||
(dm/export gin/rect-contains-shape?)
|
||||
|
||||
;; Bool
|
||||
(dm/export gsb/update-bool-selrect)
|
||||
|
||||
(dm/export gsb/calc-bool-content)
|
||||
|
||||
;; Constraints
|
||||
|
@ -216,3 +209,4 @@
|
|||
|
||||
;; Modifiers
|
||||
(dm/export gsm/set-objects-modifiers)
|
||||
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
(ns app.common.geom.shapes.bool
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.shapes.path :as gsp]
|
||||
[app.common.geom.shapes.transforms :as gtr]
|
||||
[app.common.path.bool :as pb]
|
||||
[app.common.path.shapes-to-path :as stp]))
|
||||
|
||||
|
@ -25,17 +23,5 @@
|
|||
(into [] extract-content-xf (:shapes shape))]
|
||||
(pb/content-bool (:bool-type shape) shapes-content)))
|
||||
|
||||
(defn update-bool-selrect
|
||||
"Calculates the selrect+points for the boolean shape"
|
||||
[shape children objects]
|
||||
|
||||
(let [bool-content (calc-bool-content shape objects)
|
||||
shape (assoc shape :bool-content bool-content)
|
||||
[points selrect] (gsp/content->points+selrect shape bool-content)]
|
||||
|
||||
(if (and (some? selrect) (d/not-empty? points))
|
||||
(-> shape
|
||||
(assoc :selrect selrect)
|
||||
(assoc :points points))
|
||||
(gtr/update-group-selrect shape children))))
|
||||
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.matrix :as gmt]
|
||||
[app.common.geom.point :as gpt]))
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes.rect :as gpr]))
|
||||
|
||||
(defn center-rect
|
||||
[{:keys [x y width height]}]
|
||||
|
@ -31,6 +32,22 @@
|
|||
(gpt/point (/ (+ minx maxx) 2.0)
|
||||
(/ (+ miny maxy) 2.0))))
|
||||
|
||||
(defn center-bounds [[a b c d]]
|
||||
(let [xa (:x a)
|
||||
ya (:y a)
|
||||
xb (:x b)
|
||||
yb (:y b)
|
||||
xc (:x c)
|
||||
yc (:y c)
|
||||
xd (:x d)
|
||||
yd (:y d)
|
||||
minx (min xa xb xc xd)
|
||||
miny (min ya yb yc yd)
|
||||
maxx (max xa xb xc xd)
|
||||
maxy (max ya yb yc yd)]
|
||||
(gpt/point (/ (+ minx maxx) 2.0)
|
||||
(/ (+ miny maxy) 2.0))))
|
||||
|
||||
(defn center-shape
|
||||
"Calculate the center of the shape."
|
||||
[shape]
|
||||
|
@ -49,3 +66,8 @@
|
|||
(gpt/transform point (gmt/multiply prev matrix post)))]
|
||||
(mapv tr-point points))
|
||||
points)))
|
||||
|
||||
(defn transform-selrect
|
||||
[{:keys [x1 y1 x2 y2] :as sr} matrix]
|
||||
(let [[c1 c2] (transform-points [(gpt/point x1 y1) (gpt/point x2 y2)] matrix)]
|
||||
(gpr/corners->selrect c1 c2)))
|
||||
|
|
|
@ -6,14 +6,20 @@
|
|||
|
||||
(ns app.common.geom.shapes.constraints
|
||||
(:require
|
||||
[app.common.geom.matrix :as gmt]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes.common :as gco]
|
||||
[app.common.geom.shapes.intersect :as gsi]
|
||||
[app.common.geom.shapes.points :as gpo]
|
||||
[app.common.geom.shapes.rect :as gre]
|
||||
[app.common.geom.shapes.transforms :as gst]
|
||||
[app.common.math :as mth]
|
||||
[app.common.types.modifiers :as ctm]
|
||||
[app.common.uuid :as uuid]))
|
||||
|
||||
;; Auxiliary methods to work in an specifica axis
|
||||
(defn other-axis [axis]
|
||||
(if (= :x axis) :y :x))
|
||||
|
||||
(defn get-delta-start [axis rect tr-rect]
|
||||
(if (= :x axis)
|
||||
(- (:x1 tr-rect) (:x1 rect))
|
||||
|
@ -29,6 +35,11 @@
|
|||
(- (:width tr-rect) (:width rect))
|
||||
(- (:height tr-rect) (:height rect))))
|
||||
|
||||
(defn get-delta-scale [axis rect tr-rect]
|
||||
(if (= :x axis)
|
||||
(/ (:width tr-rect) (:width rect))
|
||||
(/ (:height tr-rect) (:height rect))))
|
||||
|
||||
(defn get-delta-center [axis center tr-center]
|
||||
(if (= :x axis)
|
||||
(- (:x tr-center) (:x center))
|
||||
|
@ -53,78 +64,160 @@
|
|||
(:width rect)
|
||||
(:height rect)))
|
||||
|
||||
(defn right-vector
|
||||
[child-points parent-points]
|
||||
(let [[p0 p1 p2 _] parent-points
|
||||
[_c0 c1 _ _] child-points
|
||||
dir-v (gpt/to-vec p0 p1)
|
||||
cp (gsi/line-line-intersect c1 (gpt/add c1 dir-v) p1 p2)]
|
||||
(gpt/to-vec c1 cp)))
|
||||
|
||||
(defn left-vector
|
||||
[child-points parent-points]
|
||||
|
||||
(let [[p0 p1 _ p3] parent-points
|
||||
[_ _ _ c3] child-points
|
||||
dir-v (gpt/to-vec p0 p1)
|
||||
cp (gsi/line-line-intersect c3 (gpt/add c3 dir-v) p0 p3)]
|
||||
(gpt/to-vec c3 cp)))
|
||||
|
||||
(defn top-vector
|
||||
[child-points parent-points]
|
||||
|
||||
(let [[p0 p1 _ p3] parent-points
|
||||
[c0 _ _ _] child-points
|
||||
dir-v (gpt/to-vec p0 p3)
|
||||
cp (gsi/line-line-intersect c0 (gpt/add c0 dir-v) p0 p1)]
|
||||
(gpt/to-vec c0 cp)))
|
||||
|
||||
(defn bottom-vector
|
||||
[child-points parent-points]
|
||||
|
||||
(let [[p0 _ p2 p3] parent-points
|
||||
[_ _ c2 _] child-points
|
||||
dir-v (gpt/to-vec p0 p3)
|
||||
cp (gsi/line-line-intersect c2 (gpt/add c2 dir-v) p2 p3)]
|
||||
(gpt/to-vec c2 cp)))
|
||||
|
||||
(defn center-horizontal-vector
|
||||
[child-points parent-points]
|
||||
|
||||
(let [[p0 p1 _ p3] parent-points
|
||||
[_ c1 _ _] child-points
|
||||
|
||||
dir-v (gpt/to-vec p0 p1)
|
||||
|
||||
p1c (gpt/add p0 (gpt/scale dir-v 0.5))
|
||||
p2c (gpt/add p3 (gpt/scale dir-v 0.5))
|
||||
|
||||
cp (gsi/line-line-intersect c1 (gpt/add c1 dir-v) p1c p2c)]
|
||||
|
||||
(gpt/to-vec c1 cp)))
|
||||
|
||||
(defn center-vertical-vector
|
||||
[child-points parent-points]
|
||||
(let [[p0 p1 p2 _] parent-points
|
||||
[_ c1 _ _] child-points
|
||||
|
||||
dir-v (gpt/to-vec p1 p2)
|
||||
|
||||
p3c (gpt/add p0 (gpt/scale dir-v 0.5))
|
||||
p2c (gpt/add p1 (gpt/scale dir-v 0.5))
|
||||
|
||||
cp (gsi/line-line-intersect c1 (gpt/add c1 dir-v) p3c p2c)]
|
||||
|
||||
(gpt/to-vec c1 cp)))
|
||||
|
||||
(defn start-vector
|
||||
[axis child-points parent-points]
|
||||
(let [pos-vector
|
||||
(cond (= :x axis) left-vector
|
||||
(= :y axis) top-vector)]
|
||||
(pos-vector child-points parent-points)))
|
||||
|
||||
(defn end-vector
|
||||
[axis child-points parent-points]
|
||||
(let [pos-vector
|
||||
(cond (= :x axis) right-vector
|
||||
(= :y axis) bottom-vector)]
|
||||
(pos-vector child-points parent-points)))
|
||||
|
||||
(defn center-vector
|
||||
[axis child-points parent-points]
|
||||
((if (= :x axis) center-horizontal-vector center-vertical-vector) child-points parent-points))
|
||||
|
||||
(defn displacement
|
||||
[before-v after-v]
|
||||
(let [angl (gpt/angle-with-other before-v after-v)
|
||||
sign (if (mth/close? angl 180) -1 1)
|
||||
length (* sign (gpt/length before-v))]
|
||||
(if (mth/almost-zero? length)
|
||||
after-v
|
||||
(gpt/subtract after-v (gpt/scale (gpt/unit after-v) length)))))
|
||||
|
||||
(defn side-vector
|
||||
[axis [c0 c1 _ c3]]
|
||||
(if (= axis :x)
|
||||
(gpt/to-vec c0 c1)
|
||||
(gpt/to-vec c0 c3)))
|
||||
|
||||
(defn side-vector-resize
|
||||
[axis [c0 c1 _ c3] start-vector end-vector]
|
||||
(if (= axis :x)
|
||||
(gpt/to-vec (gpt/add c0 start-vector) (gpt/add c1 end-vector))
|
||||
(gpt/to-vec (gpt/add c0 start-vector) (gpt/add c3 end-vector))))
|
||||
|
||||
;; Constraint function definitions
|
||||
|
||||
(defmulti constraint-modifier (fn [type & _] type))
|
||||
|
||||
(defmethod constraint-modifier :start
|
||||
[_ axis parent _ _ transformed-parent-rect]
|
||||
|
||||
(let [parent-rect (:selrect parent)
|
||||
delta-start (get-delta-start axis parent-rect transformed-parent-rect)]
|
||||
(if-not (mth/almost-zero? delta-start)
|
||||
{:displacement (get-displacement axis delta-start)}
|
||||
{})))
|
||||
[_ axis child-points-before parent-points-before child-points-after parent-points-after]
|
||||
(let [start-before (start-vector axis child-points-before parent-points-before)
|
||||
start-after (start-vector axis child-points-after parent-points-after)]
|
||||
(ctm/move-modifiers (displacement start-before start-after))))
|
||||
|
||||
(defmethod constraint-modifier :end
|
||||
[_ axis parent _ _ transformed-parent-rect]
|
||||
(let [parent-rect (:selrect parent)
|
||||
delta-end (get-delta-end axis parent-rect transformed-parent-rect)]
|
||||
(if-not (mth/almost-zero? delta-end)
|
||||
{:displacement (get-displacement axis delta-end)}
|
||||
{})))
|
||||
[_ axis child-points-before parent-points-before child-points-after parent-points-after]
|
||||
(let [end-before (end-vector axis child-points-before parent-points-before)
|
||||
end-after (end-vector axis child-points-after parent-points-after)]
|
||||
(ctm/move-modifiers (displacement end-before end-after))))
|
||||
|
||||
(defmethod constraint-modifier :fixed
|
||||
[_ axis parent child _ transformed-parent-rect]
|
||||
(let [parent-rect (:selrect parent)
|
||||
child-rect (gre/points->rect (:points child))
|
||||
[_ axis child-points-before parent-points-before child-points-after parent-points-after transformed-parent]
|
||||
(let [;; Same as constraint end
|
||||
end-before (end-vector axis child-points-before parent-points-before)
|
||||
end-after (end-vector axis child-points-after parent-points-after)
|
||||
start-before (start-vector axis child-points-before parent-points-before)
|
||||
start-after (start-vector axis child-points-after parent-points-after)
|
||||
|
||||
delta-start (get-delta-start axis parent-rect transformed-parent-rect)
|
||||
delta-size (get-delta-size axis parent-rect transformed-parent-rect)
|
||||
child-size (get-size axis child-rect)]
|
||||
(if (or (not (mth/almost-zero? delta-start))
|
||||
(not (mth/almost-zero? delta-size)))
|
||||
disp-end (displacement end-before end-after)
|
||||
disp-start (displacement start-before start-after)
|
||||
|
||||
{:displacement (get-displacement axis delta-start)
|
||||
:resize-origin (get-displacement axis delta-start (:x child-rect) (:y child-rect))
|
||||
:resize-vector (get-scale axis (/ (+ child-size delta-size) child-size))}
|
||||
{})))
|
||||
;; We get the current axis side and grow it on both side by the end+start displacements
|
||||
before-vec (side-vector axis child-points-after)
|
||||
after-vec (side-vector-resize axis child-points-after disp-start disp-end)
|
||||
|
||||
;; after-vec will contain the side length of the grown side
|
||||
;; we scale the shape by the diference and translate it by the start
|
||||
;; displacement (so its left+top position is constant)
|
||||
scale (/ (gpt/length after-vec) (gpt/length before-vec))
|
||||
|
||||
resize-origin (first child-points-after)
|
||||
{:keys [transform transform-inverse]} transformed-parent]
|
||||
|
||||
(-> (ctm/empty)
|
||||
(ctm/resize (get-scale axis scale) resize-origin transform transform-inverse)
|
||||
(ctm/move disp-start))))
|
||||
|
||||
(defmethod constraint-modifier :center
|
||||
[_ axis parent _ _ transformed-parent-rect]
|
||||
(let [parent-rect (:selrect parent)
|
||||
parent-center (gco/center-rect parent-rect)
|
||||
transformed-parent-center (gco/center-rect transformed-parent-rect)
|
||||
delta-center (get-delta-center axis parent-center transformed-parent-center)]
|
||||
(if-not (mth/almost-zero? delta-center)
|
||||
{:displacement (get-displacement axis delta-center)}
|
||||
{})))
|
||||
|
||||
(defmethod constraint-modifier :scale
|
||||
[_ axis _ _ modifiers _]
|
||||
(let [{:keys [resize-vector resize-vector-2 displacement]} modifiers]
|
||||
(cond-> {}
|
||||
(and (some? resize-vector)
|
||||
(not= (axis resize-vector) 1))
|
||||
(assoc :resize-origin (:resize-origin modifiers)
|
||||
:resize-vector (if (= :x axis)
|
||||
(gpt/point (:x resize-vector) 1)
|
||||
(gpt/point 1 (:y resize-vector))))
|
||||
|
||||
(and (= :y axis) (some? resize-vector-2)
|
||||
(not (mth/close? (:y resize-vector-2) 1)))
|
||||
(assoc :resize-origin (:resize-origin-2 modifiers)
|
||||
:resize-vector (gpt/point 1 (:y resize-vector-2)))
|
||||
|
||||
(some? displacement)
|
||||
(assoc :displacement
|
||||
(get-displacement axis (-> (gpt/point 0 0)
|
||||
(gpt/transform displacement)
|
||||
(gpt/transform (:resize-transform-inverse modifiers (gmt/matrix)))
|
||||
axis))))))
|
||||
[_ axis child-points-before parent-points-before child-points-after parent-points-after]
|
||||
(let [center-before (center-vector axis child-points-before parent-points-before)
|
||||
center-after (center-vector axis child-points-after parent-points-after)]
|
||||
(ctm/move-modifiers (displacement center-before center-after))))
|
||||
|
||||
(defmethod constraint-modifier :default [_ _ _ _ _]
|
||||
{})
|
||||
[])
|
||||
|
||||
(def const->type+axis
|
||||
{:left :start
|
||||
|
@ -152,77 +245,73 @@
|
|||
:top
|
||||
:scale)))
|
||||
|
||||
(defn clean-modifiers
|
||||
"Remove redundant modifiers"
|
||||
[{:keys [displacement resize-vector resize-vector-2] :as modifiers}]
|
||||
(defn bounding-box-parent-transform
|
||||
"Returns a bounding box for the child in the same coordinate system
|
||||
as the parent.
|
||||
Returns a points array"
|
||||
[child parent]
|
||||
(-> child
|
||||
:points
|
||||
(gco/transform-points (:transform-inverse parent))
|
||||
(gre/points->rect)
|
||||
(gre/rect->points) ;; Restore to points so we can transform them
|
||||
(gco/transform-points (:transform parent))))
|
||||
|
||||
(cond-> modifiers
|
||||
;; Displacement with value 0. We don't move in any direction
|
||||
(and (some? displacement)
|
||||
(mth/almost-zero? (:e displacement))
|
||||
(mth/almost-zero? (:f displacement)))
|
||||
(dissoc :displacement)
|
||||
(defn normalize-modifiers
|
||||
"Before aplying constraints we need to remove the deformation caused by the resizing of the parent"
|
||||
[constraints-h constraints-v modifiers child parent transformed-child {:keys [transform transform-inverse] :as transformed-parent}]
|
||||
|
||||
;; Resize with value very close to 1 means no resize
|
||||
(and (some? resize-vector)
|
||||
(mth/almost-zero? (- 1.0 (:x resize-vector)))
|
||||
(mth/almost-zero? (- 1.0 (:y resize-vector))))
|
||||
(dissoc :resize-origin :resize-vector)
|
||||
(let [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 gpo/origin)]
|
||||
|
||||
(and (some? resize-vector)
|
||||
(mth/almost-zero? (- 1.0 (:x resize-vector-2)))
|
||||
(mth/almost-zero? (- 1.0 (:y resize-vector-2))))
|
||||
(dissoc :resize-origin-2 :resize-vector-2)))
|
||||
(cond-> modifiers
|
||||
(not= :scale constraints-h)
|
||||
(ctm/resize (gpt/point scale-x 1) resize-origin transform transform-inverse)
|
||||
|
||||
(not= :scale constraints-v)
|
||||
(ctm/resize (gpt/point 1 scale-y) resize-origin transform transform-inverse))))
|
||||
|
||||
(defn calc-child-modifiers
|
||||
[parent child modifiers ignore-constraints transformed-parent-rect]
|
||||
[parent child modifiers ignore-constraints transformed-parent]
|
||||
|
||||
(if (and (nil? (:resize-vector modifiers))
|
||||
(nil? (:resize-vector-2 modifiers)))
|
||||
;; If we don't have a resize modifier we return the same modifiers
|
||||
modifiers
|
||||
(let [constraints-h
|
||||
(if-not ignore-constraints
|
||||
(:constraints-h child (default-constraints-h child))
|
||||
:scale)
|
||||
(let [modifiers (ctm/select-child-modifiers modifiers)
|
||||
|
||||
constraints-v
|
||||
(if-not ignore-constraints
|
||||
(:constraints-v child (default-constraints-v child))
|
||||
:scale)
|
||||
constraints-h
|
||||
(if-not ignore-constraints
|
||||
(:constraints-h child (default-constraints-h child))
|
||||
:scale)
|
||||
|
||||
modifiers-h (constraint-modifier (constraints-h const->type+axis) :x parent child modifiers transformed-parent-rect)
|
||||
modifiers-v (constraint-modifier (constraints-v const->type+axis) :y parent child modifiers transformed-parent-rect)]
|
||||
constraints-v
|
||||
(if-not ignore-constraints
|
||||
(:constraints-v child (default-constraints-v child))
|
||||
:scale)]
|
||||
|
||||
;; Build final child modifiers. Apply transform again to the result, to get the
|
||||
;; real modifiers that need to be applied to the child, including rotation as needed.
|
||||
(cond-> {}
|
||||
(some? (:displacement-after modifiers))
|
||||
(assoc :displacement-after (:displacement-after modifiers))
|
||||
(if (and (= :scale constraints-h) (= :scale constraints-v))
|
||||
modifiers
|
||||
|
||||
(or (contains? modifiers-h :displacement)
|
||||
(contains? modifiers-v :displacement))
|
||||
(assoc :displacement (cond-> (gpt/point (get-in modifiers-h [:displacement :x] 0)
|
||||
(get-in modifiers-v [:displacement :y] 0))
|
||||
(some? (:resize-transform modifiers))
|
||||
(gpt/transform (:resize-transform modifiers))
|
||||
(let [transformed-child (gst/transform-shape child (ctm/select-child-modifiers modifiers))
|
||||
modifiers (normalize-modifiers constraints-h constraints-v modifiers child parent transformed-child transformed-parent)
|
||||
|
||||
:always
|
||||
(gmt/translate-matrix)))
|
||||
transformed-child (gst/transform-shape child modifiers)
|
||||
|
||||
(:resize-vector modifiers-h)
|
||||
(assoc :resize-origin (:resize-origin modifiers-h)
|
||||
:resize-vector (gpt/point (get-in modifiers-h [:resize-vector :x] 1)
|
||||
(get-in modifiers-h [:resize-vector :y] 1)))
|
||||
parent-points-before (bounding-box-parent-transform parent parent)
|
||||
child-points-before (bounding-box-parent-transform child parent)
|
||||
parent-points-after (bounding-box-parent-transform transformed-parent transformed-parent)
|
||||
child-points-after (bounding-box-parent-transform transformed-child transformed-parent)
|
||||
|
||||
(:resize-vector modifiers-v)
|
||||
(assoc :resize-origin-2 (:resize-origin modifiers-v)
|
||||
:resize-vector-2 (gpt/point (get-in modifiers-v [:resize-vector :x] 1)
|
||||
(get-in modifiers-v [:resize-vector :y] 1)))
|
||||
modifiers-h (constraint-modifier (constraints-h const->type+axis) :x
|
||||
child-points-before parent-points-before
|
||||
child-points-after parent-points-after
|
||||
transformed-parent)
|
||||
|
||||
(:resize-transform modifiers)
|
||||
(assoc :resize-transform (:resize-transform modifiers)
|
||||
:resize-transform-inverse (:resize-transform-inverse modifiers))
|
||||
modifiers-v (constraint-modifier (constraints-v const->type+axis) :y
|
||||
child-points-before parent-points-before
|
||||
child-points-after parent-points-after
|
||||
transformed-parent)]
|
||||
|
||||
:always
|
||||
(clean-modifiers)))))
|
||||
(-> modifiers
|
||||
(ctm/add-modifiers modifiers-h)
|
||||
(ctm/add-modifiers modifiers-v))))))
|
||||
|
|
21
common/src/app/common/geom/shapes/flex_layout.cljc
Normal file
21
common/src/app/common/geom/shapes/flex_layout.cljc
Normal file
|
@ -0,0 +1,21 @@
|
|||
;; 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
|
||||
(:require
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.geom.shapes.flex-layout.bounds :as fbo]
|
||||
[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 fbo/layout-content-bounds)
|
||||
(dm/export fdr/get-drop-index)
|
||||
(dm/export fdr/layout-drop-areas)
|
||||
(dm/export fli/calc-layout-data)
|
||||
(dm/export fmo/layout-child-modifiers)
|
||||
(dm/export fmo/normalize-child-modifiers)
|
||||
|
112
common/src/app/common/geom/shapes/flex_layout/bounds.cljc
Normal file
112
common/src/app/common/geom/shapes/flex_layout/bounds.cljc
Normal file
|
@ -0,0 +1,112 @@
|
|||
;; 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.bounds
|
||||
(:require
|
||||
[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 gre]
|
||||
[app.common.math :as mth]
|
||||
[app.common.types.shape.layout :as ctl]))
|
||||
|
||||
(defn- child-layout-bound-points
|
||||
"Returns the bounds of the children as points"
|
||||
[parent child]
|
||||
|
||||
(let [row? (ctl/row? parent)
|
||||
col? (ctl/col? parent)
|
||||
|
||||
hv (partial gpo/start-hv (:points parent))
|
||||
vv (partial gpo/start-vv (:points parent))
|
||||
|
||||
v-start? (ctl/v-start? parent)
|
||||
v-center? (ctl/v-center? parent)
|
||||
v-end? (ctl/v-end? parent)
|
||||
h-start? (ctl/h-start? parent)
|
||||
h-center? (ctl/h-center? parent)
|
||||
h-end? (ctl/h-end? parent)
|
||||
|
||||
base-p (first (:points child))
|
||||
|
||||
width (-> child :selrect :width)
|
||||
height (-> child :selrect :height)
|
||||
|
||||
min-width (if (ctl/fill-width? child)
|
||||
(ctl/child-min-width child)
|
||||
width)
|
||||
|
||||
min-height (if (ctl/fill-height? child)
|
||||
(ctl/child-min-height child)
|
||||
height)
|
||||
|
||||
;; This is the leftmost (when row) or topmost (when col) point
|
||||
;; Will be added always to the bounds and then calculated the other limits
|
||||
;; from there
|
||||
base-p (cond-> base-p
|
||||
(and row? v-center?)
|
||||
(gpt/add (vv (/ height 2)))
|
||||
|
||||
(and row? v-end?)
|
||||
(gpt/add (vv height))
|
||||
|
||||
(and col? h-center?)
|
||||
(gpt/add (hv (/ width 2)))
|
||||
|
||||
(and col? h-end?)
|
||||
(gpt/add (hv width)))]
|
||||
|
||||
(cond-> [base-p]
|
||||
(and (mth/almost-zero? min-width) (mth/almost-zero? min-height))
|
||||
(conj (cond-> base-p
|
||||
row?
|
||||
(gpt/add (hv width))
|
||||
|
||||
col?
|
||||
(gpt/add (vv height))))
|
||||
|
||||
(not (mth/almost-zero? min-width))
|
||||
(conj (cond-> base-p
|
||||
(or row? h-start?)
|
||||
(gpt/add (hv min-width))
|
||||
|
||||
(and col? h-center?)
|
||||
(gpt/add (hv (/ min-width 2)))
|
||||
|
||||
(and col? h-center?)
|
||||
(gpt/subtract (hv min-width))))
|
||||
|
||||
(not (mth/almost-zero? min-height))
|
||||
(conj (cond-> base-p
|
||||
(or col? v-start?)
|
||||
(gpt/add (vv min-height))
|
||||
|
||||
(and row? v-center?)
|
||||
(gpt/add (vv (/ min-height 2)))
|
||||
|
||||
(and row? v-end?)
|
||||
(gpt/subtract (vv min-height)))))))
|
||||
|
||||
(defn layout-content-bounds
|
||||
[{:keys [layout-padding] :as parent} children]
|
||||
|
||||
(let [{pad-top :p1 pad-right :p2 pad-bottom :p3 pad-left :p4} layout-padding
|
||||
pad-top (or pad-top 0)
|
||||
pad-right (or pad-right 0)
|
||||
pad-bottom (or pad-bottom 0)
|
||||
pad-left (or pad-left 0)
|
||||
|
||||
child-bounds
|
||||
(fn [{:keys [points] :as child}]
|
||||
(if (or (ctl/fill-height? child) (ctl/fill-height? child))
|
||||
(child-layout-bound-points parent child)
|
||||
points))]
|
||||
|
||||
(-> (mapcat child-bounds children)
|
||||
(gco/transform-points (gco/center-shape parent) (:transform-inverse parent))
|
||||
(gre/squared-points)
|
||||
(gpo/pad-points (- pad-top) (- pad-right) (- pad-bottom) (- pad-left))
|
||||
(gre/points->rect))))
|
194
common/src/app/common/geom/shapes/flex_layout/drop_area.cljc
Normal file
194
common/src/app/common/geom/shapes/flex_layout/drop_area.cljc
Normal file
|
@ -0,0 +1,194 @@
|
|||
;; 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.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 drop-child-areas
|
||||
[{:keys [transform-inverse] :as frame} parent-rect child index reverse? prev-x prev-y last?]
|
||||
|
||||
(let [col? (ctl/col? frame)
|
||||
row? (ctl/row? frame)
|
||||
[layout-gap-row layout-gap-col] (ctl/gaps frame)
|
||||
|
||||
start-p (-> child :points first)
|
||||
center (gco/center-shape frame)
|
||||
start-p (gmt/transform-point-center start-p center transform-inverse)
|
||||
|
||||
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)))]
|
||||
|
||||
(if row?
|
||||
(let [half-point-width (+ (- box-x x) (/ box-width 2))]
|
||||
[(gsr/make-rect x y width height)
|
||||
(-> (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 height)
|
||||
(-> (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))))]))))
|
||||
|
||||
(defn drop-line-area
|
||||
[{:keys [transform-inverse margin-x margin-y] :as frame}
|
||||
{:keys [start-p layout-gap-row layout-gap-col num-children line-width line-height] :as line-data}
|
||||
prev-x prev-y last?]
|
||||
|
||||
(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))
|
||||
|
||||
center (gco/center-shape frame)
|
||||
start-p (gmt/transform-point-center start-p center transform-inverse)
|
||||
|
||||
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)))]
|
||||
(gsr/make-rect x y width height)))
|
||||
|
||||
(defn layout-drop-areas
|
||||
"Retrieve the layout drop areas to move shapes inside layouts"
|
||||
[frame layout-data children]
|
||||
|
||||
(let [reverse? (:reverse? layout-data)
|
||||
children (vec (cond->> (d/enumerate children) reverse? reverse))
|
||||
lines (:layout-lines layout-data)]
|
||||
|
||||
(loop [areas []
|
||||
from-idx 0
|
||||
prev-line-x (:x frame)
|
||||
prev-line-y (:y frame)
|
||||
|
||||
current-line (first lines)
|
||||
lines (rest lines)]
|
||||
|
||||
(if (nil? current-line)
|
||||
areas
|
||||
|
||||
(let [line-area (drop-line-area frame current-line prev-line-x prev-line-y (nil? (first lines)))
|
||||
children (subvec children from-idx (+ from-idx (:num-children current-line)))
|
||||
|
||||
next-areas
|
||||
(loop [areas areas
|
||||
prev-child-x (:x line-area)
|
||||
prev-child-y (:y line-area)
|
||||
[index child] (first children)
|
||||
children (rest children)]
|
||||
|
||||
(if (nil? child)
|
||||
areas
|
||||
|
||||
(let [[child-area child-area-start child-area-end]
|
||||
(drop-child-areas frame line-area child index reverse? prev-child-x prev-child-y (nil? (first children)))]
|
||||
(recur (conj areas child-area-start child-area-end)
|
||||
(+ (:x child-area) (:width child-area))
|
||||
(+ (:y child-area) (:height child-area))
|
||||
(first children)
|
||||
(rest children)))))]
|
||||
|
||||
(recur next-areas
|
||||
(+ from-idx (:num-children current-line))
|
||||
(+ (:x line-area) (:width line-area))
|
||||
(+ (:y line-area) (:height line-area))
|
||||
(first lines)
|
||||
(rest lines)))))))
|
||||
|
||||
(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)))
|
319
common/src/app/common/geom/shapes/flex_layout/lines.cljc
Normal file
319
common/src/app/common/geom/shapes/flex_layout/lines.cljc
Normal file
|
@ -0,0 +1,319 @@
|
|||
;; 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.data :as d]
|
||||
[app.common.geom.shapes.flex-layout.positions :as flp]
|
||||
[app.common.geom.shapes.points :as gpo]
|
||||
[app.common.geom.shapes.transforms :as gst]
|
||||
[app.common.math :as mth]
|
||||
[app.common.types.shape.layout :as ctl]))
|
||||
|
||||
(def conjv (fnil conj []))
|
||||
|
||||
(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])]
|
||||
(gpo/pad-points (:points shape) 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 [col? (ctl/col? shape)
|
||||
row? (ctl/row? shape)
|
||||
|
||||
wrap? (and (ctl/wrap? shape)
|
||||
(or col? (not (ctl/auto-width? shape)))
|
||||
(or row? (not (ctl/auto-height? shape))))
|
||||
|
||||
[layout-gap-row layout-gap-col] (ctl/gaps shape)
|
||||
|
||||
layout-width (gpo/width-points layout-bounds)
|
||||
layout-height (gpo/height-points layout-bounds)]
|
||||
|
||||
(loop [line-data nil
|
||||
result []
|
||||
child (first children)
|
||||
children (rest children)]
|
||||
|
||||
(if (nil? child)
|
||||
(cond-> result (some? line-data) (conj line-data))
|
||||
|
||||
(let [{:keys [line-min-width line-min-height
|
||||
line-max-width line-max-height
|
||||
num-children
|
||||
children-data]} line-data
|
||||
|
||||
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)
|
||||
child-max-width (ctl/child-max-width child)
|
||||
child-max-height (ctl/child-max-height child)
|
||||
|
||||
[child-margin-top child-margin-right child-margin-bottom child-margin-left]
|
||||
(ctl/child-margins child)
|
||||
|
||||
child-margin-width (+ child-margin-left child-margin-right)
|
||||
child-margin-height (+ child-margin-top child-margin-bottom)
|
||||
|
||||
fill-width? (ctl/fill-width? child)
|
||||
fill-height? (ctl/fill-height? child)
|
||||
|
||||
;; We need this info later to calculate the child resizes when fill
|
||||
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-min-width (+ child-margin-width (if fill-width? child-min-width child-width))
|
||||
next-min-height (+ child-margin-height (if fill-height? child-min-height child-height))
|
||||
next-max-width (+ child-margin-width (if fill-width? child-max-width child-width))
|
||||
next-max-height (+ child-margin-height (if fill-height? child-max-height child-height))
|
||||
|
||||
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)
|
||||
(or (not wrap?)
|
||||
(and row? (<= next-line-min-width layout-width))
|
||||
(and col? (<= next-line-min-height layout-height))))
|
||||
|
||||
(recur {:line-min-width (if row? (+ line-min-width next-min-width) (max line-min-width next-min-width))
|
||||
: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)
|
||||
:children-data (conjv children-data child-data)}
|
||||
result
|
||||
(first children)
|
||||
(rest children))
|
||||
|
||||
(recur {:line-min-width next-min-width
|
||||
:line-min-height next-min-height
|
||||
:line-max-width next-max-width
|
||||
:line-max-height next-max-height
|
||||
:num-children 1
|
||||
:children-data [child-data]}
|
||||
(cond-> result (some? line-data) (conj line-data))
|
||||
(first children)
|
||||
(rest children))))))))
|
||||
|
||||
(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
|
||||
[parent layout-bounds layout-lines]
|
||||
|
||||
(let [row? (ctl/row? parent)
|
||||
col? (ctl/col? parent)
|
||||
|
||||
[layout-gap-row layout-gap-col] (ctl/gaps parent)
|
||||
|
||||
layout-width (gpo/width-points layout-bounds)
|
||||
layout-height (gpo/height-points layout-bounds)]
|
||||
|
||||
(letfn [(add-lines [[total-width total-height]
|
||||
{:keys [line-width 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 [total-width total-height num-lines [result base-p] layout-line]
|
||||
(let [start-p (flp/get-start-line parent layout-bounds layout-line base-p total-width total-height num-lines)
|
||||
next-p (flp/get-next-line parent layout-bounds layout-line base-p total-width total-height num-lines)]
|
||||
|
||||
[(conj result (assoc layout-line :start-p start-p))
|
||||
next-p]))]
|
||||
|
||||
(let [[total-min-width total-min-height total-max-width total-max-height]
|
||||
(->> layout-lines (reduce add-ranges [0 0 0 0]))
|
||||
|
||||
get-layout-width (fn [{:keys [num-children]}] (- layout-width (* layout-gap-row (dec num-children))))
|
||||
get-layout-height (fn [{:keys [num-children]}] (- layout-height (* layout-gap-col (dec num-children))))
|
||||
|
||||
num-lines (count layout-lines)
|
||||
|
||||
;; When align-items is stretch we need to adjust the main axis size to grow for the full content
|
||||
stretch-width-fix
|
||||
(if (and col? (ctl/content-stretch? parent))
|
||||
(/ (- layout-width (* layout-gap-row (dec num-lines)) total-max-width) num-lines)
|
||||
0)
|
||||
|
||||
stretch-height-fix
|
||||
(if (and row? (ctl/content-stretch? parent))
|
||||
(/ (- layout-height (* layout-gap-col (dec num-lines)) total-max-height) num-lines)
|
||||
0)
|
||||
|
||||
;; Distributes the space between the layout lines based on its max/min constraints
|
||||
layout-lines
|
||||
(cond->> layout-lines
|
||||
row?
|
||||
(map #(assoc % :line-width (max (:line-min-width %) (min (get-layout-width %) (:line-max-width %)))))
|
||||
|
||||
col?
|
||||
(map #(assoc % :line-height (max (:line-min-height %) (min (get-layout-height %) (:line-max-height %)))))
|
||||
|
||||
(and row? (>= total-min-height layout-height))
|
||||
(map #(assoc % :line-height (:line-min-height %)))
|
||||
|
||||
(and row? (<= total-max-height layout-height))
|
||||
(map #(assoc % :line-height (+ (:line-max-height %) stretch-height-fix)))
|
||||
|
||||
(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 %) stretch-width-fix)))
|
||||
|
||||
(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 (flp/get-base-line parent layout-bounds total-width total-height num-lines)]
|
||||
|
||||
(first (reduce (partial add-starts total-width total-height num-lines) [[] base-p] layout-lines))))))
|
||||
|
||||
(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 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?
|
||||
(map #(assoc % :child-width (:child-min-width %)))
|
||||
|
||||
col?
|
||||
(map #(assoc % :child-height (:child-min-height %)))
|
||||
|
||||
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
|
||||
"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)
|
||||
(into []
|
||||
(comp (map (partial add-line-spacing shape layout-bounds))
|
||||
(map (partial add-children-resizes shape)))))]
|
||||
|
||||
{:layout-lines layout-lines
|
||||
:layout-bounds layout-bounds
|
||||
:reverse? reverse?}))
|
100
common/src/app/common/geom/shapes/flex_layout/modifiers.cljc
Normal file
100
common/src/app/common/geom/shapes/flex_layout/modifiers.cljc
Normal file
|
@ -0,0 +1,100 @@
|
|||
;; 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"
|
||||
[modifiers parent child {: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 gpo/origin)
|
||||
resize-vector (gpt/point scale-x scale-y)]
|
||||
(-> modifiers
|
||||
(ctm/select-child-modifiers)
|
||||
(ctm/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}
|
||||
child
|
||||
child-origin child-width
|
||||
{:keys [children-data line-width] :as layout-data}]
|
||||
|
||||
(cond
|
||||
(ctl/row? parent)
|
||||
(let [target-width (max (get-in children-data [(:id child) :child-width]) 0.01)
|
||||
fill-scale (/ target-width child-width)]
|
||||
{:width target-width
|
||||
:modifiers (ctm/resize-modifiers (gpt/point fill-scale 1) child-origin transform transform-inverse)})
|
||||
|
||||
(ctl/col? parent)
|
||||
(let [target-width (max (- line-width (ctl/child-width-margin child)) 0.01)
|
||||
max-width (ctl/child-max-width child)
|
||||
target-width (min max-width target-width)
|
||||
fill-scale (/ target-width child-width)]
|
||||
{:width target-width
|
||||
:modifiers (ctm/resize-modifiers (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}
|
||||
child
|
||||
child-origin child-height
|
||||
{:keys [children-data line-height] :as layout-data}]
|
||||
|
||||
(cond
|
||||
(ctl/col? parent)
|
||||
(let [target-height (max (get-in children-data [(:id child) :child-height]) 0.01)
|
||||
fill-scale (/ target-height child-height)]
|
||||
{:height target-height
|
||||
:modifiers (ctm/resize-modifiers (gpt/point 1 fill-scale) child-origin transform transform-inverse)})
|
||||
|
||||
(ctl/row? parent)
|
||||
(let [target-height (max (- line-height (ctl/child-height-margin child)) 0.01)
|
||||
max-height (ctl/child-max-height child)
|
||||
target-height (min max-height target-height)
|
||||
fill-scale (/ target-height child-height)]
|
||||
{:height target-height
|
||||
:modifiers (ctm/resize-modifiers (gpt/point 1 fill-scale) child-origin transform transform-inverse)})))
|
||||
|
||||
(defn layout-child-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 (when (ctl/fill-width? child) (calc-fill-width-data parent child child-origin child-width layout-line))
|
||||
fill-height (when (ctl/fill-height? child) (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 child-width child-height layout-line)
|
||||
|
||||
move-vec (gpt/to-vec child-origin corner-p)
|
||||
|
||||
modifiers
|
||||
(-> (ctm/empty)
|
||||
(cond-> fill-width (ctm/add-modifiers (:modifiers fill-width)))
|
||||
(cond-> fill-height (ctm/add-modifiers (:modifiers fill-height)))
|
||||
(ctm/move move-vec))]
|
||||
|
||||
[modifiers layout-line]))
|
277
common/src/app/common/geom/shapes/flex_layout/positions.cljc
Normal file
277
common/src/app/common/geom/shapes/flex_layout/positions.cljc
Normal file
|
@ -0,0 +1,277 @@
|
|||
;; 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-base-line
|
||||
[parent layout-bounds total-width total-height num-lines]
|
||||
|
||||
(let [layout-width (gpo/width-points layout-bounds)
|
||||
layout-height (gpo/height-points layout-bounds)
|
||||
row? (ctl/row? parent)
|
||||
col? (ctl/col? parent)
|
||||
hv (partial gpo/start-hv layout-bounds)
|
||||
vv (partial gpo/start-vv layout-bounds)
|
||||
|
||||
end? (ctl/content-end? parent)
|
||||
center? (ctl/content-center? parent)
|
||||
around? (ctl/content-around? parent)
|
||||
|
||||
;; 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)
|
||||
|
||||
free-width-gap (- layout-width total-width lines-gap-row)
|
||||
free-height-gap (- layout-height total-height lines-gap-col)
|
||||
free-width (- layout-width total-width)
|
||||
free-height (- layout-height total-height)]
|
||||
|
||||
(cond-> (gpo/origin layout-bounds)
|
||||
row?
|
||||
(cond-> center?
|
||||
(gpt/add (vv (/ free-height-gap 2)))
|
||||
|
||||
end?
|
||||
(gpt/add (vv free-height-gap))
|
||||
|
||||
around?
|
||||
(gpt/add (vv (/ free-height (inc num-lines)))))
|
||||
|
||||
col?
|
||||
(cond-> center?
|
||||
(gpt/add (hv (/ free-width-gap 2)))
|
||||
|
||||
end?
|
||||
(gpt/add (hv free-width-gap))
|
||||
|
||||
around?
|
||||
(gpt/add (hv (/ free-width (inc num-lines))))))))
|
||||
|
||||
(defn get-next-line
|
||||
[parent layout-bounds {:keys [line-width line-height]} base-p total-width total-height num-lines]
|
||||
|
||||
(let [layout-width (gpo/width-points layout-bounds)
|
||||
layout-height (gpo/height-points layout-bounds)
|
||||
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 %)
|
||||
|
||||
stretch? (ctl/content-stretch? parent)
|
||||
between? (ctl/content-between? parent)
|
||||
around? (ctl/content-around? parent)
|
||||
|
||||
free-width (- layout-width total-width)
|
||||
free-height (- layout-height total-height)
|
||||
|
||||
line-gap-row
|
||||
(cond
|
||||
stretch?
|
||||
(/ free-width num-lines)
|
||||
|
||||
between?
|
||||
(/ free-width (dec num-lines))
|
||||
|
||||
around?
|
||||
(/ free-width (inc num-lines))
|
||||
|
||||
:else
|
||||
layout-gap-row)
|
||||
|
||||
line-gap-col
|
||||
(cond
|
||||
stretch?
|
||||
(/ free-height num-lines)
|
||||
|
||||
between?
|
||||
(/ free-height (dec num-lines))
|
||||
|
||||
around?
|
||||
(/ free-height (inc num-lines))
|
||||
|
||||
:else
|
||||
layout-gap-col)]
|
||||
|
||||
(cond-> base-p
|
||||
row?
|
||||
(gpt/add (vv (+ line-height (max layout-gap-col line-gap-col))))
|
||||
|
||||
col?
|
||||
(gpt/add (hv (+ line-width (max layout-gap-row line-gap-row)))))))
|
||||
|
||||
(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]} base-p total-width total-height num-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)
|
||||
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)
|
||||
content-stretch? (ctl/content-stretch? parent)
|
||||
hv (partial gpo/start-hv layout-bounds)
|
||||
vv (partial gpo/start-vv layout-bounds)
|
||||
children-gap-width (* layout-gap-row (dec num-children))
|
||||
children-gap-height (* layout-gap-col (dec num-children))
|
||||
|
||||
line-height
|
||||
(if (and row? content-stretch?)
|
||||
(+ line-height (/ (- layout-height total-height) num-lines))
|
||||
line-height)
|
||||
|
||||
line-width
|
||||
(if (and col? content-stretch?)
|
||||
(+ line-width (/ (- layout-width total-width) num-lines))
|
||||
line-width)
|
||||
|
||||
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))))
|
||||
|
||||
;; 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)))))]
|
||||
|
||||
start-p))
|
||||
|
||||
(defn get-child-position
|
||||
"Calculates the position for the current shape given the layout-data context"
|
||||
[parent child
|
||||
child-width child-height
|
||||
{:keys [start-p layout-gap-row layout-gap-col margin-x margin-y line-height line-width] :as layout-data}]
|
||||
|
||||
(let [row? (ctl/row? parent)
|
||||
col? (ctl/col? parent)
|
||||
h-start? (ctl/h-start? parent)
|
||||
h-center? (ctl/h-center? parent)
|
||||
h-end? (ctl/h-end? parent)
|
||||
v-start? (ctl/v-start? parent)
|
||||
v-center? (ctl/v-center? parent)
|
||||
v-end? (ctl/v-end? parent)
|
||||
|
||||
self-start? (ctl/align-self-start? child)
|
||||
self-end? (ctl/align-self-end? child)
|
||||
self-center? (ctl/align-self-center? child)
|
||||
align-self? (or self-start? self-end? self-center?)
|
||||
|
||||
v-start? (if (or col? (not align-self?)) v-start? self-start?)
|
||||
v-center? (if (or col? (not align-self?)) v-center? self-center?)
|
||||
v-end? (if (or col? (not align-self?)) v-end? self-end?)
|
||||
|
||||
h-start? (if (or row? (not align-self?)) h-start? self-start?)
|
||||
h-center? (if (or row? (not align-self?)) h-center? self-center?)
|
||||
h-end? (if (or row? (not align-self?)) h-end? self-end?)
|
||||
|
||||
[margin-top margin-right margin-bottom margin-left] (ctl/child-margins child)
|
||||
|
||||
points (:points parent)
|
||||
hv (partial gpo/start-hv points)
|
||||
vv (partial gpo/start-vv points)
|
||||
|
||||
corner-p
|
||||
(cond-> start-p
|
||||
;; COLUMN DIRECTION
|
||||
col?
|
||||
(cond-> (some? margin-top)
|
||||
(gpt/add (vv margin-top))
|
||||
|
||||
h-center?
|
||||
(gpt/add (hv (- (/ child-width 2))))
|
||||
|
||||
h-end?
|
||||
(gpt/add (hv (- child-width)))
|
||||
|
||||
h-start?
|
||||
(gpt/add (hv margin-left))
|
||||
|
||||
h-center?
|
||||
(gpt/add (hv (+ (/ line-width 2) (/ (- margin-left margin-right) 2))))
|
||||
|
||||
h-end?
|
||||
(gpt/add (hv (+ line-width (- margin-right)))))
|
||||
|
||||
;; ROW DIRECTION
|
||||
row?
|
||||
(cond-> v-center?
|
||||
(gpt/add (vv (- (/ child-height 2))))
|
||||
|
||||
v-end?
|
||||
(gpt/add (vv (- child-height)))
|
||||
|
||||
(some? margin-left)
|
||||
(gpt/add (hv margin-left))
|
||||
|
||||
v-start?
|
||||
(gpt/add (vv margin-top))
|
||||
|
||||
v-center?
|
||||
(gpt/add (vv (+ (/ line-height 2) (/ (- margin-top margin-bottom) 2))))
|
||||
|
||||
v-end?
|
||||
(gpt/add (vv (+ line-height (- margin-bottom)))))
|
||||
|
||||
;; Margins
|
||||
(some? margin-x)
|
||||
(gpt/add (hv margin-x))
|
||||
|
||||
(some? margin-y)
|
||||
(gpt/add (vv margin-y)))
|
||||
|
||||
;; Fix position when layout is flipped
|
||||
corner-p
|
||||
(cond-> corner-p
|
||||
(:flip-x parent)
|
||||
(gpt/add (hv child-width))
|
||||
|
||||
(:flip-y parent)
|
||||
(gpt/add (vv child-height)))
|
||||
|
||||
next-p
|
||||
(cond-> start-p
|
||||
row?
|
||||
(-> (gpt/add (hv (+ child-width layout-gap-row)))
|
||||
(gpt/add (hv (+ margin-left margin-right))))
|
||||
|
||||
col?
|
||||
(-> (gpt/add (vv (+ margin-top margin-bottom)))
|
||||
(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]))
|
|
@ -348,3 +348,25 @@
|
|||
:points
|
||||
(every? (partial has-point-rect? rect))))
|
||||
|
||||
|
||||
(defn line-line-intersect
|
||||
"Calculates the interesection point for two lines given by the points a-b and b-c"
|
||||
[a b c d]
|
||||
|
||||
(let [;; Line equation representation: ax + by + c = 0
|
||||
a1 (- (:y b) (:y a))
|
||||
b1 (- (:x a) (:x b))
|
||||
c1 (+ (* a1 (:x a)) (* b1 (:y a)))
|
||||
|
||||
a2 (- (:y d) (:y c))
|
||||
b2 (- (:x c) (:x d))
|
||||
c2 (+ (* a2 (:x c)) (* b2 (:y c)))
|
||||
|
||||
;; Cramer's rule
|
||||
det (- (* a1 b2) (* a2 b1))]
|
||||
|
||||
;; If almost zero the lines are parallel
|
||||
(when (not (mth/almost-zero? det))
|
||||
(let [x (/ (- (* b2 c1) (* b1 c2)) det)
|
||||
y (/ (- (* c2 a1) (* c1 a2)) det)]
|
||||
(gpt/point x y)))))
|
||||
|
|
|
@ -1,327 +0,0 @@
|
|||
;; 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.layout
|
||||
(:require
|
||||
[app.common.geom.matrix :as gmt]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes.rect :as gre]))
|
||||
|
||||
;; :layout ;; true if active, false if not
|
||||
;; :layout-flex-dir ;; :row, :column, :reverse-row, :reverse-column
|
||||
;; :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-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-h-orientation]}]
|
||||
(= layout-h-orientation :left))
|
||||
|
||||
(defn h-center?
|
||||
[{:keys [layout-h-orientation]}]
|
||||
(= layout-h-orientation :center))
|
||||
|
||||
(defn h-end?
|
||||
[{:keys [layout-h-orientation]}]
|
||||
(= layout-h-orientation :right))
|
||||
|
||||
(defn v-start?
|
||||
[{:keys [layout-v-orientation]}]
|
||||
(= layout-v-orientation :top))
|
||||
|
||||
(defn v-center?
|
||||
[{:keys [layout-v-orientation]}]
|
||||
(= layout-v-orientation :center))
|
||||
|
||||
(defn v-end?
|
||||
[{:keys [layout-v-orientation]}]
|
||||
(= layout-v-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-lines
|
||||
[{:keys [layout-gap layout-wrap-type] :as shape} children {:keys [width height] :as layout-bounds}]
|
||||
|
||||
(let [wrap? (= layout-wrap-type :wrap)
|
||||
|
||||
reduce-fn
|
||||
(fn [[{:keys [line-width line-height num-children] :as line-data} result] child]
|
||||
(let [child-bounds (-> child :points gre/points->rect)
|
||||
next-width (-> child-bounds :width)
|
||||
next-height (-> child-bounds :height)]
|
||||
|
||||
(if (and (some? line-data)
|
||||
(or (not wrap?)
|
||||
(and (col? shape) (<= (+ line-width next-width (* layout-gap num-children)) width))
|
||||
(and (row? shape) (<= (+ line-height next-height (* layout-gap num-children)) height))))
|
||||
|
||||
[{:line-width (if (col? shape) (+ line-width next-width) (max line-width next-width))
|
||||
:line-height (if (row? shape) (+ line-height next-height) (max line-height next-height))
|
||||
:num-children (inc num-children)}
|
||||
result]
|
||||
|
||||
[{:line-width next-width
|
||||
:line-height next-height
|
||||
:num-children 1}
|
||||
(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-gap layout-type] :as shape} {:keys [x y width height]} layout-lines]
|
||||
|
||||
(letfn [(get-base-line
|
||||
[total-width total-height]
|
||||
|
||||
(let [base-x
|
||||
(cond
|
||||
(and (row? shape) (h-center? shape))
|
||||
(+ x (/ (- width total-width) 2))
|
||||
|
||||
(and (row? shape) (h-end? shape))
|
||||
(+ x width (- total-width))
|
||||
|
||||
:else x)
|
||||
|
||||
base-y
|
||||
(cond
|
||||
(and (col? shape) (v-center? shape))
|
||||
(+ y (/ (- height total-height) 2))
|
||||
|
||||
(and (col? shape) (v-end? shape))
|
||||
(+ y height (- total-height))
|
||||
|
||||
:else y)]
|
||||
|
||||
[base-x base-y]))
|
||||
|
||||
(get-start-line
|
||||
[{:keys [line-width line-height num-children]} base-x base-y]
|
||||
|
||||
(let [children-gap (* layout-gap (dec num-children))
|
||||
|
||||
start-x
|
||||
(cond
|
||||
(or (and (col? shape) (= :space-between layout-type))
|
||||
(and (col? shape) (= :space-around layout-type)))
|
||||
x
|
||||
|
||||
(and (col? shape) (h-center? shape))
|
||||
(- (+ x (/ width 2)) (/ (+ line-width children-gap) 2))
|
||||
|
||||
(and (col? shape) (h-end? shape))
|
||||
(- (+ x width) (+ line-width children-gap))
|
||||
|
||||
(and (row? shape) (h-center? shape))
|
||||
(+ base-x (/ line-width 2))
|
||||
|
||||
(and (row? shape) (h-end? shape))
|
||||
(+ base-x line-width)
|
||||
|
||||
(row? shape)
|
||||
base-x
|
||||
|
||||
:else
|
||||
x)
|
||||
|
||||
start-y
|
||||
(cond
|
||||
(or (and (row? shape) (= :space-between layout-type))
|
||||
(and (row? shape) (= :space-around layout-type)))
|
||||
y
|
||||
|
||||
(and (row? shape) (v-center? shape))
|
||||
(- (+ y (/ height 2)) (/ (+ line-height children-gap) 2))
|
||||
|
||||
(and (row? shape) (v-end? shape))
|
||||
(- (+ y height) (+ line-height children-gap))
|
||||
|
||||
(and (col? shape) (v-center? shape))
|
||||
(+ base-y (/ line-height 2))
|
||||
|
||||
(and (col? shape) (v-end? shape))
|
||||
(+ base-y line-height)
|
||||
|
||||
(col? shape)
|
||||
base-y
|
||||
|
||||
:else
|
||||
y)]
|
||||
[start-x start-y]))
|
||||
|
||||
(get-next-line
|
||||
[{:keys [line-width line-height]} base-x base-y]
|
||||
(let [next-x (if (col? shape) base-x (+ base-x line-width layout-gap))
|
||||
next-y (if (row? shape) base-y (+ base-y line-height layout-gap))]
|
||||
[next-x next-y]))
|
||||
|
||||
(add-lines [[total-width total-height] {:keys [line-width line-height]}]
|
||||
[(+ total-width line-width)
|
||||
(+ total-height line-height)])
|
||||
|
||||
(add-starts [[result base-x base-y] layout-line]
|
||||
(let [[start-x start-y] (get-start-line layout-line base-x base-y)
|
||||
[next-x next-y] (get-next-line layout-line base-x base-y)]
|
||||
[(conj result
|
||||
(assoc layout-line
|
||||
:start-x start-x
|
||||
:start-y start-y))
|
||||
next-x
|
||||
next-y]))]
|
||||
|
||||
(let [[total-width total-height]
|
||||
(->> layout-lines (reduce add-lines [0 0]))
|
||||
|
||||
total-width (+ total-width (* layout-gap (dec (count layout-lines))))
|
||||
total-height (+ total-height (* layout-gap (dec (count layout-lines))))
|
||||
|
||||
[base-x base-y]
|
||||
(get-base-line total-width total-height)
|
||||
|
||||
[layout-lines _ _ _ _]
|
||||
(reduce add-starts [[] base-x base-y] layout-lines)]
|
||||
layout-lines)))
|
||||
|
||||
(defn calc-layout-line-data
|
||||
[{:keys [layout-type layout-gap] :as shape}
|
||||
{:keys [width height] :as layout-bounds}
|
||||
{:keys [num-children line-width line-height] :as line-data}]
|
||||
|
||||
(let [layout-gap
|
||||
(cond
|
||||
(= :packed layout-type)
|
||||
layout-gap
|
||||
|
||||
(= :space-around layout-type)
|
||||
0
|
||||
|
||||
(and (col? shape) (= :space-between layout-type))
|
||||
(/ (- width line-width) (dec num-children))
|
||||
|
||||
(and (row? shape) (= :space-between layout-type))
|
||||
(/ (- height line-height) (dec num-children)))
|
||||
|
||||
margin-x
|
||||
(if (and (col? shape) (= :space-around layout-type))
|
||||
(/ (- width line-width) (inc num-children) )
|
||||
0)
|
||||
|
||||
margin-y
|
||||
(if (and (row? shape) (= :space-around layout-type))
|
||||
(/ (- height line-height) (inc num-children))
|
||||
0)]
|
||||
|
||||
(assoc line-data
|
||||
:layout-gap layout-gap
|
||||
:margin-x margin-x
|
||||
:margin-y margin-y)))
|
||||
|
||||
|
||||
(defn calc-layout-data
|
||||
"Digest the layout data to pass it to the constrains"
|
||||
[{:keys [layout-flex-dir] :as shape} children layout-bounds]
|
||||
|
||||
(let [reverse? (or (= :reverse-row layout-flex-dir) (= :reverse-column layout-flex-dir))
|
||||
layout-bounds (-> layout-bounds (add-padding shape))
|
||||
children (cond->> children reverse? reverse)
|
||||
layout-lines
|
||||
(->> (calc-layout-lines shape children layout-bounds)
|
||||
(calc-layout-lines-position shape layout-bounds)
|
||||
(map (partial calc-layout-line-data shape layout-bounds)))]
|
||||
|
||||
{:layout-lines layout-lines
|
||||
:reverse? reverse?}))
|
||||
|
||||
(defn next-p
|
||||
"Calculates the position for the current shape given the layout-data context"
|
||||
[shape
|
||||
{:keys [width height]}
|
||||
{:keys [start-x start-y layout-gap margin-x margin-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)
|
||||
|
||||
pos-x (cond-> pos-x (some? margin-x) (+ margin-x))
|
||||
pos-y (cond-> pos-y (some? margin-y) (+ margin-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)
|
||||
|
||||
next-x (cond-> next-x (some? margin-x) (+ margin-x))
|
||||
next-y (cond-> next-y (some? margin-y) (+ margin-y))
|
||||
|
||||
layout-data
|
||||
(assoc layout-data :start-x next-x :start-y next-y)]
|
||||
[corner-p layout-data]))
|
||||
|
||||
(defn calc-layout-modifiers
|
||||
"Calculates the modifiers for the layout"
|
||||
[parent transform child layout-data]
|
||||
|
||||
(let [bounds (-> child :points gre/points->selrect)
|
||||
|
||||
[corner-p layout-data] (next-p parent bounds layout-data)
|
||||
|
||||
delta-p (-> corner-p
|
||||
(gpt/subtract (gpt/point bounds))
|
||||
(cond-> (some? transform) (gpt/transform transform)))
|
||||
|
||||
modifiers {:displacement-after (gmt/translate-matrix delta-p)}]
|
||||
|
||||
[modifiers layout-data]))
|
|
@ -7,188 +7,161 @@
|
|||
(ns app.common.geom.shapes.modifiers
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.matrix :as gmt]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes.common :as gco]
|
||||
[app.common.geom.shapes.constraints :as gct]
|
||||
[app.common.geom.shapes.layout :as gcl]
|
||||
[app.common.geom.shapes.rect :as gpr]
|
||||
[app.common.geom.shapes.flex-layout :as gcl]
|
||||
[app.common.geom.shapes.pixel-precision :as gpp]
|
||||
[app.common.geom.shapes.transforms :as gtr]
|
||||
[app.common.math :as mth]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.spec :as us]
|
||||
[app.common.types.modifiers :as ctm]
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.common.uuid :as uuid]))
|
||||
|
||||
(defn set-pixel-precision
|
||||
"Adjust modifiers so they adjust to the pixel grid"
|
||||
[modifiers shape]
|
||||
;;#?(:cljs
|
||||
;; (defn modif->js
|
||||
;; [modif-tree objects]
|
||||
;; (clj->js (into {}
|
||||
;; (map (fn [[k v]]
|
||||
;; [(get-in objects [k :name]) v]))
|
||||
;; modif-tree))))
|
||||
|
||||
(if (and (some? (:resize-transform modifiers))
|
||||
(not (gmt/unit? (:resize-transform modifiers))))
|
||||
;; If we're working with a rotation we don't handle pixel precision because
|
||||
;; the transformation won't have the precision anyway
|
||||
modifiers
|
||||
(defn resolve-tree-sequence
|
||||
"Given the ids that have changed search for layout roots to recalculate"
|
||||
[ids objects]
|
||||
|
||||
(let [center (gco/center-shape shape)
|
||||
base-bounds (-> (:points shape) (gpr/points->rect))
|
||||
(us/assert!
|
||||
:expr (or (nil? ids) (set? ids))
|
||||
:hint (dm/str "tree sequence from not set: " ids))
|
||||
|
||||
raw-bounds
|
||||
(-> (gtr/transform-bounds (:points shape) center modifiers)
|
||||
(gpr/points->rect))
|
||||
(letfn [(get-tree-root ;; Finds the tree root for the current id
|
||||
[id]
|
||||
|
||||
flip-x? (neg? (get-in modifiers [:resize-vector :x]))
|
||||
flip-y? (or (neg? (get-in modifiers [:resize-vector :y]))
|
||||
(neg? (get-in modifiers [:resize-vector-2 :y])))
|
||||
(loop [current id
|
||||
result id]
|
||||
(let [shape (get objects current)
|
||||
parent (get objects (:parent-id shape))]
|
||||
(cond
|
||||
(or (not shape) (= uuid/zero current))
|
||||
result
|
||||
|
||||
path? (= :path (:type shape))
|
||||
vertical-line? (and path? (<= (:width raw-bounds) 0.01))
|
||||
horizontal-line? (and path? (<= (:height raw-bounds) 0.01))
|
||||
;; Frame found, but not layout we return the last layout found (or the id)
|
||||
(and (= :frame (:type parent))
|
||||
(not (ctl/layout? parent)))
|
||||
result
|
||||
|
||||
target-width (if vertical-line?
|
||||
(:width raw-bounds)
|
||||
(max 1 (mth/round (:width raw-bounds))))
|
||||
;; Layout found. We continue upward but we mark this layout
|
||||
(ctl/layout? parent)
|
||||
(recur (:id parent) (:id parent))
|
||||
|
||||
target-height (if horizontal-line?
|
||||
(:height raw-bounds)
|
||||
(max 1 (mth/round (:height raw-bounds))))
|
||||
;; If group or boolean or other type of group we continue with the last result
|
||||
:else
|
||||
(recur (:id parent) result)))))
|
||||
|
||||
target-p (cond-> (gpt/round (gpt/point raw-bounds))
|
||||
flip-x?
|
||||
(update :x + target-width)
|
||||
(calculate-common-roots ;; Given some roots retrieves the minimum number of tree roots
|
||||
[result id]
|
||||
(if (= id uuid/zero)
|
||||
result
|
||||
(let [root (get-tree-root id)
|
||||
|
||||
flip-y?
|
||||
(update :y + target-height))
|
||||
;; Remove the children from the current root
|
||||
result
|
||||
(into #{} (remove #(cph/is-child? objects root %)) result)
|
||||
|
||||
ratio-width (/ target-width (:width raw-bounds))
|
||||
ratio-height (/ target-height (:height raw-bounds))
|
||||
contains-parent?
|
||||
(some #(cph/is-child? objects % root) result)]
|
||||
|
||||
modifiers
|
||||
(-> modifiers
|
||||
(d/without-nils)
|
||||
(d/update-in-when
|
||||
[:resize-vector :x] #(* % ratio-width))
|
||||
(cond-> result
|
||||
(not contains-parent?)
|
||||
(conj root)))))
|
||||
|
||||
;; If the resize-vector-2 modifier arrives means the resize-vector
|
||||
;; will only resize on the x axis
|
||||
(cond-> (nil? (:resize-vector-2 modifiers))
|
||||
(d/update-in-when
|
||||
[:resize-vector :y] #(* % ratio-height)))
|
||||
(generate-tree ;; Generate a tree sequence from a given root id
|
||||
[id]
|
||||
(->> (tree-seq
|
||||
#(d/not-empty? (dm/get-in objects [% :shapes]))
|
||||
#(dm/get-in objects [% :shapes])
|
||||
id)
|
||||
(map #(get objects %))))]
|
||||
|
||||
(d/update-in-when
|
||||
[:resize-vector-2 :y] #(* % ratio-height)))
|
||||
(let [roots (->> ids (reduce calculate-common-roots #{}))]
|
||||
(concat
|
||||
(when (contains? ids uuid/zero) [(get objects uuid/zero)])
|
||||
(mapcat generate-tree roots)))))
|
||||
|
||||
origin (get modifiers :resize-origin)
|
||||
origin-2 (get modifiers :resize-origin-2)
|
||||
(defn- set-children-modifiers
|
||||
"Propagates the modifiers from a parent too its children applying constraints if necesary"
|
||||
[modif-tree objects parent transformed-parent ignore-constraints]
|
||||
(let [children (:shapes parent)
|
||||
modifiers (dm/get-in modif-tree [(:id parent) :modifiers])]
|
||||
|
||||
resize-v (get modifiers :resize-vector)
|
||||
resize-v-2 (get modifiers :resize-vector-2)
|
||||
displacement (get modifiers :displacement)
|
||||
(if (ctm/only-move? modifiers)
|
||||
;; Move modifiers don't need to calculate constraints
|
||||
(loop [modif-tree modif-tree
|
||||
children (seq children)]
|
||||
(if-let [current (first children)]
|
||||
(recur (update-in modif-tree [current :modifiers] ctm/add-modifiers modifiers)
|
||||
(rest children))
|
||||
modif-tree))
|
||||
|
||||
target-p-inv
|
||||
(-> target-p
|
||||
(gpt/transform
|
||||
(cond-> (gmt/matrix)
|
||||
(some? displacement)
|
||||
(gmt/multiply (gmt/inverse displacement))
|
||||
;; Check the constraints, then resize
|
||||
(let [parent (gtr/transform-shape parent (ctm/select-parent-modifiers modifiers))]
|
||||
(loop [modif-tree modif-tree
|
||||
children (seq children)]
|
||||
(if-let [current (first children)]
|
||||
(let [child-modifiers (gct/calc-child-modifiers parent (get objects current) modifiers ignore-constraints transformed-parent)]
|
||||
(recur (cond-> modif-tree
|
||||
(not (ctm/empty? child-modifiers))
|
||||
(update-in [current :modifiers] ctm/add-modifiers child-modifiers))
|
||||
(rest children)))
|
||||
modif-tree))))))
|
||||
|
||||
(and (some? resize-v) (some? origin))
|
||||
(gmt/scale (gpt/inverse resize-v) origin)
|
||||
|
||||
(and (some? resize-v-2) (some? origin-2))
|
||||
(gmt/scale (gpt/inverse resize-v-2) origin-2))))
|
||||
|
||||
delta-v (gpt/subtract target-p-inv (gpt/point base-bounds))
|
||||
|
||||
modifiers
|
||||
(-> modifiers
|
||||
(d/update-when :displacement #(gmt/multiply (gmt/translate-matrix delta-v) %))
|
||||
(cond-> (nil? (:displacement modifiers))
|
||||
(assoc :displacement (gmt/translate-matrix delta-v))))]
|
||||
modifiers)))
|
||||
|
||||
|
||||
(defn set-children-modifiers
|
||||
[modif-tree shape objects ignore-constraints snap-pixel?]
|
||||
(letfn [(set-child [transformed-rect snap-pixel? modif-tree child]
|
||||
(let [modifiers (get-in modif-tree [(:id shape) :modifiers])
|
||||
child-modifiers (gct/calc-child-modifiers shape child modifiers ignore-constraints transformed-rect)
|
||||
child-modifiers (cond-> child-modifiers snap-pixel? (set-pixel-precision child))]
|
||||
(defn- process-layout-children
|
||||
[modif-tree objects parent transformed-parent]
|
||||
(letfn [(process-child [modif-tree child]
|
||||
(let [modifiers (dm/get-in modif-tree [(:id parent) :modifiers])
|
||||
child-modifiers (-> modifiers
|
||||
(ctm/select-child-geometry-modifiers)
|
||||
(gcl/normalize-child-modifiers parent child transformed-parent))]
|
||||
(cond-> modif-tree
|
||||
(not (gtr/empty-modifiers? child-modifiers))
|
||||
(update-in [(:id child) :modifiers] #(merge child-modifiers %)))))]
|
||||
(let [children (map (d/getf objects) (:shapes shape))
|
||||
modifiers (get-in modif-tree [(:id shape) :modifiers])
|
||||
transformed-rect (gtr/transform-selrect (:selrect shape) modifiers)
|
||||
resize-modif? (or (:resize-vector modifiers) (:resize-vector-2 modifiers))]
|
||||
(reduce (partial set-child transformed-rect (and snap-pixel? resize-modif?)) modif-tree children))))
|
||||
(not (ctm/empty? child-modifiers))
|
||||
(update-in [(:id child) :modifiers] ctm/add-modifiers child-modifiers))))]
|
||||
(let [children (map (d/getf objects) (:shapes transformed-parent))]
|
||||
(reduce process-child modif-tree children))))
|
||||
|
||||
(defn group? [shape]
|
||||
(or (= :group (:type shape))
|
||||
(= :bool (:type shape))))
|
||||
(defn- set-layout-modifiers
|
||||
[modif-tree objects parent]
|
||||
|
||||
(defn merge-modifiers
|
||||
[modif-tree ids modifiers]
|
||||
(reduce
|
||||
(fn [modif-tree id]
|
||||
(update-in modif-tree [id :modifiers] #(merge % modifiers)))
|
||||
modif-tree
|
||||
ids))
|
||||
(letfn [(apply-modifiers [modif-tree child]
|
||||
(let [modifiers (-> (dm/get-in modif-tree [(:id child) :modifiers])
|
||||
(ctm/select-geometry))]
|
||||
(cond
|
||||
(cph/group-like-shape? child)
|
||||
(gtr/apply-group-modifiers child objects modif-tree)
|
||||
|
||||
(defn set-layout-modifiers
|
||||
[modif-tree objects id]
|
||||
(some? modifiers)
|
||||
(gtr/transform-shape child modifiers)
|
||||
|
||||
(letfn [(transform-child [parent child]
|
||||
(let [modifiers (get modif-tree (:id child))
|
||||
:else
|
||||
child)))
|
||||
|
||||
child
|
||||
(cond-> child
|
||||
(not (group? child))
|
||||
(-> (merge modifiers) gtr/transform-shape)
|
||||
|
||||
(group? child)
|
||||
(gtr/apply-group-modifiers objects modif-tree))
|
||||
|
||||
child
|
||||
(-> child
|
||||
(gtr/apply-transform (gmt/transform-in (gco/center-shape parent) (:transform-inverse parent))))]
|
||||
|
||||
child))
|
||||
|
||||
(set-layout-modifiers [parent transform [layout-data modif-tree] child]
|
||||
(let [[modifiers layout-data]
|
||||
(gcl/calc-layout-modifiers parent transform child layout-data)
|
||||
(set-child-modifiers [parent [layout-line modif-tree] child]
|
||||
(let [[modifiers layout-line]
|
||||
(gcl/layout-child-modifiers parent child layout-line)
|
||||
|
||||
modif-tree
|
||||
(cond-> modif-tree
|
||||
(not (gtr/empty-modifiers? modifiers))
|
||||
(merge-modifiers [(:id child)] modifiers)
|
||||
(d/not-empty? modifiers)
|
||||
(update-in [(:id child) :modifiers] ctm/add-modifiers modifiers))]
|
||||
|
||||
(and (not (gtr/empty-modifiers? modifiers)) (group? child))
|
||||
(merge-modifiers (:shapes child) modifiers))]
|
||||
[layout-line modif-tree]))]
|
||||
|
||||
[layout-data modif-tree]))]
|
||||
|
||||
(let [modifiers (get modif-tree id)
|
||||
|
||||
shape (-> (get objects id) (merge modifiers) gtr/transform-shape)
|
||||
|
||||
|
||||
children (->> (:shapes shape)
|
||||
(map (d/getf objects))
|
||||
(map (partial transform-child shape)))
|
||||
|
||||
center (gco/center-shape shape)
|
||||
{:keys [transform transform-inverse]} shape
|
||||
|
||||
shape
|
||||
(-> shape
|
||||
(gtr/apply-transform (gmt/transform-in center transform-inverse)))
|
||||
|
||||
transformed-rect (:selrect shape)
|
||||
|
||||
layout-data (gcl/calc-layout-data shape children transformed-rect)
|
||||
children (into [] (cond-> children (:reverse? layout-data) reverse))
|
||||
|
||||
max-idx (dec (count children))
|
||||
layout-lines (:layout-lines layout-data)]
|
||||
(let [children (map (d/getf objects) (:shapes parent))
|
||||
children (->> children (map (partial apply-modifiers modif-tree)))
|
||||
layout-data (gcl/calc-layout-data parent children)
|
||||
children (into [] (cond-> children (:reverse? layout-data) reverse))
|
||||
max-idx (dec (count children))
|
||||
layout-lines (:layout-lines layout-data)]
|
||||
|
||||
(loop [modif-tree modif-tree
|
||||
layout-line (first layout-lines)
|
||||
|
@ -199,92 +172,146 @@
|
|||
children (subvec children from-idx to-idx)
|
||||
|
||||
[_ modif-tree]
|
||||
(reduce (partial set-layout-modifiers shape transform) [layout-line modif-tree] children)]
|
||||
|
||||
(reduce (partial set-child-modifiers parent) [layout-line modif-tree] children)]
|
||||
(recur modif-tree (first pending) (rest pending) to-idx))
|
||||
|
||||
modif-tree)))))
|
||||
|
||||
(defn get-first-layout
|
||||
[id objects]
|
||||
(defn- calc-auto-modifiers
|
||||
"Calculates the modifiers to adjust the bounds for auto-width/auto-height shapes"
|
||||
[objects parent]
|
||||
(letfn [(set-parent-auto-width
|
||||
[modifiers auto-width]
|
||||
(let [origin (-> parent :points first)
|
||||
scale-width (/ auto-width (-> parent :selrect :width) )]
|
||||
(-> modifiers
|
||||
(ctm/resize-parent (gpt/point scale-width 1) origin (:transform parent) (:transform-inverse parent)))))
|
||||
|
||||
(loop [current id
|
||||
result id]
|
||||
(let [shape (get objects current)
|
||||
parent (get objects (:parent-id shape))]
|
||||
(cond
|
||||
(or (not shape) (= uuid/zero current))
|
||||
result
|
||||
(set-parent-auto-height
|
||||
[modifiers auto-height]
|
||||
(let [origin (-> parent :points first)
|
||||
scale-height (/ auto-height (-> parent :selrect :height) )]
|
||||
(-> modifiers
|
||||
(ctm/resize-parent (gpt/point 1 scale-height) origin (:transform parent) (:transform-inverse parent)))))]
|
||||
|
||||
;; Frame found, but not layout we return the last layout found (or the id)
|
||||
(and (= :frame (:type parent))
|
||||
(not (:layout parent)))
|
||||
result
|
||||
(let [children (->> parent :shapes (map (d/getf objects)))
|
||||
|
||||
;; Layout found. We continue upward but we mark this layout
|
||||
(and (= :frame (:type parent))
|
||||
(:layout parent))
|
||||
(:id parent)
|
||||
{auto-width :width auto-height :height}
|
||||
(when (and (d/not-empty? children) (or (ctl/auto-height? parent) (ctl/auto-width? parent)))
|
||||
(gcl/layout-content-bounds parent children))]
|
||||
|
||||
;; If group or boolean or other type of group we continue with the last result
|
||||
:else
|
||||
(recur (:id parent) result)))))
|
||||
(cond-> (ctm/empty)
|
||||
(and (some? auto-width) (ctl/auto-width? parent))
|
||||
(set-parent-auto-width auto-width)
|
||||
|
||||
(defn resolve-layout-ids
|
||||
"Given a list of ids, resolve the parent layouts that will need to update. This will go upwards
|
||||
in the tree while a layout is found"
|
||||
[ids objects]
|
||||
(and (some? auto-height) (ctl/auto-height? parent))
|
||||
(set-parent-auto-height auto-height)))))
|
||||
|
||||
(into (d/ordered-set)
|
||||
(map #(get-first-layout % objects))
|
||||
ids))
|
||||
(defn- propagate-modifiers
|
||||
"Propagate modifiers to its children"
|
||||
[objects ignore-constraints [modif-tree autolayouts] parent]
|
||||
(let [parent-id (:id parent)
|
||||
root? (= uuid/zero parent-id)
|
||||
modifiers (-> (dm/get-in modif-tree [parent-id :modifiers])
|
||||
(ctm/select-geometry))
|
||||
transformed-parent (gtr/transform-shape parent modifiers)
|
||||
|
||||
has-modifiers? (ctm/child-modifiers? modifiers)
|
||||
layout? (ctl/layout? parent)
|
||||
auto? (or (ctl/auto-height? transformed-parent) (ctl/auto-width? transformed-parent))
|
||||
parent? (or (cph/group-like-shape? parent) (cph/frame-shape? parent))
|
||||
|
||||
;; If the current child is inside the layout we ignore the constraints
|
||||
inside-layout? (ctl/inside-layout? objects parent)]
|
||||
|
||||
[(cond-> modif-tree
|
||||
(and (not layout?) has-modifiers? parent? (not root?))
|
||||
(set-children-modifiers objects parent transformed-parent (or ignore-constraints inside-layout?))
|
||||
|
||||
layout?
|
||||
(-> (process-layout-children objects parent transformed-parent)
|
||||
(set-layout-modifiers objects transformed-parent)))
|
||||
|
||||
;; Auto-width/height can change the positions in the parent so we need to recalculate
|
||||
(cond-> autolayouts auto? (conj (:id parent)))]))
|
||||
|
||||
(defn- apply-structure-modifiers
|
||||
[objects modif-tree]
|
||||
(letfn [(apply-shape [objects [id {:keys [modifiers]}]]
|
||||
(cond-> objects
|
||||
(ctm/has-structure? modifiers)
|
||||
(update id ctm/apply-structure-modifiers modifiers)))]
|
||||
(reduce apply-shape objects modif-tree)))
|
||||
|
||||
(defn- apply-partial-objects-modifiers
|
||||
[objects tree-seq modif-tree]
|
||||
|
||||
(letfn [(apply-shape [objects {:keys [id] :as shape}]
|
||||
(if (cph/group-shape? shape)
|
||||
(let [children (cph/get-children objects id)]
|
||||
(assoc objects id
|
||||
(cond
|
||||
(cph/mask-shape? shape)
|
||||
(gtr/update-mask-selrect shape children)
|
||||
|
||||
:else
|
||||
(gtr/update-group-selrect shape children))))
|
||||
|
||||
(let [modifiers (get-in modif-tree [id :modifiers])
|
||||
object (cond-> shape
|
||||
(some? modifiers)
|
||||
(gtr/transform-shape modifiers))]
|
||||
(assoc objects id object))))]
|
||||
|
||||
(reduce apply-shape objects (reverse tree-seq))))
|
||||
|
||||
(defn merge-modif-tree
|
||||
[modif-tree other-tree]
|
||||
(reduce (fn [modif-tree [id {:keys [modifiers]}]]
|
||||
(update-in modif-tree [id :modifiers] ctm/add-modifiers modifiers))
|
||||
modif-tree
|
||||
other-tree))
|
||||
|
||||
(defn sizing-auto-modifiers
|
||||
"Recalculates the layouts to adjust the sizing: auto new sizes"
|
||||
[modif-tree sizing-auto-layouts objects ignore-constraints]
|
||||
(loop [modif-tree modif-tree
|
||||
sizing-auto-layouts (reverse sizing-auto-layouts)]
|
||||
(if-let [current (first sizing-auto-layouts)]
|
||||
(let [parent-base (get objects current)
|
||||
tree-seq (resolve-tree-sequence #{current} objects)
|
||||
|
||||
;; Apply the current stack of transformations so we can calculate the auto-layouts
|
||||
objects (apply-partial-objects-modifiers objects tree-seq modif-tree)
|
||||
|
||||
resize-modif-tree
|
||||
{current {:modifiers (calc-auto-modifiers objects parent-base)}}
|
||||
|
||||
tree-seq (resolve-tree-sequence #{current} objects)
|
||||
|
||||
[resize-modif-tree _]
|
||||
(reduce (partial propagate-modifiers objects ignore-constraints) [resize-modif-tree #{}] tree-seq)
|
||||
|
||||
modif-tree (merge-modif-tree modif-tree resize-modif-tree)]
|
||||
(recur modif-tree (rest sizing-auto-layouts)))
|
||||
modif-tree)))
|
||||
|
||||
(defn set-objects-modifiers
|
||||
[ids objects get-modifier ignore-constraints snap-pixel?]
|
||||
[modif-tree objects ignore-constraints snap-pixel?]
|
||||
|
||||
(let [set-modifiers
|
||||
(fn [modif-tree id]
|
||||
(let [shape (get objects id)
|
||||
modifiers (cond-> (get-modifier shape) snap-pixel? (set-pixel-precision shape))]
|
||||
(-> modif-tree
|
||||
(assoc id {:modifiers modifiers}))))
|
||||
(let [objects (apply-structure-modifiers objects modif-tree)
|
||||
shapes-tree (resolve-tree-sequence (-> modif-tree keys set) objects)
|
||||
|
||||
modif-tree (reduce set-modifiers {} ids)
|
||||
[modif-tree sizing-auto-layouts]
|
||||
(reduce (partial propagate-modifiers objects ignore-constraints) [modif-tree #{}] shapes-tree)
|
||||
|
||||
ids (resolve-layout-ids ids objects)
|
||||
;; Calculate hug layouts positions
|
||||
modif-tree (sizing-auto-modifiers modif-tree sizing-auto-layouts objects ignore-constraints)
|
||||
|
||||
;; First: Calculate children modifiers (constraints, etc)
|
||||
[modif-tree touched-layouts]
|
||||
(loop [current (first ids)
|
||||
pending (rest ids)
|
||||
modif-tree modif-tree
|
||||
touched-layouts (d/ordered-set)]
|
||||
(if (some? current)
|
||||
(let [shape (get objects current)
|
||||
pending (concat pending (:shapes shape))
|
||||
|
||||
touched-layouts
|
||||
(cond-> touched-layouts
|
||||
(:layout shape)
|
||||
(conj (:id shape)))
|
||||
|
||||
modif-tree
|
||||
(-> modif-tree
|
||||
(set-children-modifiers shape objects ignore-constraints snap-pixel?))]
|
||||
|
||||
(recur (first pending) (rest pending) modif-tree touched-layouts))
|
||||
|
||||
[modif-tree touched-layouts]))
|
||||
|
||||
;; Second: Calculate layout positioning
|
||||
modif-tree
|
||||
(loop [current (first touched-layouts)
|
||||
pending (rest touched-layouts)
|
||||
modif-tree modif-tree]
|
||||
|
||||
(if (some? current)
|
||||
(let [modif-tree (set-layout-modifiers modif-tree objects current)]
|
||||
(recur (first pending) (rest pending) modif-tree))
|
||||
modif-tree))]
|
||||
(cond-> modif-tree
|
||||
snap-pixel? (gpp/adjust-pixel-precision objects))]
|
||||
|
||||
;;#?(:cljs
|
||||
;; (.log js/console ">result" (modif->js modif-tree objects)))
|
||||
modif-tree))
|
||||
|
|
71
common/src/app/common/geom/shapes/pixel_precision.cljc
Normal file
71
common/src/app/common/geom/shapes/pixel_precision.cljc
Normal file
|
@ -0,0 +1,71 @@
|
|||
;; 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.pixel-precision
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes.points :as gpo]
|
||||
[app.common.geom.shapes.rect :as gpr]
|
||||
[app.common.geom.shapes.transforms :as gtr]
|
||||
[app.common.math :as mth]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.types.modifiers :as ctm]))
|
||||
|
||||
(defn size-pixel-precision
|
||||
[modifiers {:keys [points transform transform-inverse] :as shape}]
|
||||
(let [origin (gpo/origin points)
|
||||
curr-width (gpo/width-points points)
|
||||
curr-height (gpo/height-points points)
|
||||
|
||||
path? (cph/path-shape? shape)
|
||||
vertical-line? (and path? (<= curr-width 0.01))
|
||||
horizontal-line? (and path? (<= curr-height 0.01))
|
||||
|
||||
target-width (if vertical-line? curr-width (max 1 (mth/round curr-width)))
|
||||
target-height (if horizontal-line? curr-height (max 1 (mth/round curr-height)))
|
||||
|
||||
ratio-width (/ target-width curr-width)
|
||||
ratio-height (/ target-height curr-height)
|
||||
scalev (gpt/point ratio-width ratio-height)]
|
||||
(-> modifiers
|
||||
(ctm/resize scalev origin transform transform-inverse))))
|
||||
|
||||
(defn position-pixel-precision
|
||||
[modifiers {:keys [points]}]
|
||||
(let [bounds (gpr/points->rect points)
|
||||
corner (gpt/point bounds)
|
||||
target-corner (gpt/round corner)
|
||||
deltav (gpt/to-vec corner target-corner)]
|
||||
(-> modifiers
|
||||
(ctm/move deltav))))
|
||||
|
||||
(defn set-pixel-precision
|
||||
"Adjust modifiers so they adjust to the pixel grid"
|
||||
[modifiers shape]
|
||||
(let [move? (ctm/only-move? modifiers)]
|
||||
(cond-> modifiers
|
||||
(not move?)
|
||||
(size-pixel-precision shape)
|
||||
|
||||
:always
|
||||
(position-pixel-precision shape))))
|
||||
|
||||
(defn adjust-pixel-precision
|
||||
[modif-tree objects]
|
||||
(let [update-modifiers
|
||||
(fn [modif-tree shape]
|
||||
(let [modifiers (dm/get-in modif-tree [(:id shape) :modifiers])]
|
||||
(if-not (ctm/has-geometry? modifiers)
|
||||
modif-tree
|
||||
(let [shape (gtr/transform-shape shape modifiers)]
|
||||
(-> modif-tree
|
||||
(update-in [(:id shape) :modifiers] set-pixel-precision shape))))))]
|
||||
|
||||
(->> (keys modif-tree)
|
||||
(map (d/getf objects))
|
||||
(reduce update-modifiers modif-tree))))
|
62
common/src/app/common/geom/shapes/points.cljc
Normal file
62
common/src/app/common/geom/shapes/points.cljc
Normal file
|
@ -0,0 +1,62 @@
|
|||
;; 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.points
|
||||
(:require
|
||||
[app.common.geom.point :as gpt]))
|
||||
|
||||
(defn origin
|
||||
[points]
|
||||
(nth points 0))
|
||||
|
||||
(defn start-hv
|
||||
"Horizontal vector from the origin with a magnitude `val`"
|
||||
[[p0 p1 _ _] val]
|
||||
(-> (gpt/to-vec p0 p1)
|
||||
(gpt/unit)
|
||||
(gpt/scale val)))
|
||||
|
||||
(defn end-hv
|
||||
"Horizontal vector from the oposite to the origin in the x axis with a magnitude `val`"
|
||||
[[p0 p1 _ _] val]
|
||||
(-> (gpt/to-vec p1 p0)
|
||||
(gpt/unit)
|
||||
(gpt/scale val)))
|
||||
|
||||
(defn start-vv
|
||||
"Vertical vector from the oposite to the origin in the x axis with a magnitude `val`"
|
||||
[[p0 _ _ p3] val]
|
||||
(-> (gpt/to-vec p0 p3)
|
||||
(gpt/unit)
|
||||
(gpt/scale val)))
|
||||
|
||||
(defn end-vv
|
||||
"Vertical vector from the oposite to the origin in the x axis with a magnitude `val`"
|
||||
[[p0 _ _ p3] val]
|
||||
(-> (gpt/to-vec p3 p0)
|
||||
(gpt/unit)
|
||||
(gpt/scale val)))
|
||||
|
||||
(defn width-points
|
||||
[[p0 p1 _ _]]
|
||||
(gpt/length (gpt/to-vec p0 p1)))
|
||||
|
||||
(defn height-points
|
||||
[[p0 _ _ p3]]
|
||||
(gpt/length (gpt/to-vec p0 p3)))
|
||||
|
||||
(defn pad-points
|
||||
[[p0 p1 p2 p3 :as points] pad-top pad-right pad-bottom pad-left]
|
||||
(when (some? points)
|
||||
(let [top-v (start-vv points pad-top)
|
||||
right-v (end-hv points pad-right)
|
||||
bottom-v (end-vv points pad-bottom)
|
||||
left-v (start-hv points pad-left)]
|
||||
|
||||
[(-> p0 (gpt/add left-v) (gpt/add top-v))
|
||||
(-> p1 (gpt/add right-v) (gpt/add top-v))
|
||||
(-> p2 (gpt/add right-v) (gpt/add bottom-v))
|
||||
(-> p3 (gpt/add left-v) (gpt/add bottom-v))])))
|
|
@ -11,14 +11,25 @@
|
|||
[app.common.math :as mth]))
|
||||
|
||||
(defn make-rect
|
||||
[x y width height]
|
||||
(when (d/num? x y width height)
|
||||
(let [width (max width 0.01)
|
||||
height (max height 0.01)]
|
||||
{:x x
|
||||
:y y
|
||||
:width width
|
||||
:height height})))
|
||||
([p1 p2]
|
||||
(let [xp1 (:x p1)
|
||||
yp1 (:y p1)
|
||||
xp2 (:x p2)
|
||||
yp2 (:y p2)
|
||||
x1 (min xp1 xp2)
|
||||
y1 (min yp1 yp2)
|
||||
x2 (max xp1 xp2)
|
||||
y2 (max yp1 yp2)]
|
||||
(make-rect x1 y1 (- x2 x1) (- y2 y1))))
|
||||
|
||||
([x y width height]
|
||||
(when (d/num? x y width height)
|
||||
(let [width (max width 0.01)
|
||||
height (max height 0.01)]
|
||||
{:x x
|
||||
:y y
|
||||
:width width
|
||||
:height height}))))
|
||||
|
||||
(defn make-selrect
|
||||
[x y width height]
|
||||
|
@ -80,6 +91,19 @@
|
|||
(when (d/num? minx miny maxx maxy)
|
||||
(make-rect minx miny (- maxx minx) (- maxy miny))))))
|
||||
|
||||
(defn squared-points
|
||||
[points]
|
||||
(when (d/not-empty? points)
|
||||
(let [minx (transduce (keep :x) min ##Inf points)
|
||||
miny (transduce (keep :y) min ##Inf points)
|
||||
maxx (transduce (keep :x) max ##-Inf points)
|
||||
maxy (transduce (keep :y) max ##-Inf points)]
|
||||
(when (d/num? minx miny maxx maxy)
|
||||
[(gpt/point minx miny)
|
||||
(gpt/point maxx miny)
|
||||
(gpt/point maxx maxy)
|
||||
(gpt/point minx maxy)]))))
|
||||
|
||||
(defn points->selrect [points]
|
||||
(when-let [rect (points->rect points)]
|
||||
(let [{:keys [x y width height]} rect]
|
||||
|
@ -168,3 +192,10 @@
|
|||
(>= (:y1 sr2) (:y1 sr1))
|
||||
(<= (:y2 sr2) (:y2 sr1))))
|
||||
|
||||
(defn corners->selrect
|
||||
[p1 p2]
|
||||
(let [xp1 (:x p1)
|
||||
xp2 (:x p2)
|
||||
yp1 (:y p1)
|
||||
yp2 (:y p2)]
|
||||
(make-selrect (min xp1 xp2) (min yp1 yp2) (abs (- xp1 xp2)) (abs (- yp1 yp2)))))
|
||||
|
|
|
@ -10,12 +10,14 @@
|
|||
[app.common.data.macros :as dm]
|
||||
[app.common.geom.matrix :as gmt]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes.bool :as gshb]
|
||||
[app.common.geom.shapes.common :as gco]
|
||||
[app.common.geom.shapes.path :as gpa]
|
||||
[app.common.geom.shapes.rect :as gpr]
|
||||
[app.common.math :as mth]
|
||||
[app.common.spec :as us]
|
||||
[app.common.text :as txt]))
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.types.modifiers :as ctm]
|
||||
[app.common.uuid :as uuid]))
|
||||
|
||||
(def ^:dynamic *skip-adjust* false)
|
||||
|
||||
|
@ -76,14 +78,6 @@
|
|||
|
||||
; ---- Geometric operations
|
||||
|
||||
(defn- normalize-scale
|
||||
"We normalize the scale so it's not too close to 0"
|
||||
[scale]
|
||||
(cond
|
||||
(and (< scale 0) (> scale -0.01)) -0.01
|
||||
(and (>= scale 0) (< scale 0.01)) 0.01
|
||||
:else scale))
|
||||
|
||||
(defn- calculate-skew-angle
|
||||
"Calculates the skew angle of the parallelogram given by the points"
|
||||
[[p1 _ p3 p4]]
|
||||
|
@ -99,6 +93,7 @@
|
|||
(defn- calculate-height
|
||||
"Calculates the height of a parallelogram given by the points"
|
||||
[[p1 _ _ p4]]
|
||||
|
||||
(-> (gpt/to-vec p4 p1)
|
||||
(gpt/length)))
|
||||
|
||||
|
@ -182,17 +177,6 @@
|
|||
(gmt/multiply (:transform-inverse shape (gmt/matrix)))
|
||||
(gmt/translate (gpt/negate center)))))
|
||||
|
||||
(defn transform-point-center
|
||||
"Transform a point around the shape center"
|
||||
[point center matrix]
|
||||
(if (and (some? point) (some? matrix) (some? center))
|
||||
(gpt/transform
|
||||
point
|
||||
(gmt/multiply (gmt/translate-matrix center)
|
||||
matrix
|
||||
(gmt/translate-matrix (gpt/negate center))))
|
||||
point))
|
||||
|
||||
(defn transform-rect
|
||||
"Transform a rectangles and changes its attributes"
|
||||
[rect matrix]
|
||||
|
@ -205,55 +189,52 @@
|
|||
"Calculates a matrix that is a series of transformations we have to do to the transformed rectangle so that
|
||||
after applying them the end result is the `shape-path-temp`.
|
||||
This is compose of three transformations: skew, resize and rotation"
|
||||
([points-temp points-rec]
|
||||
(calculate-adjust-matrix points-temp points-rec false false))
|
||||
[points-temp points-rec flip-x flip-y]
|
||||
(let [center (gco/center-bounds points-temp)
|
||||
|
||||
([points-temp points-rec flip-x flip-y]
|
||||
(let [center (gco/center-points points-temp)
|
||||
stretch-matrix (gmt/matrix)
|
||||
|
||||
stretch-matrix (gmt/matrix)
|
||||
skew-angle (calculate-skew-angle points-temp)
|
||||
|
||||
skew-angle (calculate-skew-angle points-temp)
|
||||
;; When one of the axis is flipped we have to reverse the skew
|
||||
;; skew-angle (if (neg? (* (:x resize-vector) (:y resize-vector))) (- skew-angle) skew-angle )
|
||||
skew-angle (if (and (or flip-x flip-y)
|
||||
(not (and flip-x flip-y))) (- skew-angle) skew-angle )
|
||||
skew-angle (if (mth/nan? skew-angle) 0 skew-angle)
|
||||
|
||||
;; When one of the axis is flipped we have to reverse the skew
|
||||
;; skew-angle (if (neg? (* (:x resize-vector) (:y resize-vector))) (- skew-angle) skew-angle )
|
||||
skew-angle (if (and (or flip-x flip-y)
|
||||
(not (and flip-x flip-y))) (- skew-angle) skew-angle )
|
||||
skew-angle (if (mth/nan? skew-angle) 0 skew-angle)
|
||||
stretch-matrix (gmt/multiply stretch-matrix (gmt/skew-matrix skew-angle 0))
|
||||
|
||||
stretch-matrix (gmt/multiply stretch-matrix (gmt/skew-matrix skew-angle 0))
|
||||
h1 (max 1 (calculate-height points-temp))
|
||||
h2 (max 1 (calculate-height (gco/transform-points points-rec center stretch-matrix)))
|
||||
h3 (if-not (mth/almost-zero? h2) (/ h1 h2) 1)
|
||||
h3 (if (mth/nan? h3) 1 h3)
|
||||
|
||||
h1 (max 1 (calculate-height points-temp))
|
||||
h2 (max 1 (calculate-height (gco/transform-points points-rec center stretch-matrix)))
|
||||
h3 (if-not (mth/almost-zero? h2) (/ h1 h2) 1)
|
||||
h3 (if (mth/nan? h3) 1 h3)
|
||||
w1 (max 1 (calculate-width points-temp))
|
||||
w2 (max 1 (calculate-width (gco/transform-points points-rec center stretch-matrix)))
|
||||
w3 (if-not (mth/almost-zero? w2) (/ w1 w2) 1)
|
||||
w3 (if (mth/nan? w3) 1 w3)
|
||||
|
||||
w1 (max 1 (calculate-width points-temp))
|
||||
w2 (max 1 (calculate-width (gco/transform-points points-rec center stretch-matrix)))
|
||||
w3 (if-not (mth/almost-zero? w2) (/ w1 w2) 1)
|
||||
w3 (if (mth/nan? w3) 1 w3)
|
||||
stretch-matrix (gmt/multiply stretch-matrix (gmt/scale-matrix (gpt/point w3 h3)))
|
||||
|
||||
stretch-matrix (gmt/multiply stretch-matrix (gmt/scale-matrix (gpt/point w3 h3)))
|
||||
rotation-angle (calculate-rotation
|
||||
center
|
||||
(gco/transform-points points-rec (gco/center-points points-rec) stretch-matrix)
|
||||
points-temp
|
||||
flip-x
|
||||
flip-y)
|
||||
|
||||
rotation-angle (calculate-rotation
|
||||
center
|
||||
(gco/transform-points points-rec (gco/center-points points-rec) stretch-matrix)
|
||||
points-temp
|
||||
flip-x
|
||||
flip-y)
|
||||
stretch-matrix (gmt/multiply (gmt/rotate-matrix rotation-angle) stretch-matrix)
|
||||
|
||||
stretch-matrix (gmt/multiply (gmt/rotate-matrix rotation-angle) stretch-matrix)
|
||||
|
||||
;; This is the inverse to be able to remove the transformation
|
||||
stretch-matrix-inverse
|
||||
(gmt/multiply (gmt/scale-matrix (gpt/point (/ 1 w3) (/ 1 h3)))
|
||||
(gmt/skew-matrix (- skew-angle) 0)
|
||||
(gmt/rotate-matrix (- rotation-angle)))]
|
||||
[stretch-matrix stretch-matrix-inverse rotation-angle])))
|
||||
;; This is the inverse to be able to remove the transformation
|
||||
stretch-matrix-inverse
|
||||
(gmt/multiply (gmt/scale-matrix (gpt/point (/ 1 w3) (/ 1 h3)))
|
||||
(gmt/skew-matrix (- skew-angle) 0)
|
||||
(gmt/rotate-matrix (- rotation-angle)))]
|
||||
[stretch-matrix stretch-matrix-inverse rotation-angle]))
|
||||
|
||||
(defn- adjust-rotated-transform
|
||||
[{:keys [transform transform-inverse flip-x flip-y]} points]
|
||||
(let [center (gco/center-points points)
|
||||
(let [center (gco/center-bounds points)
|
||||
|
||||
points-temp (cond-> points
|
||||
(some? transform-inverse)
|
||||
|
@ -273,13 +254,59 @@
|
|||
(if transform (gmt/multiply transform matrix) matrix)
|
||||
(if transform-inverse (gmt/multiply matrix-inverse transform-inverse) matrix-inverse)]))
|
||||
|
||||
(defn apply-transform
|
||||
(defn- adjust-shape-flips
|
||||
"After some tranformations the flip-x/flip-y flags can change we need
|
||||
to check this before adjusting the selrect"
|
||||
[shape points]
|
||||
|
||||
(let [points' (:points shape)
|
||||
|
||||
xv1 (gpt/to-vec (nth points' 0) (nth points' 1))
|
||||
xv2 (gpt/to-vec (nth points 0) (nth points 1))
|
||||
dot-x (gpt/dot xv1 xv2)
|
||||
|
||||
yv1 (gpt/to-vec (nth points' 0) (nth points' 3))
|
||||
yv2 (gpt/to-vec (nth points 0) (nth points 3))
|
||||
dot-y (gpt/dot yv1 yv2)]
|
||||
|
||||
(cond-> shape
|
||||
(neg? dot-x)
|
||||
(-> (update :flip-x not)
|
||||
(update :rotation -))
|
||||
|
||||
(neg? dot-y)
|
||||
(-> (update :flip-y not)
|
||||
(update :rotation -)))))
|
||||
|
||||
(defn- apply-transform-move
|
||||
"Given a new set of points transformed, set up the rectangle so it keeps
|
||||
its properties. We adjust de x,y,width,height and create a custom transform"
|
||||
[shape transform-mtx]
|
||||
(let [bool? (= (:type shape) :bool)
|
||||
path? (= (:type shape) :path)
|
||||
points (gco/transform-points (:points shape) transform-mtx)
|
||||
selrect (gco/transform-selrect (:selrect shape) transform-mtx)]
|
||||
(-> shape
|
||||
(cond-> bool?
|
||||
(update :bool-content gpa/transform-content transform-mtx))
|
||||
(cond-> path?
|
||||
(update :content gpa/transform-content transform-mtx))
|
||||
(cond-> (not path?)
|
||||
(assoc :x (:x selrect)
|
||||
:y (:y selrect)
|
||||
:width (:width selrect)
|
||||
:height (:height selrect)))
|
||||
(assoc :selrect selrect)
|
||||
(assoc :points points))))
|
||||
|
||||
(defn- apply-transform-generic
|
||||
"Given a new set of points transformed, set up the rectangle so it keeps
|
||||
its properties. We adjust de x,y,width,height and create a custom transform"
|
||||
[shape transform-mtx]
|
||||
|
||||
(let [points' (:points shape)
|
||||
points (gco/transform-points points' transform-mtx)
|
||||
shape (-> shape (adjust-shape-flips points))
|
||||
bool? (= (:type shape) :bool)
|
||||
path? (= (:type shape) :path)
|
||||
|
||||
|
@ -289,14 +316,16 @@
|
|||
base-rotation (or (:rotation shape) 0)
|
||||
modif-rotation (or (get-in shape [:modifiers :rotation]) 0)
|
||||
rotation (mod (+ base-rotation modif-rotation) 360)]
|
||||
|
||||
(-> shape
|
||||
(cond-> bool?
|
||||
(update :bool-content gpa/transform-content transform-mtx))
|
||||
(cond-> path?
|
||||
(update :content gpa/transform-content transform-mtx))
|
||||
(cond-> (not path?)
|
||||
(-> (merge (select-keys selrect [:x :y :width :height]))))
|
||||
(assoc :x (:x selrect)
|
||||
:y (:y selrect)
|
||||
:width (:width selrect)
|
||||
:height (:height selrect)))
|
||||
(cond-> transform
|
||||
(-> (assoc :transform transform)
|
||||
(assoc :transform-inverse transform-inverse)))
|
||||
|
@ -304,10 +333,19 @@
|
|||
(dissoc :transform :transform-inverse))
|
||||
(cond-> (some? selrect)
|
||||
(assoc :selrect selrect))
|
||||
|
||||
(cond-> (d/not-empty? points)
|
||||
(assoc :points points))
|
||||
(assoc :rotation rotation))))
|
||||
|
||||
(defn- apply-transform
|
||||
"Given a new set of points transformed, set up the rectangle so it keeps
|
||||
its properties. We adjust de x,y,width,height and create a custom transform"
|
||||
[shape transform-mtx]
|
||||
(if (gmt/move? transform-mtx)
|
||||
(apply-transform-move shape transform-mtx)
|
||||
(apply-transform-generic shape transform-mtx)))
|
||||
|
||||
(defn- update-group-viewbox
|
||||
"Updates the viewbox for groups imported from SVG's"
|
||||
[{:keys [selrect svg-viewbox] :as group} new-selrect]
|
||||
|
@ -332,6 +370,9 @@
|
|||
;; Points for every shape inside the group
|
||||
points (->> children (mapcat :points))
|
||||
|
||||
;; Fixed problem with empty groups. Should not happen (but it does)
|
||||
points (if (empty? points) (:points group) points)
|
||||
|
||||
;; Invert to get the points minus the transforms applied to the group
|
||||
base-points (gco/transform-points points shape-center (:transform-inverse group (gmt/matrix)))
|
||||
|
||||
|
@ -368,284 +409,74 @@
|
|||
(assoc :flip-x (-> mask :flip-x))
|
||||
(assoc :flip-y (-> mask :flip-y)))))
|
||||
|
||||
;; --- Modifiers
|
||||
(defn update-bool-selrect
|
||||
"Calculates the selrect+points for the boolean shape"
|
||||
[shape children objects]
|
||||
|
||||
;; The `modifiers` structure contains a list of transformations to
|
||||
;; do make to a shape, in this order:
|
||||
;;
|
||||
;; - resize-origin (gpt/point) + resize-vector (gpt/point)
|
||||
;; apply a scale vector to all points of the shapes, starting
|
||||
;; from the origin point.
|
||||
;;
|
||||
;; - resize-origin-2 + resize-vector-2
|
||||
;; same as the previous one, for cases in that we need to make
|
||||
;; two vectors from different origin points.
|
||||
;;
|
||||
;; - displacement (gmt/matrix)
|
||||
;; apply a translation matrix to the shape
|
||||
;;
|
||||
;; - rotation (gmt/matrix)
|
||||
;; apply a rotation matrix to the shape
|
||||
;;
|
||||
;; - resize-transform (gmt/matrix) + resize-transform-inverse (gmt/matrix)
|
||||
;; a copy of the rotation matrix currently applied to the shape;
|
||||
;; this is needed temporarily to apply the resize vectors.
|
||||
;;
|
||||
;; - resize-scale-text (bool)
|
||||
;; tells if the resize vectors must be applied to text shapes
|
||||
;; or not.
|
||||
(let [bool-content (gshb/calc-bool-content shape objects)
|
||||
shape (assoc shape :bool-content bool-content)
|
||||
[points selrect] (gpa/content->points+selrect shape bool-content)]
|
||||
|
||||
(defn empty-modifiers? [modifiers]
|
||||
(empty? (dissoc modifiers :ignore-geometry?)))
|
||||
|
||||
(defn resize-modifiers
|
||||
[shape attr value]
|
||||
(us/assert map? shape)
|
||||
(us/assert #{:width :height} attr)
|
||||
(us/assert number? value)
|
||||
(let [{:keys [proportion proportion-lock]} shape
|
||||
size (select-keys (:selrect shape) [:width :height])
|
||||
new-size (if-not proportion-lock
|
||||
(assoc size attr value)
|
||||
(if (= attr :width)
|
||||
(-> size
|
||||
(assoc :width value)
|
||||
(assoc :height (/ value proportion)))
|
||||
(-> size
|
||||
(assoc :height value)
|
||||
(assoc :width (* value proportion)))))
|
||||
width (:width new-size)
|
||||
height (:height new-size)
|
||||
|
||||
shape-transform (:transform shape)
|
||||
shape-transform-inv (:transform-inverse shape)
|
||||
shape-center (gco/center-shape shape)
|
||||
{sr-width :width sr-height :height} (:selrect shape)
|
||||
|
||||
origin (cond-> (gpt/point (:selrect shape))
|
||||
(some? shape-transform)
|
||||
(transform-point-center shape-center shape-transform))
|
||||
|
||||
scalev (gpt/divide (gpt/point width height)
|
||||
(gpt/point sr-width sr-height))]
|
||||
{:resize-vector scalev
|
||||
:resize-origin origin
|
||||
:resize-transform shape-transform
|
||||
:resize-transform-inverse shape-transform-inv}))
|
||||
|
||||
(defn change-orientation-modifiers
|
||||
[shape orientation]
|
||||
(us/assert map? shape)
|
||||
(us/verify #{:horiz :vert} orientation)
|
||||
(let [width (:width shape)
|
||||
height (:height shape)
|
||||
new-width (if (= orientation :horiz) (max width height) (min width height))
|
||||
new-height (if (= orientation :horiz) (min width height) (max width height))
|
||||
|
||||
shape-transform (:transform shape)
|
||||
shape-transform-inv (:transform-inverse shape)
|
||||
shape-center (gco/center-shape shape)
|
||||
{sr-width :width sr-height :height} (:selrect shape)
|
||||
|
||||
origin (cond-> (gpt/point (:selrect shape))
|
||||
(some? shape-transform)
|
||||
(transform-point-center shape-center shape-transform))
|
||||
|
||||
scalev (gpt/divide (gpt/point new-width new-height)
|
||||
(gpt/point sr-width sr-height))]
|
||||
{:resize-vector scalev
|
||||
:resize-origin origin
|
||||
:resize-transform shape-transform
|
||||
:resize-transform-inverse shape-transform-inv}))
|
||||
|
||||
(defn rotation-modifiers
|
||||
[shape center angle]
|
||||
(let [displacement (let [shape-center (gco/center-shape shape)]
|
||||
(-> (gmt/matrix)
|
||||
(gmt/rotate angle center)
|
||||
(gmt/rotate (- angle) shape-center)))]
|
||||
{:rotation angle
|
||||
:displacement displacement}))
|
||||
|
||||
(defn merge-modifiers
|
||||
[objects modifiers]
|
||||
|
||||
(let [set-modifier
|
||||
(fn [objects [id modifiers]]
|
||||
(-> objects
|
||||
(d/update-when id merge modifiers)))]
|
||||
(->> modifiers
|
||||
(reduce set-modifier objects))))
|
||||
|
||||
(defn modifiers->transform
|
||||
([modifiers]
|
||||
(modifiers->transform nil modifiers))
|
||||
|
||||
([center modifiers]
|
||||
(let [displacement (:displacement modifiers)
|
||||
displacement-after (:displacement-after modifiers)
|
||||
resize-v1 (:resize-vector modifiers)
|
||||
resize-v2 (:resize-vector-2 modifiers)
|
||||
origin-1 (:resize-origin modifiers (gpt/point))
|
||||
origin-2 (:resize-origin-2 modifiers (gpt/point))
|
||||
|
||||
;; Normalize x/y vector coordinates because scale by 0 is infinite
|
||||
resize-1 (when (some? resize-v1)
|
||||
(gpt/point (normalize-scale (:x resize-v1))
|
||||
(normalize-scale (:y resize-v1))))
|
||||
|
||||
resize-2 (when (some? resize-v2)
|
||||
(gpt/point (normalize-scale (:x resize-v2))
|
||||
(normalize-scale (:y resize-v2))))
|
||||
|
||||
|
||||
resize-transform (:resize-transform modifiers)
|
||||
resize-transform-inverse (:resize-transform-inverse modifiers)
|
||||
|
||||
rt-modif (:rotation modifiers)]
|
||||
|
||||
(cond-> (gmt/matrix)
|
||||
(some? displacement-after)
|
||||
(gmt/multiply displacement-after)
|
||||
|
||||
(some? resize-1)
|
||||
(-> (gmt/translate origin-1)
|
||||
(cond-> (some? resize-transform)
|
||||
(gmt/multiply resize-transform))
|
||||
(gmt/scale resize-1)
|
||||
(cond-> (some? resize-transform-inverse)
|
||||
(gmt/multiply resize-transform-inverse))
|
||||
(gmt/translate (gpt/negate origin-1)))
|
||||
|
||||
(some? resize-2)
|
||||
(-> (gmt/translate origin-2)
|
||||
(cond-> (some? resize-transform)
|
||||
(gmt/multiply resize-transform))
|
||||
(gmt/scale resize-2)
|
||||
(cond-> (some? resize-transform-inverse)
|
||||
(gmt/multiply resize-transform-inverse))
|
||||
(gmt/translate (gpt/negate origin-2)))
|
||||
|
||||
(some? displacement)
|
||||
(gmt/multiply displacement)
|
||||
|
||||
(some? rt-modif)
|
||||
(-> (gmt/translate center)
|
||||
(gmt/multiply (gmt/rotate-matrix rt-modif))
|
||||
(gmt/translate (gpt/negate center)))))))
|
||||
|
||||
(defn- set-flip [shape modifiers]
|
||||
(let [rv1x (or (get-in modifiers [:resize-vector :x]) 1)
|
||||
rv1y (or (get-in modifiers [:resize-vector :y]) 1)
|
||||
rv2x (or (get-in modifiers [:resize-vector-2 :x]) 1)
|
||||
rv2y (or (get-in modifiers [:resize-vector-2 :y]) 1)]
|
||||
(cond-> shape
|
||||
(or (neg? rv1x) (neg? rv2x))
|
||||
(-> (update :flip-x not)
|
||||
(update :rotation -))
|
||||
(or (neg? rv1y) (neg? rv2y))
|
||||
(-> (update :flip-y not)
|
||||
(update :rotation -)))))
|
||||
|
||||
(defn- apply-displacement [shape]
|
||||
(let [modifiers (:modifiers shape)]
|
||||
(if (contains? modifiers :displacement)
|
||||
(let [mov-vec (-> (gpt/point 0 0)
|
||||
(gpt/transform (:displacement modifiers)))
|
||||
shape (move shape mov-vec)
|
||||
modifiers (dissoc modifiers :displacement)]
|
||||
(-> shape
|
||||
(assoc :modifiers modifiers)
|
||||
(cond-> (empty-modifiers? modifiers)
|
||||
(dissoc :modifiers))))
|
||||
shape)))
|
||||
|
||||
(defn- apply-text-resize
|
||||
[shape modifiers]
|
||||
(if (and (= (:type shape) :text)
|
||||
(:resize-scale-text modifiers))
|
||||
(let [merge-attrs (fn [attrs]
|
||||
(let [font-size (-> (get attrs :font-size 14)
|
||||
(d/parse-double)
|
||||
(* (get-in modifiers [:resize-vector :x] 1))
|
||||
(* (get-in modifiers [:resize-vector-2 :x] 1))
|
||||
(str))]
|
||||
(d/txt-merge attrs {:font-size font-size})))]
|
||||
(update shape :content #(txt/transform-nodes
|
||||
txt/is-text-node?
|
||||
merge-attrs
|
||||
%)))
|
||||
shape))
|
||||
|
||||
(defn apply-modifiers
|
||||
[shape modifiers]
|
||||
(let [center (gco/center-shape shape)
|
||||
transform (modifiers->transform center modifiers)]
|
||||
(apply-transform shape transform)))
|
||||
(if (and (some? selrect) (d/not-empty? points))
|
||||
(-> shape
|
||||
(assoc :selrect selrect)
|
||||
(assoc :points points))
|
||||
(update-group-selrect shape children))))
|
||||
|
||||
(defn transform-shape
|
||||
[shape]
|
||||
(let [modifiers (:modifiers shape)]
|
||||
(cond
|
||||
(nil? modifiers)
|
||||
shape
|
||||
([shape]
|
||||
(let [modifiers (:modifiers shape)]
|
||||
(-> shape
|
||||
(dissoc :modifiers)
|
||||
(transform-shape modifiers))))
|
||||
|
||||
(empty-modifiers? modifiers)
|
||||
(dissoc shape :modifiers)
|
||||
([shape modifiers]
|
||||
(letfn [(apply-modifiers
|
||||
[shape modifiers]
|
||||
(if (ctm/empty? modifiers)
|
||||
shape
|
||||
(let [transform (ctm/modifiers->transform modifiers)]
|
||||
(cond-> shape
|
||||
(and (some? transform) (not= uuid/zero (:id shape))) ;; Never transform the root frame
|
||||
(apply-transform transform)
|
||||
|
||||
:else
|
||||
(let [shape (apply-displacement shape)
|
||||
modifiers (:modifiers shape)]
|
||||
(cond-> shape
|
||||
(not (empty-modifiers? modifiers))
|
||||
(-> (set-flip modifiers)
|
||||
(apply-modifiers modifiers)
|
||||
(apply-text-resize modifiers))
|
||||
(ctm/has-structure? modifiers)
|
||||
(ctm/apply-structure-modifiers modifiers)))))]
|
||||
|
||||
:always
|
||||
(dissoc :modifiers))))))
|
||||
(cond-> shape
|
||||
(and (some? modifiers) (not (ctm/empty? modifiers)))
|
||||
(apply-modifiers modifiers)))))
|
||||
|
||||
(defn apply-objects-modifiers
|
||||
[objects modifiers]
|
||||
|
||||
(loop [objects objects
|
||||
entry (first modifiers)
|
||||
modifiers (rest modifiers)]
|
||||
|
||||
(if (nil? entry)
|
||||
objects
|
||||
(let [[id modifier] entry]
|
||||
(recur (d/update-when objects id transform-shape (:modifiers modifier))
|
||||
(first modifiers)
|
||||
(rest modifiers))))))
|
||||
|
||||
(defn transform-bounds
|
||||
[points center {:keys [displacement displacement-after resize-transform-inverse resize-vector resize-origin resize-vector-2 resize-origin-2]}]
|
||||
;; FIXME: Improve Performance
|
||||
(let [resize-transform-inverse (or resize-transform-inverse (gmt/matrix))
|
||||
([points modifiers]
|
||||
(transform-bounds points nil modifiers))
|
||||
|
||||
displacement
|
||||
(when (some? displacement)
|
||||
(gmt/multiply resize-transform-inverse displacement))
|
||||
|
||||
resize-origin
|
||||
(when (some? resize-origin)
|
||||
(transform-point-center resize-origin center resize-transform-inverse))
|
||||
|
||||
resize-origin-2
|
||||
(when (some? resize-origin-2)
|
||||
(transform-point-center resize-origin-2 center resize-transform-inverse))
|
||||
]
|
||||
|
||||
(if (and (nil? displacement) (nil? resize-origin) (nil? resize-origin-2) (nil? displacement-after))
|
||||
points
|
||||
|
||||
(cond-> points
|
||||
(some? displacement)
|
||||
(gco/transform-points displacement)
|
||||
|
||||
(some? resize-origin)
|
||||
(gco/transform-points resize-origin (gmt/scale-matrix resize-vector))
|
||||
|
||||
(some? resize-origin-2)
|
||||
(gco/transform-points resize-origin-2 (gmt/scale-matrix resize-vector-2))
|
||||
|
||||
(some? displacement-after)
|
||||
(gco/transform-points displacement-after)))))
|
||||
([points center modifiers]
|
||||
(let [transform (ctm/modifiers->transform modifiers)]
|
||||
(cond-> points
|
||||
(some? transform)
|
||||
(gco/transform-points center transform)))))
|
||||
|
||||
(defn transform-selrect
|
||||
[selrect modifiers]
|
||||
(let [center (gco/center-selrect selrect)]
|
||||
(-> selrect
|
||||
(gpr/rect->points)
|
||||
(transform-bounds center modifiers)
|
||||
(gpr/points->selrect))))
|
||||
(-> selrect
|
||||
(gpr/rect->points)
|
||||
(transform-bounds modifiers)
|
||||
(gpr/points->selrect)))
|
||||
|
||||
(defn transform-selrect-matrix
|
||||
[selrect mtx]
|
||||
|
@ -662,17 +493,62 @@
|
|||
(map (comp gpr/points->selrect :points transform-shape))
|
||||
(gpr/join-selrects)))
|
||||
|
||||
(declare apply-group-modifiers)
|
||||
|
||||
(defn apply-children-modifiers
|
||||
[objects modif-tree parent-modifiers children propagate?]
|
||||
(->> children
|
||||
(map (fn [child]
|
||||
(let [modifiers (cond-> (get-in modif-tree [(:id child) :modifiers])
|
||||
propagate? (ctm/add-modifiers parent-modifiers))
|
||||
child (transform-shape child modifiers)
|
||||
parent? (cph/group-like-shape? child)
|
||||
|
||||
modif-tree
|
||||
(cond-> modif-tree
|
||||
propagate?
|
||||
(assoc-in [(:id child) :modifiers] modifiers))]
|
||||
|
||||
(cond-> child
|
||||
parent?
|
||||
(apply-group-modifiers objects modif-tree propagate?)))))))
|
||||
|
||||
(defn apply-group-modifiers
|
||||
"Apply the modifiers to the group children to calculate its selection rect"
|
||||
[group objects modif-tree]
|
||||
([group objects modif-tree]
|
||||
(apply-group-modifiers group objects modif-tree true))
|
||||
|
||||
(let [children
|
||||
(->> (:shapes group)
|
||||
(map (d/getf objects))
|
||||
(map (fn [shape]
|
||||
(let [modifiers (get modif-tree (:id shape))
|
||||
shape (-> shape (merge modifiers) transform-shape)]
|
||||
(if (= :group (:type shape))
|
||||
(apply-group-modifiers shape objects modif-tree)
|
||||
shape)))))]
|
||||
(update-group-selrect group children)))
|
||||
([group objects modif-tree propagate?]
|
||||
(let [modifiers (get-in modif-tree [(:id group) :modifiers])
|
||||
children
|
||||
(as-> (:shapes group) $
|
||||
(map (d/getf objects) $)
|
||||
(apply-children-modifiers objects modif-tree modifiers $ propagate?))]
|
||||
(cond
|
||||
(cph/mask-shape? group)
|
||||
(update-mask-selrect group children)
|
||||
|
||||
(cph/bool-shape? group)
|
||||
(transform-shape group modifiers)
|
||||
|
||||
(cph/group-shape? group)
|
||||
(update-group-selrect group children)
|
||||
|
||||
:else
|
||||
group))))
|
||||
|
||||
(defn parent-coords-rect
|
||||
[child parent]
|
||||
(-> child
|
||||
:points
|
||||
(gco/transform-points (:transform-inverse parent))
|
||||
(gpr/points->rect)))
|
||||
|
||||
(defn parent-coords-points
|
||||
[child parent]
|
||||
(-> child
|
||||
:points
|
||||
(gco/transform-points (:transform-inverse parent))
|
||||
(gpr/points->rect)
|
||||
(gpr/rect->points)
|
||||
(gco/transform-points (:transform parent))))
|
||||
|
|
|
@ -156,7 +156,7 @@
|
|||
(if (> num to) to num)))
|
||||
|
||||
(defn almost-zero? [num]
|
||||
(< (abs (double num)) 1e-5))
|
||||
(< (abs (double num)) 1e-4))
|
||||
|
||||
(defonce float-equal-precision 0.001)
|
||||
|
||||
|
@ -174,3 +174,8 @@
|
|||
(defn max-abs
|
||||
[a b]
|
||||
(max (abs a) (abs b)))
|
||||
|
||||
(defn sign
|
||||
"Get the sign (+1 / -1) for the number"
|
||||
[n]
|
||||
(if (neg? n) -1 1))
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
[app.common.data.macros :as dm]
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.geom.shapes.bool :as gshb]
|
||||
[app.common.math :as mth]
|
||||
[app.common.pages.common :refer [component-sync-attrs]]
|
||||
[app.common.pages.helpers :as cph]
|
||||
|
@ -170,7 +169,7 @@
|
|||
group
|
||||
|
||||
(= :bool (:type group))
|
||||
(gshb/update-bool-selrect group children objects)
|
||||
(gsh/update-bool-selrect group children objects)
|
||||
|
||||
(:masked-group? group)
|
||||
(set-mask-selrect group children)
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
[app.common.geom.matrix :as gmt]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.geom.shapes.bool :as gshb]
|
||||
[app.common.geom.shapes.rect :as gshr]
|
||||
[app.common.math :as mth]
|
||||
[app.common.pages :as cp]
|
||||
[app.common.pages.helpers :as cph]
|
||||
|
@ -419,7 +417,7 @@
|
|||
(every? #(apply gpt/close? %) (d/zip old-val new-val))
|
||||
|
||||
(= attr :selrect)
|
||||
(gshr/close-selrect? old-val new-val)
|
||||
(gsh/close-selrect? old-val new-val)
|
||||
|
||||
:else
|
||||
(= old-val new-val))]
|
||||
|
@ -438,7 +436,7 @@
|
|||
nil ;; so it does not need resize
|
||||
|
||||
(= (:type parent) :bool)
|
||||
(gshb/update-bool-selrect parent children objects)
|
||||
(gsh/update-bool-selrect parent children objects)
|
||||
|
||||
(= (:type parent) :group)
|
||||
(if (:masked-group? parent)
|
||||
|
|
|
@ -71,7 +71,28 @@
|
|||
:constraints-h :constraints-group
|
||||
:constraints-v :constraints-group
|
||||
:fixed-scroll :constraints-group
|
||||
:exports :exports-group})
|
||||
:exports :exports-group
|
||||
|
||||
:layout :layout-container
|
||||
:layout-dir :layout-container
|
||||
:layout-gap :layout-container
|
||||
:layout-type :layout-container
|
||||
:layout-wrap-type :layout-container
|
||||
:layout-padding-type :layout-container
|
||||
:layout-padding :layout-container
|
||||
:layout-h-orientation :layout-container
|
||||
:layout-v-orientation :layout-container
|
||||
|
||||
:layout-item-margin :layout-item
|
||||
:layout-item-margin-type :layout-item
|
||||
:layout-item-h-sizing :layout-item
|
||||
:layout-item-v-sizing :layout-item
|
||||
:layout-item-max-h :layout-item
|
||||
:layout-item-min-h :layout-item
|
||||
:layout-item-max-w :layout-item
|
||||
:layout-item-min-w :layout-item
|
||||
:layout-item-align-self :layout-item
|
||||
})
|
||||
|
||||
;; Attributes that may directly be edited by the user with forms
|
||||
(def editable-attrs
|
||||
|
@ -111,31 +132,62 @@
|
|||
:stroke-cap-start
|
||||
:stroke-cap-end
|
||||
|
||||
:exports}
|
||||
:exports
|
||||
|
||||
:group #{:proportion-lock
|
||||
:width :height
|
||||
:x :y
|
||||
:rotation
|
||||
:selrect
|
||||
:points
|
||||
:layout
|
||||
:layout-flex-dir
|
||||
:layout-gap
|
||||
:layout-gap-type
|
||||
:layout-align-items
|
||||
:layout-justify-content
|
||||
:layout-align-content
|
||||
:layout-wrap-type
|
||||
:layout-padding-type
|
||||
:layout-padding
|
||||
|
||||
:constraints-h
|
||||
:constraints-v
|
||||
:fixed-scroll
|
||||
:parent-id
|
||||
:frame-id
|
||||
: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}
|
||||
|
||||
:opacity
|
||||
:blend-mode
|
||||
:blocked
|
||||
:hidden
|
||||
:group #{:proportion-lock
|
||||
:width :height
|
||||
:x :y
|
||||
:rotation
|
||||
:selrect
|
||||
:points
|
||||
|
||||
:shadow
|
||||
:constraints-h
|
||||
:constraints-v
|
||||
:fixed-scroll
|
||||
:parent-id
|
||||
:frame-id
|
||||
|
||||
:blur
|
||||
:opacity
|
||||
:blend-mode
|
||||
:blocked
|
||||
:hidden
|
||||
|
||||
:exports}
|
||||
:shadow
|
||||
|
||||
:blur
|
||||
|
||||
:exports
|
||||
|
||||
: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}
|
||||
|
||||
:rect #{:proportion-lock
|
||||
:width :height
|
||||
|
@ -180,7 +232,17 @@
|
|||
|
||||
:blur
|
||||
|
||||
:exports}
|
||||
:exports
|
||||
|
||||
: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}
|
||||
|
||||
:circle #{:proportion-lock
|
||||
:width :height
|
||||
|
@ -223,118 +285,239 @@
|
|||
|
||||
:blur
|
||||
|
||||
:exports}
|
||||
:exports
|
||||
|
||||
:path #{:proportion-lock
|
||||
:width :height
|
||||
:x :y
|
||||
:rotation
|
||||
:selrect
|
||||
:points
|
||||
: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}
|
||||
|
||||
:constraints-h
|
||||
:constraints-v
|
||||
:fixed-scroll
|
||||
:parent-id
|
||||
:frame-id
|
||||
:path #{:proportion-lock
|
||||
:width :height
|
||||
:x :y
|
||||
:rotation
|
||||
:selrect
|
||||
:points
|
||||
|
||||
:opacity
|
||||
:blend-mode
|
||||
:blocked
|
||||
:hidden
|
||||
:constraints-h
|
||||
:constraints-v
|
||||
:fixed-scroll
|
||||
:parent-id
|
||||
:frame-id
|
||||
|
||||
:fills
|
||||
:fill-color
|
||||
:fill-opacity
|
||||
:fill-color-ref-id
|
||||
:fill-color-ref-file
|
||||
:fill-color-gradient
|
||||
:opacity
|
||||
:blend-mode
|
||||
:blocked
|
||||
:hidden
|
||||
|
||||
:strokes
|
||||
:stroke-style
|
||||
:stroke-alignment
|
||||
:stroke-width
|
||||
:stroke-color
|
||||
:stroke-color-ref-id
|
||||
:stroke-color-ref-file
|
||||
:stroke-opacity
|
||||
:stroke-color-gradient
|
||||
:stroke-cap-start
|
||||
:stroke-cap-end
|
||||
:fills
|
||||
:fill-color
|
||||
:fill-opacity
|
||||
:fill-color-ref-id
|
||||
:fill-color-ref-file
|
||||
:fill-color-gradient
|
||||
|
||||
:shadow
|
||||
:strokes
|
||||
:stroke-style
|
||||
:stroke-alignment
|
||||
:stroke-width
|
||||
:stroke-color
|
||||
:stroke-color-ref-id
|
||||
:stroke-color-ref-file
|
||||
:stroke-opacity
|
||||
:stroke-color-gradient
|
||||
:stroke-cap-start
|
||||
:stroke-cap-end
|
||||
|
||||
:blur
|
||||
:shadow
|
||||
|
||||
:exports}
|
||||
:blur
|
||||
|
||||
:text #{:proportion-lock
|
||||
:width :height
|
||||
:x :y
|
||||
:rotation
|
||||
:selrect
|
||||
:points
|
||||
:exports
|
||||
|
||||
:constraints-h
|
||||
:constraints-v
|
||||
:fixed-scroll
|
||||
:parent-id
|
||||
:frame-id
|
||||
: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}
|
||||
|
||||
:opacity
|
||||
:blend-mode
|
||||
:blocked
|
||||
:hidden
|
||||
:text #{:proportion-lock
|
||||
:width :height
|
||||
:x :y
|
||||
:rotation
|
||||
:selrect
|
||||
:points
|
||||
|
||||
:fill-color
|
||||
:fill-opacity
|
||||
:fill-color-ref-id
|
||||
:fill-color-ref-file
|
||||
:fill-color-gradient
|
||||
:constraints-h
|
||||
:constraints-v
|
||||
:fixed-scroll
|
||||
:parent-id
|
||||
:frame-id
|
||||
|
||||
:stroke-style
|
||||
:stroke-alignment
|
||||
:stroke-width
|
||||
:stroke-color
|
||||
:stroke-color-ref-id
|
||||
:stroke-color-ref-file
|
||||
:stroke-opacity
|
||||
:stroke-color-gradient
|
||||
:stroke-cap-start
|
||||
:stroke-cap-end
|
||||
:opacity
|
||||
:blend-mode
|
||||
:blocked
|
||||
:hidden
|
||||
|
||||
:shadow
|
||||
:fill-color
|
||||
:fill-opacity
|
||||
:fill-color-ref-id
|
||||
:fill-color-ref-file
|
||||
:fill-color-gradient
|
||||
|
||||
:blur
|
||||
:stroke-style
|
||||
:stroke-alignment
|
||||
:stroke-width
|
||||
:stroke-color
|
||||
:stroke-color-ref-id
|
||||
:stroke-color-ref-file
|
||||
:stroke-opacity
|
||||
:stroke-color-gradient
|
||||
:stroke-cap-start
|
||||
:stroke-cap-end
|
||||
|
||||
:typography-ref-id
|
||||
:typography-ref-file
|
||||
:shadow
|
||||
|
||||
:font-id
|
||||
:font-family
|
||||
:font-variant-id
|
||||
:font-size
|
||||
:font-weight
|
||||
:font-style
|
||||
:blur
|
||||
|
||||
:text-align
|
||||
:typography-ref-id
|
||||
:typography-ref-file
|
||||
|
||||
:text-direction
|
||||
:font-id
|
||||
:font-family
|
||||
:font-variant-id
|
||||
:font-size
|
||||
:font-weight
|
||||
:font-style
|
||||
|
||||
:line-height
|
||||
:letter-spacing
|
||||
:text-align
|
||||
|
||||
:vertical-align
|
||||
:text-direction
|
||||
|
||||
:text-decoration
|
||||
:line-height
|
||||
:letter-spacing
|
||||
|
||||
:text-transform
|
||||
:vertical-align
|
||||
|
||||
:grow-type
|
||||
:text-decoration
|
||||
|
||||
:exports}
|
||||
:text-transform
|
||||
|
||||
:image #{:proportion-lock
|
||||
:grow-type
|
||||
|
||||
:exports
|
||||
|
||||
: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}
|
||||
|
||||
:image #{:proportion-lock
|
||||
:width :height
|
||||
:x :y
|
||||
:rotation
|
||||
:rx :ry
|
||||
:r1 :r2 :r3 :r4
|
||||
:selrect
|
||||
:points
|
||||
|
||||
:constraints-h
|
||||
:constraints-v
|
||||
:fixed-scroll
|
||||
:parent-id
|
||||
:frame-id
|
||||
|
||||
:opacity
|
||||
:blend-mode
|
||||
:blocked
|
||||
:hidden
|
||||
|
||||
:shadow
|
||||
|
||||
:blur
|
||||
|
||||
:exports
|
||||
|
||||
: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}
|
||||
|
||||
:svg-raw #{:proportion-lock
|
||||
:width :height
|
||||
:x :y
|
||||
:rotation
|
||||
:rx :ry
|
||||
:r1 :r2 :r3 :r4
|
||||
:selrect
|
||||
:points
|
||||
|
||||
:constraints-h
|
||||
:constraints-v
|
||||
:fixed-scroll
|
||||
:parent-id
|
||||
:frame-id
|
||||
|
||||
:opacity
|
||||
:blend-mode
|
||||
:blocked
|
||||
:hidden
|
||||
|
||||
:fills
|
||||
:fill-color
|
||||
:fill-opacity
|
||||
:fill-color-ref-id
|
||||
:fill-color-ref-file
|
||||
:fill-color-gradient
|
||||
|
||||
:strokes
|
||||
:stroke-style
|
||||
:stroke-alignment
|
||||
:stroke-width
|
||||
:stroke-color
|
||||
:stroke-color-ref-id
|
||||
:stroke-color-ref-file
|
||||
:stroke-opacity
|
||||
:stroke-color-gradient
|
||||
:stroke-cap-start
|
||||
:stroke-cap-end
|
||||
|
||||
:shadow
|
||||
|
||||
:blur
|
||||
|
||||
:exports
|
||||
|
||||
: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}
|
||||
|
||||
:bool #{:proportion-lock
|
||||
:width :height
|
||||
:x :y
|
||||
:rotation
|
||||
|
@ -354,97 +537,36 @@
|
|||
:blocked
|
||||
:hidden
|
||||
|
||||
:fill-color
|
||||
:fill-opacity
|
||||
:fill-color-ref-id
|
||||
:fill-color-ref-file
|
||||
:fill-color-gradient
|
||||
|
||||
:stroke-style
|
||||
:stroke-alignment
|
||||
:stroke-width
|
||||
:stroke-color
|
||||
:stroke-color-ref-id
|
||||
:stroke-color-ref-file
|
||||
:stroke-opacity
|
||||
:stroke-color-gradient
|
||||
:stroke-cap-start
|
||||
:stroke-cap-end
|
||||
|
||||
:shadow
|
||||
|
||||
:blur
|
||||
|
||||
:exports}
|
||||
:exports
|
||||
|
||||
:svg-raw #{:proportion-lock
|
||||
:width :height
|
||||
:x :y
|
||||
:rotation
|
||||
:rx :ry
|
||||
:r1 :r2 :r3 :r4
|
||||
:selrect
|
||||
:points
|
||||
|
||||
:constraints-h
|
||||
:constraints-v
|
||||
:fixed-scroll
|
||||
:parent-id
|
||||
:frame-id
|
||||
|
||||
:opacity
|
||||
:blend-mode
|
||||
:blocked
|
||||
:hidden
|
||||
|
||||
:fills
|
||||
:fill-color
|
||||
:fill-opacity
|
||||
:fill-color-ref-id
|
||||
:fill-color-ref-file
|
||||
:fill-color-gradient
|
||||
|
||||
:strokes
|
||||
:stroke-style
|
||||
:stroke-alignment
|
||||
:stroke-width
|
||||
:stroke-color
|
||||
:stroke-color-ref-id
|
||||
:stroke-color-ref-file
|
||||
:stroke-opacity
|
||||
:stroke-color-gradient
|
||||
:stroke-cap-start
|
||||
:stroke-cap-end
|
||||
|
||||
:shadow
|
||||
|
||||
:blur
|
||||
|
||||
:exports}
|
||||
|
||||
:bool #{:proportion-lock
|
||||
:width :height
|
||||
:x :y
|
||||
:rotation
|
||||
:rx :ry
|
||||
:r1 :r2 :r3 :r4
|
||||
:selrect
|
||||
:points
|
||||
|
||||
:constraints-h
|
||||
:constraints-v
|
||||
:fixed-scroll
|
||||
:parent-id
|
||||
:frame-id
|
||||
|
||||
:opacity
|
||||
:blend-mode
|
||||
:blocked
|
||||
:hidden
|
||||
|
||||
:fill-color
|
||||
:fill-opacity
|
||||
:fill-color-ref-id
|
||||
:fill-color-ref-file
|
||||
:fill-color-gradient
|
||||
|
||||
:stroke-style
|
||||
:stroke-alignment
|
||||
:stroke-width
|
||||
:stroke-color
|
||||
:stroke-color-ref-id
|
||||
:stroke-color-ref-file
|
||||
:stroke-opacity
|
||||
:stroke-color-gradient
|
||||
:stroke-cap-start
|
||||
:stroke-cap-end
|
||||
|
||||
:shadow
|
||||
|
||||
:blur
|
||||
|
||||
:exports}})
|
||||
: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}})
|
||||
|
||||
|
|
|
@ -28,17 +28,27 @@
|
|||
(= frame-id uuid/zero)))
|
||||
|
||||
(defn frame-shape?
|
||||
[{:keys [type]}]
|
||||
(= type :frame))
|
||||
([objects id]
|
||||
(frame-shape? (get objects id)))
|
||||
([{:keys [type]}]
|
||||
(= type :frame)))
|
||||
|
||||
(defn group-shape?
|
||||
[{:keys [type]}]
|
||||
(= type :group))
|
||||
|
||||
(defn mask-shape?
|
||||
[{:keys [type masked-group?]}]
|
||||
(and (= type :group) masked-group?))
|
||||
|
||||
(defn bool-shape?
|
||||
[{:keys [type]}]
|
||||
(= type :bool))
|
||||
|
||||
(defn group-like-shape?
|
||||
[{:keys [type]}]
|
||||
(or (= :group type) (= :bool type)))
|
||||
|
||||
(defn text-shape?
|
||||
[{:keys [type]}]
|
||||
(= type :text))
|
||||
|
@ -63,9 +73,12 @@
|
|||
|
||||
(defn get-children-ids
|
||||
[objects id]
|
||||
(if-let [shapes (-> (get objects id) :shapes (some-> vec))]
|
||||
(into shapes (mapcat #(get-children-ids objects %)) shapes)
|
||||
[]))
|
||||
(letfn [(get-children-ids-rec
|
||||
[id processed]
|
||||
(when (not (contains? processed id))
|
||||
(when-let [shapes (-> (get objects id) :shapes (some-> vec))]
|
||||
(into shapes (mapcat #(get-children-ids-rec % (conj processed id))) shapes))))]
|
||||
(get-children-ids-rec id #{})))
|
||||
|
||||
(defn get-children
|
||||
[objects id]
|
||||
|
@ -456,7 +469,6 @@
|
|||
|
||||
(defn selected-with-children
|
||||
[objects selected]
|
||||
|
||||
(into selected
|
||||
(mapcat #(get-children-ids objects %))
|
||||
selected))
|
||||
|
|
|
@ -102,13 +102,7 @@
|
|||
(fix-frames-selrects)
|
||||
|
||||
(and (empty? (:points object)) (not= (:id object) uuid/zero))
|
||||
(fix-empty-points)
|
||||
|
||||
;; Setup an empty transformation to re-calculate selrects
|
||||
;; and points data
|
||||
:always
|
||||
(-> (assoc :modifiers {:displacement (gmt/matrix)})
|
||||
(gsh/transform-shape))))
|
||||
(fix-empty-points)))
|
||||
|
||||
(update-page [page]
|
||||
(update page :objects d/update-vals update-object))]
|
||||
|
|
500
common/src/app/common/types/modifiers.cljc
Normal file
500
common/src/app/common/types/modifiers.cljc
Normal file
|
@ -0,0 +1,500 @@
|
|||
;; 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) UXBOX Labs SL
|
||||
|
||||
(ns app.common.types.modifiers
|
||||
(:refer-clojure :exclude [empty empty?])
|
||||
(: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.math :as mth]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.spec :as us]
|
||||
[app.common.text :as txt]
|
||||
#?(:cljs [cljs.core :as c]
|
||||
:clj [clojure.core :as c])))
|
||||
|
||||
;; --- Modifiers
|
||||
|
||||
;; Moodifiers types
|
||||
;; - geometry-parent: Geometry non-recursive
|
||||
;; * move
|
||||
;; * resize
|
||||
;; * rotation
|
||||
;; - geometry-child: Geometry recursive
|
||||
;; * move
|
||||
;; * resize
|
||||
;; * rotation
|
||||
;; - structure-parent: Structure non recursive
|
||||
;; * add-children
|
||||
;; * remove-children
|
||||
;; * reflow
|
||||
;; - structure-child: Structure recursive
|
||||
;; * scale-content
|
||||
;; * rotation
|
||||
;; * change-properties
|
||||
|
||||
;; Private aux functions
|
||||
|
||||
(def conjv (fnil conj []))
|
||||
|
||||
(defn- move-vec? [vector]
|
||||
(or (not (mth/almost-zero? (:x vector)))
|
||||
(not (mth/almost-zero? (:y vector)))))
|
||||
|
||||
(defn- resize-vec? [vector]
|
||||
(or (not (mth/almost-zero? (- (:x vector) 1)))
|
||||
(not (mth/almost-zero? (- (:y vector) 1)))))
|
||||
|
||||
|
||||
(defn- mergeable-move?
|
||||
[op1 op2]
|
||||
(and (= :move (:type op1))
|
||||
(= :move (:type op2))))
|
||||
|
||||
(defn- mergeable-resize?
|
||||
[op1 op2]
|
||||
(and (= :resize (:type op1))
|
||||
(= :resize (:type op2))
|
||||
|
||||
;; Same transforms
|
||||
(gmt/close? (or (:transform op1) (gmt/matrix)) (or (:transform op2) (gmt/matrix)))
|
||||
(gmt/close? (or (:transform-inverse op1) (gmt/matrix)) (or (:transform-inverse op2) (gmt/matrix)))
|
||||
|
||||
;; Same origin
|
||||
(gpt/close? (:origin op1) (:origin op2))))
|
||||
|
||||
(defn- merge-move
|
||||
[op1 op2]
|
||||
{:type :move
|
||||
:vector (gpt/add (:vector op1) (:vector op2))})
|
||||
|
||||
(defn- merge-resize
|
||||
[op1 op2]
|
||||
(let [vector (gpt/point (* (-> op1 :vector :x) (-> op2 :vector :x))
|
||||
(* (-> op1 :vector :y) (-> op2 :vector :y)))]
|
||||
(assoc op1 :vector vector)))
|
||||
|
||||
(defn- maybe-add-move
|
||||
"Check the last operation to check if we can stack it over the last one"
|
||||
[operations op]
|
||||
(if (c/empty? operations)
|
||||
[op]
|
||||
(let [head (peek operations)]
|
||||
(if (mergeable-move? head op)
|
||||
(let [item (merge-move head op)]
|
||||
(cond-> (pop operations)
|
||||
(move-vec? (:vector item))
|
||||
(conj item)))
|
||||
(conj operations op)))))
|
||||
|
||||
(defn- maybe-add-resize
|
||||
"Check the last operation to check if we can stack it over the last one"
|
||||
[operations op]
|
||||
|
||||
(if (c/empty? operations)
|
||||
[op]
|
||||
(let [head (peek operations)]
|
||||
(if (mergeable-resize? head op)
|
||||
(let [item (merge-resize head op)]
|
||||
(cond-> (pop operations)
|
||||
(resize-vec? (:vector item))
|
||||
(conj item)))
|
||||
(conj operations op)))))
|
||||
|
||||
;; Public builder API
|
||||
|
||||
(defn empty []
|
||||
{})
|
||||
|
||||
(defn move-parent
|
||||
([modifiers x y]
|
||||
(move-parent modifiers (gpt/point x y)))
|
||||
|
||||
([modifiers vector]
|
||||
(cond-> modifiers
|
||||
(move-vec? vector)
|
||||
(update :geometry-parent conjv {:type :move :vector vector}))))
|
||||
|
||||
(defn resize-parent
|
||||
([modifiers vector origin]
|
||||
(cond-> modifiers
|
||||
(resize-vec? vector)
|
||||
(update :geometry-parent maybe-add-resize {:type :resize
|
||||
:vector vector
|
||||
:origin origin})))
|
||||
|
||||
([modifiers vector origin transform transform-inverse]
|
||||
(cond-> modifiers
|
||||
(resize-vec? vector)
|
||||
(update :geometry-parent maybe-add-resize {:type :resize
|
||||
:vector vector
|
||||
:origin origin
|
||||
:transform transform
|
||||
:transform-inverse transform-inverse}))))
|
||||
(defn move
|
||||
([modifiers x y]
|
||||
(move modifiers (gpt/point x y)))
|
||||
|
||||
([modifiers vector]
|
||||
(cond-> modifiers
|
||||
(move-vec? vector)
|
||||
(update :geometry-child maybe-add-move {:type :move :vector vector}))))
|
||||
|
||||
(defn resize
|
||||
([modifiers vector origin]
|
||||
(cond-> modifiers
|
||||
(resize-vec? vector)
|
||||
(update :geometry-child maybe-add-resize {:type :resize
|
||||
:vector vector
|
||||
:origin origin})))
|
||||
|
||||
([modifiers vector origin transform transform-inverse]
|
||||
(cond-> modifiers
|
||||
(resize-vec? vector)
|
||||
(update :geometry-child maybe-add-resize {:type :resize
|
||||
:vector vector
|
||||
:origin origin
|
||||
:transform transform
|
||||
:transform-inverse transform-inverse}))))
|
||||
|
||||
(defn rotation
|
||||
[modifiers center angle]
|
||||
(cond-> modifiers
|
||||
(not (mth/close? angle 0))
|
||||
(-> (update :structure-child conjv {:type :rotation
|
||||
:rotation angle})
|
||||
(update :geometry-child conjv {:type :rotation
|
||||
:center center
|
||||
:rotation angle}))))
|
||||
|
||||
(defn remove-children
|
||||
[modifiers shapes]
|
||||
(cond-> modifiers
|
||||
(d/not-empty? shapes)
|
||||
(update :structure-parent conjv {:type :remove-children
|
||||
:value shapes})))
|
||||
|
||||
(defn add-children
|
||||
[modifiers shapes index]
|
||||
(cond-> modifiers
|
||||
(d/not-empty? shapes)
|
||||
(update :structure-parent conjv {:type :add-children
|
||||
:value shapes
|
||||
:index index})))
|
||||
|
||||
(defn reflow
|
||||
[modifiers]
|
||||
(-> modifiers
|
||||
(update :structure-parent conjv {:type :reflow})))
|
||||
|
||||
(defn scale-content
|
||||
[modifiers value]
|
||||
(-> modifiers
|
||||
(update :structure-child conjv {:type :scale-content :value value})))
|
||||
|
||||
(defn change-property
|
||||
[modifiers property value]
|
||||
(-> modifiers
|
||||
(update :structure-child conjv {:type :change-property
|
||||
:property property
|
||||
:value value})))
|
||||
|
||||
(defn add-modifiers
|
||||
[modifiers new-modifiers]
|
||||
|
||||
(cond-> modifiers
|
||||
(some? (:geometry-child new-modifiers))
|
||||
(update :geometry-child #(d/concat-vec [] % (:geometry-child new-modifiers)))
|
||||
|
||||
(some? (:geometry-parent new-modifiers))
|
||||
(update :geometry-parent #(d/concat-vec [] % (:geometry-parent new-modifiers)))
|
||||
|
||||
(some? (:structure-parent new-modifiers))
|
||||
(update :structure-parent #(d/concat-vec [] % (:structure-parent new-modifiers)))
|
||||
|
||||
(some? (:structure-child new-modifiers))
|
||||
(update :structure-child #(d/concat-vec [] % (:structure-child new-modifiers)))))
|
||||
|
||||
|
||||
;; These are convenience methods to create single operation modifiers without the builder
|
||||
|
||||
(defn move-modifiers
|
||||
([x y]
|
||||
(move (empty) (gpt/point x y)))
|
||||
|
||||
([vector]
|
||||
(move (empty) vector)))
|
||||
|
||||
(defn move-parent-modifiers
|
||||
([x y]
|
||||
(move-parent (empty) (gpt/point x y)))
|
||||
|
||||
([vector]
|
||||
(move-parent (empty) vector)))
|
||||
|
||||
(defn resize-modifiers
|
||||
([vector origin]
|
||||
(resize (empty) vector origin))
|
||||
|
||||
([vector origin transform transform-inverse]
|
||||
(resize (empty) vector origin transform transform-inverse)))
|
||||
|
||||
(defn resize-parent-modifiers
|
||||
([vector origin]
|
||||
(resize-parent (empty) vector origin))
|
||||
|
||||
([vector origin transform transform-inverse]
|
||||
(resize-parent (empty) vector origin transform transform-inverse)))
|
||||
|
||||
(defn rotation-modifiers
|
||||
[shape center angle]
|
||||
(let [shape-center (gco/center-shape shape)
|
||||
;; Translation caused by the rotation
|
||||
move-vec
|
||||
(gpt/transform
|
||||
(gpt/point 0 0)
|
||||
(-> (gmt/matrix)
|
||||
(gmt/rotate angle center)
|
||||
(gmt/rotate (- angle) shape-center)))]
|
||||
|
||||
(-> (empty)
|
||||
(rotation shape-center angle)
|
||||
(move move-vec))))
|
||||
|
||||
(defn remove-children-modifiers
|
||||
[shapes]
|
||||
(-> (empty)
|
||||
(remove-children shapes)))
|
||||
|
||||
(defn add-children-modifiers
|
||||
[shapes index]
|
||||
(-> (empty)
|
||||
(add-children shapes index)))
|
||||
|
||||
(defn reflow-modifiers
|
||||
[]
|
||||
(-> (empty)
|
||||
(reflow)))
|
||||
|
||||
(defn scale-content-modifiers
|
||||
[value]
|
||||
(-> (empty)
|
||||
(scale-content value)))
|
||||
|
||||
(defn change-dimensions-modifiers
|
||||
[{:keys [transform transform-inverse] :as shape} attr value]
|
||||
(us/assert map? shape)
|
||||
(us/assert #{:width :height} attr)
|
||||
(us/assert number? value)
|
||||
|
||||
(let [{:keys [proportion proportion-lock]} shape
|
||||
size (select-keys (:selrect shape) [:width :height])
|
||||
new-size (if-not proportion-lock
|
||||
(assoc size attr value)
|
||||
(if (= attr :width)
|
||||
(-> size
|
||||
(assoc :width value)
|
||||
(assoc :height (/ value proportion)))
|
||||
(-> size
|
||||
(assoc :height value)
|
||||
(assoc :width (* value proportion)))))
|
||||
|
||||
width (:width new-size)
|
||||
height (:height new-size)
|
||||
|
||||
{sr-width :width sr-height :height} (:selrect shape)
|
||||
|
||||
origin (-> shape :points first)
|
||||
scalex (/ width sr-width)
|
||||
scaley (/ height sr-height)]
|
||||
|
||||
(resize-modifiers (gpt/point scalex scaley) origin transform transform-inverse)))
|
||||
|
||||
(defn change-orientation-modifiers
|
||||
[shape orientation]
|
||||
(us/assert map? shape)
|
||||
(us/verify #{:horiz :vert} orientation)
|
||||
(let [width (:width shape)
|
||||
height (:height shape)
|
||||
new-width (if (= orientation :horiz) (max width height) (min width height))
|
||||
new-height (if (= orientation :horiz) (min width height) (max width height))
|
||||
|
||||
shape-transform (:transform shape)
|
||||
shape-transform-inv (:transform-inverse shape)
|
||||
shape-center (gco/center-shape shape)
|
||||
{sr-width :width sr-height :height} (:selrect shape)
|
||||
|
||||
origin (cond-> (gpt/point (:selrect shape))
|
||||
(some? shape-transform)
|
||||
(gmt/transform-point-center shape-center shape-transform))
|
||||
|
||||
scalev (gpt/divide (gpt/point new-width new-height)
|
||||
(gpt/point sr-width sr-height))]
|
||||
|
||||
(resize-modifiers scalev origin shape-transform shape-transform-inv)))
|
||||
|
||||
;; Predicates
|
||||
|
||||
(defn empty?
|
||||
[modifiers]
|
||||
(and (c/empty? (:geometry-child modifiers))
|
||||
(c/empty? (:geometry-parent modifiers))
|
||||
(c/empty? (:structure-parent modifiers))
|
||||
(c/empty? (:structure-child modifiers))))
|
||||
|
||||
(defn child-modifiers?
|
||||
[{:keys [geometry-child structure-child]}]
|
||||
(or (d/not-empty? geometry-child)
|
||||
(d/not-empty? structure-child)))
|
||||
|
||||
(defn only-move?
|
||||
"Returns true if there are only move operations"
|
||||
[{:keys [geometry-child geometry-parent]}]
|
||||
(and (every? #(= :move (:type %)) geometry-child)
|
||||
(every? #(= :move (:type %)) geometry-parent)))
|
||||
|
||||
(defn has-geometry?
|
||||
[{:keys [geometry-parent geometry-child]}]
|
||||
(or (d/not-empty? geometry-parent)
|
||||
(d/not-empty? geometry-child)))
|
||||
|
||||
(defn has-structure?
|
||||
[{:keys [structure-parent structure-child]}]
|
||||
(or (d/not-empty? structure-parent)
|
||||
(d/not-empty? structure-child)))
|
||||
|
||||
;; Extract subsets of modifiers
|
||||
|
||||
(defn select-child-modifiers
|
||||
[modifiers]
|
||||
(select-keys modifiers [:geometry-child :structure-child]))
|
||||
|
||||
(defn select-child-geometry-modifiers
|
||||
[modifiers]
|
||||
(select-keys modifiers [:geometry-child]))
|
||||
|
||||
(defn select-parent-modifiers
|
||||
[modifiers]
|
||||
(select-keys modifiers [:geometry-parent :structure-parent]))
|
||||
|
||||
(defn select-structure
|
||||
[modifiers]
|
||||
(select-keys modifiers [:structure-parent :structure-child]))
|
||||
|
||||
(defn select-geometry
|
||||
[modifiers]
|
||||
(select-keys modifiers [:geometry-parent :geometry-child]))
|
||||
|
||||
(defn added-children-frames
|
||||
"Returns the frames that have an 'add-children' operation"
|
||||
[modif-tree]
|
||||
(let [structure-changes
|
||||
(into {}
|
||||
(comp (filter (fn [[_ val]] (-> val :modifiers :structure-parent some?)))
|
||||
(map (fn [[key val]]
|
||||
[key (-> val :modifiers :structure-parent)])))
|
||||
modif-tree)]
|
||||
(into []
|
||||
(mapcat (fn [[frame-id changes]]
|
||||
(->> changes
|
||||
(filter (fn [{:keys [type]}] (= type :add-children)))
|
||||
(mapcat (fn [{:keys [value]}]
|
||||
(->> value (map (fn [id] {:frame frame-id :shape id}))))))))
|
||||
structure-changes)))
|
||||
|
||||
;; Main transformation functions
|
||||
|
||||
(defn modifiers->transform
|
||||
"Given a set of modifiers returns its transformation matrix"
|
||||
[modifiers]
|
||||
(letfn [(apply-modifier [matrix {:keys [type vector rotation center origin transform transform-inverse] :as modifier}]
|
||||
(case type
|
||||
:move
|
||||
(gmt/multiply (gmt/translate-matrix vector) matrix)
|
||||
|
||||
:resize
|
||||
(gmt/multiply
|
||||
(-> (gmt/matrix)
|
||||
(gmt/translate origin)
|
||||
(cond-> (some? transform)
|
||||
(gmt/multiply transform))
|
||||
(gmt/scale vector)
|
||||
(cond-> (some? transform-inverse)
|
||||
(gmt/multiply transform-inverse))
|
||||
(gmt/translate (gpt/negate origin)))
|
||||
matrix)
|
||||
|
||||
:rotation
|
||||
(gmt/multiply
|
||||
(-> (gmt/matrix)
|
||||
(gmt/translate center)
|
||||
(gmt/multiply (gmt/rotate-matrix rotation))
|
||||
(gmt/translate (gpt/negate center)))
|
||||
matrix)))]
|
||||
(let [modifiers (if (d/not-empty? (:geometry-parent modifiers))
|
||||
(d/concat-vec (:geometry-parent modifiers) (:geometry-child modifiers))
|
||||
(:geometry-child modifiers))]
|
||||
(when (d/not-empty? modifiers)
|
||||
(->> modifiers
|
||||
(reduce apply-modifier (gmt/matrix)))))))
|
||||
|
||||
(defn apply-structure-modifiers
|
||||
"Apply structure changes to a shape"
|
||||
[shape modifiers]
|
||||
(letfn [(scale-text-content
|
||||
[content value]
|
||||
|
||||
(->> content
|
||||
(txt/transform-nodes
|
||||
txt/is-text-node?
|
||||
(fn [attrs]
|
||||
(let [font-size (-> (get attrs :font-size 14)
|
||||
(d/parse-double)
|
||||
(* value)
|
||||
(str)) ]
|
||||
(d/txt-merge attrs {:font-size font-size}))))))
|
||||
|
||||
(apply-scale-content
|
||||
[shape value]
|
||||
|
||||
(cond-> shape
|
||||
(cph/text-shape? shape)
|
||||
(update :content scale-text-content value)))]
|
||||
(let [remove-children
|
||||
(fn [shapes children-to-remove]
|
||||
(let [remove? (set children-to-remove)]
|
||||
(d/removev remove? shapes)))
|
||||
|
||||
apply-modifier
|
||||
(fn [shape {:keys [type property value index rotation]}]
|
||||
(cond-> shape
|
||||
(= type :rotation)
|
||||
(update :rotation #(mod (+ % rotation) 360))
|
||||
|
||||
(and (= type :add-children) (some? index))
|
||||
(update :shapes
|
||||
(fn [shapes]
|
||||
(if (vector? shapes)
|
||||
(cph/insert-at-index shapes index value)
|
||||
(d/concat-vec shapes value))))
|
||||
|
||||
(and (= type :add-children) (nil? index))
|
||||
(update :shapes d/concat-vec value)
|
||||
|
||||
(= type :remove-children)
|
||||
(update :shapes remove-children value)
|
||||
|
||||
(= type :scale-content)
|
||||
(apply-scale-content value)
|
||||
|
||||
(= type :change-property)
|
||||
(assoc property value)))]
|
||||
|
||||
(as-> shape $
|
||||
(reduce apply-modifier $ (:structure-parent modifiers))
|
||||
(reduce apply-modifier $ (:structure-child modifiers))))))
|
|
@ -9,12 +9,33 @@
|
|||
[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 :stretch
|
||||
;; :layout-justify-content ;; :start :center :end :space-between :space-around
|
||||
;; :layout-align-content ;; :start :center :end :space-between :space-around :stretch (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-item-margin ;; {:m1 0 :m2 0 :m3 0 :m4 0}
|
||||
;; :layout-item-margin-type ;; :simple :multiple
|
||||
;; :layout-item-h-sizing ;; :fill :fix :auto
|
||||
;; :layout-item-v-sizing ;; :fill :fix :auto
|
||||
;; :layout-item-max-h ;; num
|
||||
;; :layout-item-min-h ;; num
|
||||
;; :layout-item-max-w ;; num
|
||||
;; :layout-item-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})
|
||||
(s/def ::layout-gap ::us/safe-number)
|
||||
(s/def ::layout-align-items #{:start :end :center :strech})
|
||||
(s/def ::layout-align-content #{:start :end :center :space-between :space-around :strech})
|
||||
(s/def ::layout-align-items #{:start :end :center :stretch})
|
||||
(s/def ::layout-align-content #{:start :end :center :space-between :space-around :stretch})
|
||||
(s/def ::layout-justify-content #{:start :center :end :space-between :space-around})
|
||||
(s/def ::layout-wrap-type #{:wrap :no-wrap})
|
||||
(s/def ::layout-padding-type #{:simple :multiple})
|
||||
|
@ -25,15 +46,14 @@
|
|||
(s/def ::p4 ::us/safe-number)
|
||||
|
||||
(s/def ::layout-padding
|
||||
(s/keys :req-un [::p1]
|
||||
:opt-un [::p2 ::p3 ::p4]))
|
||||
(s/keys :opt-un [::p1 ::p2 ::p3 ::p4]))
|
||||
|
||||
(s/def ::row-gap ::us/safe-number)
|
||||
(s/def ::column-gap ::us/safe-number)
|
||||
(s/def ::layout-type #{:flex :grid})
|
||||
|
||||
(s/def ::layout-gap
|
||||
(s/keys :req-un [::row-gap ::column-gap]))
|
||||
(s/keys :opt-un [::row-gap ::column-gap]))
|
||||
|
||||
(s/def ::layout-container-props
|
||||
(s/keys :opt-un [::layout
|
||||
|
@ -53,25 +73,223 @@
|
|||
(s/def ::m3 ::us/safe-number)
|
||||
(s/def ::m4 ::us/safe-number)
|
||||
|
||||
(s/def ::layout-margin (s/keys :req-un [::m1]
|
||||
:opt-un [::m2 ::m3 ::m4]))
|
||||
(s/def ::layout-item-margin (s/keys :opt-un [::m1 ::m2 ::m3 ::m4]))
|
||||
|
||||
(s/def ::layout-margin-type #{:simple :multiple})
|
||||
(s/def ::layout-h-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-max-h ::us/safe-number)
|
||||
(s/def ::layout-min-h ::us/safe-number)
|
||||
(s/def ::layout-max-w ::us/safe-number)
|
||||
(s/def ::layout-min-w ::us/safe-number)
|
||||
(s/def ::layout-item-margin-type #{:simple :multiple})
|
||||
(s/def ::layout-item-h-sizing #{:fill :fix :auto})
|
||||
(s/def ::layout-item-v-sizing #{:fill :fix :auto})
|
||||
(s/def ::layout-item-align-self #{:start :end :center :stretch})
|
||||
(s/def ::layout-item-max-h ::us/safe-number)
|
||||
(s/def ::layout-item-min-h ::us/safe-number)
|
||||
(s/def ::layout-item-max-w ::us/safe-number)
|
||||
(s/def ::layout-item-min-w ::us/safe-number)
|
||||
|
||||
(s/def ::layout-child-props
|
||||
(s/keys :opt-un [::layout-margin
|
||||
::layout-margin-type
|
||||
::layout-h-behavior
|
||||
::layout-v-behavior
|
||||
::layout-max-h
|
||||
::layout-min-h
|
||||
::layout-max-w
|
||||
::layout-min-w
|
||||
::layout-align-self]))
|
||||
(s/keys :opt-un [::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]))
|
||||
|
||||
(defn layout?
|
||||
([objects id]
|
||||
(layout? (get objects id)))
|
||||
([shape]
|
||||
(and (= :frame (:type shape)) (= :flex (:layout shape)))))
|
||||
|
||||
(defn layout-child? [objects shape]
|
||||
(let [parent-id (:parent-id shape)
|
||||
parent (get objects parent-id)]
|
||||
(layout? parent)))
|
||||
|
||||
(defn inside-layout?
|
||||
"Check if the shape is inside a layout"
|
||||
[objects shape]
|
||||
|
||||
(loop [current-id (:id shape)]
|
||||
(let [current (get objects current-id)]
|
||||
(cond
|
||||
(or (nil? current) (= current-id (:parent-id current)))
|
||||
false
|
||||
|
||||
(= :frame (:type current))
|
||||
(:layout current)
|
||||
|
||||
:else
|
||||
(recur (:parent-id current))))))
|
||||
|
||||
(defn wrap? [{:keys [layout-wrap-type]}]
|
||||
(= layout-wrap-type :wrap))
|
||||
|
||||
(defn fill-width? [child]
|
||||
(= :fill (:layout-item-h-sizing child)))
|
||||
|
||||
(defn fill-height? [child]
|
||||
(= :fill (:layout-item-v-sizing child)))
|
||||
|
||||
(defn auto-width? [child]
|
||||
(= :auto (:layout-item-h-sizing child)))
|
||||
|
||||
(defn auto-height? [child]
|
||||
(= :auto (:layout-item-v-sizing 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-item-min-w child)))
|
||||
(max 0 (:layout-item-min-w child))
|
||||
0))
|
||||
|
||||
(defn child-max-width
|
||||
[child]
|
||||
(if (and (fill-width? child)
|
||||
(some? (:layout-item-max-w child)))
|
||||
(max 0 (:layout-item-max-w child))
|
||||
##Inf))
|
||||
|
||||
(defn child-min-height
|
||||
[child]
|
||||
(if (and (fill-height? child)
|
||||
(some? (:layout-item-min-h child)))
|
||||
(max 0 (:layout-item-min-h child))
|
||||
0))
|
||||
|
||||
(defn child-max-height
|
||||
[child]
|
||||
(if (and (fill-height? child)
|
||||
(some? (:layout-item-max-h child)))
|
||||
(max 0 (:layout-item-max-h child))
|
||||
##Inf))
|
||||
|
||||
(defn child-margins
|
||||
[{{:keys [m1 m2 m3 m4]} :layout-item-margin :keys [layout-item-margin-type]}]
|
||||
(let [m1 (or m1 0)
|
||||
m2 (or m2 0)
|
||||
m3 (or m3 0)
|
||||
m4 (or m4 0)]
|
||||
(if (= layout-item-margin-type :multiple)
|
||||
[m1 m2 m3 m4]
|
||||
[m1 m1 m1 m1])))
|
||||
|
||||
(defn child-height-margin
|
||||
[child]
|
||||
(let [[top _ bottom _] (child-margins child)]
|
||||
(+ top bottom)))
|
||||
|
||||
(defn child-width-margin
|
||||
[child]
|
||||
(let [[_ right _ left] (child-margins child)]
|
||||
(+ right left)))
|
||||
|
||||
(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 content-start?
|
||||
[{:keys [layout-align-content]}]
|
||||
(= :start layout-align-content))
|
||||
|
||||
(defn content-center?
|
||||
[{:keys [layout-align-content]}]
|
||||
(= :center layout-align-content))
|
||||
|
||||
(defn content-end?
|
||||
[{:keys [layout-align-content]}]
|
||||
(= :end layout-align-content))
|
||||
|
||||
(defn content-between?
|
||||
[{:keys [layout-align-content]}]
|
||||
(= :space-between layout-align-content))
|
||||
|
||||
(defn content-around?
|
||||
[{:keys [layout-align-content]}]
|
||||
(= :space-around layout-align-content))
|
||||
|
||||
(defn content-stretch?
|
||||
[{:keys [layout-align-content]}]
|
||||
(or (= :stretch layout-align-content)
|
||||
(nil? layout-align-content)))
|
||||
|
||||
|
||||
(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))
|
||||
|
||||
(defn align-self-start? [{:keys [layout-item-align-self]}]
|
||||
(= :start layout-item-align-self))
|
||||
|
||||
(defn align-self-end? [{:keys [layout-item-align-self]}]
|
||||
(= :end layout-item-align-self))
|
||||
|
||||
(defn align-self-center? [{:keys [layout-item-align-self]}]
|
||||
(= :center layout-item-align-self))
|
||||
|
||||
(defn align-self-stretch? [{:keys [layout-item-align-self]}]
|
||||
(= :stretch layout-item-align-self))
|
||||
|
|
|
@ -224,8 +224,31 @@
|
|||
"Search for the top nested frame for positioning shapes when moving or creating.
|
||||
Looks for all the frames in a position and then goes in depth between the top-most and its
|
||||
children to find the target."
|
||||
[objects position]
|
||||
(let [frame-ids (all-frames-by-position objects position)
|
||||
([objects position]
|
||||
(top-nested-frame objects position nil))
|
||||
|
||||
([objects position excluded]
|
||||
(assert (or (nil? excluded) (set? excluded)))
|
||||
|
||||
(let [frame-ids (cond->> (all-frames-by-position objects position)
|
||||
(some? excluded)
|
||||
(remove excluded))
|
||||
|
||||
frame-set (set frame-ids)]
|
||||
|
||||
(loop [current-id (first frame-ids)]
|
||||
(let [current-shape (get objects current-id)
|
||||
child-frame-id (d/seek #(contains? frame-set %)
|
||||
(-> (:shapes current-shape) reverse))]
|
||||
(if (nil? child-frame-id)
|
||||
(or current-id uuid/zero)
|
||||
(recur child-frame-id)))))))
|
||||
|
||||
(defn top-nested-frame-ids
|
||||
"Search the top nested frame in a list of ids"
|
||||
[objects ids]
|
||||
|
||||
(let [frame-ids (->> ids (filter #(cph/frame-shape? objects %)))
|
||||
frame-set (set frame-ids)]
|
||||
(loop [current-id (first frame-ids)]
|
||||
(let [current-shape (get objects current-id)
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.math :as mth :refer [close?]]
|
||||
[app.common.types.modifiers :as ctm]
|
||||
[app.common.types.shape :as cts]
|
||||
[clojure.test :as t]))
|
||||
|
||||
|
@ -59,7 +60,7 @@
|
|||
|
||||
(t/testing "Transform shape with translation modifiers"
|
||||
(t/are [type]
|
||||
(let [modifiers {:displacement (gmt/translate-matrix (gpt/point 10 -10))}]
|
||||
(let [modifiers (ctm/move-modifiers (gpt/point 10 -10))]
|
||||
(let [shape-before (create-test-shape type {:modifiers modifiers})
|
||||
shape-after (gsh/transform-shape shape-before)]
|
||||
(t/is (not= shape-before shape-after))
|
||||
|
@ -91,9 +92,7 @@
|
|||
|
||||
(t/testing "Transform shape with resize modifiers"
|
||||
(t/are [type]
|
||||
(let [modifiers {:resize-origin (gpt/point 0 0)
|
||||
:resize-vector (gpt/point 2 2)
|
||||
:resize-transform (gmt/matrix)}
|
||||
(let [modifiers (ctm/resize-modifiers (gpt/point 2 2) (gpt/point 0 0))
|
||||
shape-before (create-test-shape type {:modifiers modifiers})
|
||||
shape-after (gsh/transform-shape shape-before)]
|
||||
(t/is (not= shape-before shape-after))
|
||||
|
@ -113,9 +112,7 @@
|
|||
|
||||
(t/testing "Transform with empty resize"
|
||||
(t/are [type]
|
||||
(let [modifiers {:resize-origin (gpt/point 0 0)
|
||||
:resize-vector (gpt/point 1 1)
|
||||
:resize-transform (gmt/matrix)}
|
||||
(let [modifiers (ctm/resize-modifiers (gpt/point 1 1) (gpt/point 0 0))
|
||||
shape-before (create-test-shape type {:modifiers modifiers})
|
||||
shape-after (gsh/transform-shape shape-before)]
|
||||
(t/are [prop]
|
||||
|
@ -126,9 +123,7 @@
|
|||
|
||||
(t/testing "Transform with resize=0"
|
||||
(t/are [type]
|
||||
(let [modifiers {:resize-origin (gpt/point 0 0)
|
||||
:resize-vector (gpt/point 0 0)
|
||||
:resize-transform (gmt/matrix)}
|
||||
(let [modifiers (ctm/resize-modifiers (gpt/point 0 0) (gpt/point 0 0))
|
||||
shape-before (create-test-shape type {:modifiers modifiers})
|
||||
shape-after (gsh/transform-shape shape-before)]
|
||||
(t/is (> (get-in shape-before [:selrect :width])
|
||||
|
@ -142,13 +137,11 @@
|
|||
|
||||
(t/testing "Transform shape with rotation modifiers"
|
||||
(t/are [type]
|
||||
(let [modifiers {:rotation 30}
|
||||
shape-before (create-test-shape type {:modifiers modifiers})
|
||||
(let [shape-before (create-test-shape type)
|
||||
modifiers (ctm/rotation-modifiers shape-before (gsh/center-shape shape-before) 30 )
|
||||
shape-before (assoc shape-before :modifiers modifiers)
|
||||
shape-after (gsh/transform-shape shape-before)]
|
||||
|
||||
(t/is (not= shape-before shape-after))
|
||||
|
||||
;; Selrect won't change with a rotation, but points will
|
||||
(t/is (close? (get-in shape-before [:selrect :x])
|
||||
(get-in shape-after [:selrect :x])))
|
||||
|
||||
|
@ -166,9 +159,9 @@
|
|||
|
||||
(t/testing "Transform shape with rotation = 0 should leave equal selrect"
|
||||
(t/are [type]
|
||||
(let [modifiers {:rotation 0}
|
||||
shape-before (create-test-shape type {:modifiers modifiers})
|
||||
shape-after (gsh/transform-shape shape-before)]
|
||||
(let [shape-before (create-test-shape type)
|
||||
modifiers (ctm/rotation-modifiers shape-before (gsh/center-shape shape-before) 0)
|
||||
shape-after (gsh/transform-shape (assoc shape-before :modifiers modifiers))]
|
||||
(t/are [prop]
|
||||
(t/is (close? (get-in shape-before [:selrect prop])
|
||||
(get-in shape-after [:selrect prop])))
|
||||
|
@ -177,12 +170,12 @@
|
|||
|
||||
(t/testing "Transform shape with invalid selrect fails gracefully"
|
||||
(t/are [type selrect]
|
||||
(let [modifiers {:displacement (gmt/matrix)}
|
||||
shape-before (-> (create-test-shape type {:modifiers modifiers})
|
||||
(assoc :selrect selrect))
|
||||
shape-after (gsh/transform-shape shape-before)]
|
||||
(= (:selrect shape-before)
|
||||
(:selrect shape-after)))
|
||||
(let [modifiers (ctm/move-modifiers 0 0)
|
||||
shape-before (-> (create-test-shape type) (assoc :selrect selrect))
|
||||
shape-after (gsh/transform-shape shape-before modifiers)]
|
||||
|
||||
(t/is (= (:selrect shape-before)
|
||||
(:selrect shape-after))))
|
||||
|
||||
:rect {:x 0.0 :y 0.0 :x1 0.0 :y1 0.0 :x2 ##Inf :y2 ##Inf :width ##Inf :height ##Inf}
|
||||
:path {:x 0.0 :y 0.0 :x1 0.0 :y1 0.0 :x2 ##Inf :y2 ##Inf :width ##Inf :height ##Inf}
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500" viewBox="0 0 132.292 132.292">
|
||||
<path d="M0 0v132.292h132.292V0H0zm11.232 11.232H121.06v109.827H11.232V11.232zm17.876 18.346v73.136h29.726V29.578H29.108zm44.45 0v73.136h29.726V29.578H73.558z"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 267 B |
3
frontend/resources/images/icons/layout-columns.svg
Normal file
3
frontend/resources/images/icons/layout-columns.svg
Normal file
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500" viewBox="0 0 132.292 132.292">
|
||||
<path transform="rotate(90, 66.146, 66.146)" d="M0 0v132.292h132.292V0H0zm11.232 11.232H121.06v109.827H11.232V11.232zm17.876 18.346v73.136h29.726V29.578H29.108zm44.45 0v73.136h29.726V29.578H73.558z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 306 B |
Before Width: | Height: | Size: 267 B After Width: | Height: | Size: 267 B |
|
@ -182,6 +182,10 @@
|
|||
&.error {
|
||||
border-color: $color-danger;
|
||||
}
|
||||
|
||||
&[disabled] {
|
||||
color: $color-gray-30;
|
||||
}
|
||||
}
|
||||
|
||||
.input-select {
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
[app.common.types.pages-list :as ctpl]
|
||||
[app.common.types.shape :as cts]
|
||||
[app.common.types.shape-tree :as ctst]
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.config :as cfg]
|
||||
[app.main.data.comments :as dcm]
|
||||
|
@ -52,8 +53,8 @@
|
|||
[app.main.data.workspace.path.shapes-to-path :as dwps]
|
||||
[app.main.data.workspace.persistence :as dwp]
|
||||
[app.main.data.workspace.selection :as dws]
|
||||
[app.main.data.workspace.shape-layout :as dwsl]
|
||||
[app.main.data.workspace.shapes :as dwsh]
|
||||
[app.main.data.workspace.shapes-update-layout :as dwul]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.main.data.workspace.svg-upload :as svg]
|
||||
[app.main.data.workspace.thumbnails :as dwth]
|
||||
|
@ -686,11 +687,16 @@
|
|||
shapes-to-detach
|
||||
shapes-to-reroot
|
||||
shapes-to-deroot
|
||||
ids)]
|
||||
ids)
|
||||
|
||||
layouts-to-update
|
||||
(into #{}
|
||||
(filter (partial ctl/layout? objects))
|
||||
(concat [parent-id] (cph/get-parent-ids objects parent-id)))]
|
||||
|
||||
(rx/of (dch/commit-changes changes)
|
||||
(dwco/expand-collapse parent-id)
|
||||
(dwsl/update-layout-positions [parent-id]))))))
|
||||
(dwul/update-layout-positions layouts-to-update))))))
|
||||
|
||||
(defn relocate-selected-shapes
|
||||
[parent-id to-index]
|
||||
|
@ -1571,37 +1577,6 @@
|
|||
|
||||
(rx/of (dch/commit-changes changes))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Artboard
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn create-artboard-from-selection
|
||||
[]
|
||||
(ptk/reify ::create-artboard-from-selection
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [page-id (:current-page-id state)
|
||||
objects (wsh/lookup-page-objects state page-id)
|
||||
selected (wsh/lookup-selected state)
|
||||
selected-objs (map #(get objects %) selected)]
|
||||
(when (d/not-empty? selected)
|
||||
(let [srect (gsh/selection-rect selected-objs)
|
||||
frame-id (get-in objects [(first selected) :frame-id])
|
||||
parent-id (get-in objects [(first selected) :parent-id])
|
||||
shape (-> (cts/make-minimal-shape :frame)
|
||||
(merge {:x (:x srect) :y (:y srect) :width (:width srect) :height (:height srect)})
|
||||
(assoc :frame-id frame-id :parent-id parent-id)
|
||||
(cond-> (not= frame-id uuid/zero)
|
||||
(assoc :fills [] :hide-in-viewer true))
|
||||
(cts/setup-rect-selrect))]
|
||||
(rx/of
|
||||
(dwu/start-undo-transaction)
|
||||
(dwsh/add-shape shape)
|
||||
(dwsh/move-shapes-into-frame (:id shape) selected)
|
||||
|
||||
(dwu/commit-undo-transaction))))))))
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Remove graphics
|
||||
;; TODO: this should be deprecated and removed together with components-v2
|
||||
|
|
|
@ -159,8 +159,8 @@
|
|||
build-move-event
|
||||
(fn [comment-thread]
|
||||
(let [frame (get objects (:frame-id comment-thread))
|
||||
frame' (-> (merge frame (get object-modifiers (:frame-id comment-thread)))
|
||||
(gsh/transform-shape))
|
||||
modifiers (get object-modifiers (:frame-id comment-thread))
|
||||
frame' (gsh/transform-shape frame modifiers)
|
||||
moved (gpt/to-vec (gpt/point (:x frame) (:y frame))
|
||||
(gpt/point (:x frame') (:y frame')))
|
||||
position (get-in threads-position-map [(:id comment-thread) :position])
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.math :as mth]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.types.modifiers :as ctm]
|
||||
[app.common.types.shape :as cts]
|
||||
[app.common.types.shape-tree :as ctst]
|
||||
[app.common.uuid :as uuid]
|
||||
|
@ -20,29 +21,38 @@
|
|||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]))
|
||||
|
||||
(defn truncate-zero [num default]
|
||||
(if (mth/almost-zero? num) default num))
|
||||
(defn adjust-ratio
|
||||
[point initial]
|
||||
(let [v (gpt/to-vec point initial)
|
||||
dx (mth/abs (:x v))
|
||||
dy (mth/abs (:y v))
|
||||
sx (mth/sign (:x v))
|
||||
sy (mth/sign (:y v))]
|
||||
|
||||
(cond-> point
|
||||
(> dx dy)
|
||||
(assoc :y (- (:y point) (* sy (- dx dy))))
|
||||
|
||||
(> dy dx)
|
||||
(assoc :x (- (:x point) (* sx (- dy dx)))))))
|
||||
|
||||
(defn resize-shape [{:keys [x y width height] :as shape} initial point lock?]
|
||||
(let [draw-rect (gsh/make-rect initial (cond-> point lock? (adjust-ratio initial)))
|
||||
shape-rect (gsh/make-rect x y width height)
|
||||
|
||||
scalev (gpt/point (/ (:width draw-rect) (:width shape-rect))
|
||||
(/ (:height draw-rect) (:height shape-rect)))
|
||||
|
||||
movev (gpt/to-vec (gpt/point shape-rect) (gpt/point draw-rect))]
|
||||
|
||||
(defn resize-shape [{:keys [x y width height] :as shape} point lock?]
|
||||
(let [;; The new shape behaves like a resize on the bottom-right corner
|
||||
initial (gpt/point (+ x width) (+ y height))
|
||||
shapev (gpt/point width height)
|
||||
deltav (gpt/to-vec initial point)
|
||||
scalev (-> (gpt/divide (gpt/add shapev deltav) shapev)
|
||||
(update :x truncate-zero 0.01)
|
||||
(update :y truncate-zero 0.01))
|
||||
scalev (if lock?
|
||||
(let [v (max (:x scalev) (:y scalev))]
|
||||
(gpt/point v v))
|
||||
scalev)]
|
||||
(-> shape
|
||||
(assoc :click-draw? false)
|
||||
(assoc-in [:modifiers :resize-vector] scalev)
|
||||
(assoc-in [:modifiers :resize-origin] (gpt/point x y))
|
||||
(assoc-in [:modifiers :resize-rotation] 0))))
|
||||
(gsh/transform-shape (-> (ctm/empty)
|
||||
(ctm/resize scalev (gpt/point x y))
|
||||
(ctm/move movev))))))
|
||||
|
||||
(defn update-drawing [state point lock?]
|
||||
(update-in state [:workspace-drawing :object] resize-shape point lock?))
|
||||
(defn update-drawing [state initial point lock?]
|
||||
(update-in state [:workspace-drawing :object] resize-shape initial point lock?))
|
||||
|
||||
(defn move-drawing
|
||||
[{:keys [x y]}]
|
||||
|
@ -58,8 +68,7 @@
|
|||
layout (get state :workspace-layout)
|
||||
snap-pixel? (contains? layout :snap-pixel-grid)
|
||||
|
||||
initial (cond-> @ms/mouse-position
|
||||
snap-pixel? gpt/round)
|
||||
initial (cond-> @ms/mouse-position snap-pixel? gpt/round)
|
||||
|
||||
page-id (:current-page-id state)
|
||||
objects (wsh/lookup-page-objects state page-id)
|
||||
|
@ -97,7 +106,7 @@
|
|||
(rx/map #(conj current %)))))
|
||||
(rx/map
|
||||
(fn [[_ shift? point]]
|
||||
#(update-drawing % (cond-> point snap-pixel? gpt/round) shift?)))
|
||||
#(update-drawing % initial (cond-> point snap-pixel? gpt/round) shift?)))
|
||||
|
||||
(rx/take-until stoper))
|
||||
(rx/of (common/handle-finish-drawing)))))))
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
|
||||
(ns app.main.data.workspace.drawing.common
|
||||
(:require
|
||||
[app.common.geom.matrix :as gmt]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.math :as mth]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.types.modifiers :as ctm]
|
||||
[app.common.types.shape :as cts]
|
||||
[app.main.data.workspace.shapes :as dwsh]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
|
@ -51,8 +51,7 @@
|
|||
|
||||
(and click-draw? (not text?))
|
||||
(-> (assoc :width min-side :height min-side)
|
||||
(assoc-in [:modifiers :displacement]
|
||||
(gmt/translate-matrix (- (/ min-side 2)) (- (/ min-side 2)))))
|
||||
(gsh/transform-shape (ctm/move-modifiers (- (/ min-side 2)) (- (/ min-side 2)))))
|
||||
|
||||
(and click-draw? text?)
|
||||
(assoc :height 17 :width 4 :grow-type :auto-width)
|
||||
|
@ -61,8 +60,7 @@
|
|||
(cts/setup-rect-selrect)
|
||||
|
||||
:always
|
||||
(-> (gsh/transform-shape)
|
||||
(dissoc :initialized? :click-draw?)))]
|
||||
(dissoc :initialized? :click-draw?))]
|
||||
;; Add & select the created shape to the workspace
|
||||
(rx/concat
|
||||
(if (= :text (:type shape))
|
||||
|
|
|
@ -79,8 +79,7 @@
|
|||
build-move-event
|
||||
(fn [guide]
|
||||
(let [frame (get objects (:frame-id guide))
|
||||
frame' (-> (merge frame (get object-modifiers (:frame-id guide)))
|
||||
(gsh/transform-shape))
|
||||
frame' (gsh/transform-shape (get object-modifiers (:frame-id guide)))
|
||||
|
||||
moved (gpt/to-vec (gpt/point (:x frame) (:y frame))
|
||||
(gpt/point (:x frame') (:y frame')))
|
||||
|
|
297
frontend/src/app/main/data/workspace/modifiers.cljs
Normal file
297
frontend/src/app/main/data/workspace/modifiers.cljs
Normal file
|
@ -0,0 +1,297 @@
|
|||
;; 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.main.data.workspace.modifiers
|
||||
"Events related with shapes transformations"
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.math :as mth]
|
||||
[app.common.pages.common :as cpc]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.spec :as us]
|
||||
[app.common.types.modifiers :as ctm]
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.comments :as-alias dwcm]
|
||||
[app.main.data.workspace.guides :as-alias dwg]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[beicon.core :as rx]
|
||||
[cljs.spec.alpha :as s]
|
||||
[potok.core :as ptk]))
|
||||
|
||||
;; -- temporary modifiers -------------------------------------------
|
||||
|
||||
;; During an interactive transformation of shapes (e.g. when resizing or rotating
|
||||
;; a group with the mouse), there are a lot of objects that need to be modified
|
||||
;; (in this case, the group and all its children).
|
||||
;;
|
||||
;; To avoid updating the shapes theirselves, and forcing redraw of all components
|
||||
;; that depend on the "objects" global state, we set a "modifiers" structure, with
|
||||
;; the changes that need to be applied, and store it in :workspace-modifiers global
|
||||
;; variable. The viewport reads this and merges it into the objects list it uses to
|
||||
;; paint the viewport content, redrawing only the objects that have new modifiers.
|
||||
;;
|
||||
;; When the interaction is finished (e.g. user releases mouse button), the
|
||||
;; apply-modifiers event is done, that consolidates all modifiers into the base
|
||||
;; geometric attributes of the shapes.
|
||||
|
||||
|
||||
(defn- check-delta
|
||||
"If the shape is a component instance, check its relative position respect the
|
||||
root of the component, and see if it changes after applying a transformation."
|
||||
[shape root transformed-shape transformed-root objects modif-tree]
|
||||
(let [root
|
||||
(cond
|
||||
(:component-root? shape)
|
||||
shape
|
||||
|
||||
(nil? root)
|
||||
(cph/get-root-shape objects shape)
|
||||
|
||||
:else root)
|
||||
|
||||
transformed-root
|
||||
(cond
|
||||
(:component-root? transformed-shape)
|
||||
transformed-shape
|
||||
|
||||
(nil? transformed-root)
|
||||
(as-> (cph/get-root-shape objects transformed-shape) $
|
||||
(gsh/transform-shape (merge $ (get modif-tree (:id $)))))
|
||||
|
||||
:else transformed-root)
|
||||
|
||||
shape-delta
|
||||
(when root
|
||||
(gpt/point (- (gsh/left-bound shape) (gsh/left-bound root))
|
||||
(- (gsh/top-bound shape) (gsh/top-bound root))))
|
||||
|
||||
transformed-shape-delta
|
||||
(when transformed-root
|
||||
(gpt/point (- (gsh/left-bound transformed-shape) (gsh/left-bound transformed-root))
|
||||
(- (gsh/top-bound transformed-shape) (gsh/top-bound transformed-root))))
|
||||
|
||||
;; There are cases in that the coordinates change slightly (e.g. when
|
||||
;; rounding to pixel, or when recalculating text positions in different
|
||||
;; zoom levels). To take this into account, we ignore movements smaller
|
||||
;; than 1 pixel.
|
||||
distance (if (and shape-delta transformed-shape-delta)
|
||||
(gpt/distance-vector shape-delta transformed-shape-delta)
|
||||
(gpt/point 0 0))
|
||||
|
||||
ignore-geometry? (and (< (:x distance) 1) (< (:y distance) 1))]
|
||||
|
||||
[root transformed-root ignore-geometry?]))
|
||||
|
||||
(defn- get-ignore-tree
|
||||
"Retrieves a map with the flag `ignore-geometry?` given a tree of modifiers"
|
||||
([modif-tree objects shape]
|
||||
(get-ignore-tree modif-tree objects shape nil nil {}))
|
||||
|
||||
([modif-tree objects shape root transformed-root ignore-tree]
|
||||
(let [children (map (d/getf objects) (:shapes shape))
|
||||
|
||||
shape-id (:id shape)
|
||||
transformed-shape (gsh/transform-shape shape (get modif-tree shape-id))
|
||||
|
||||
[root transformed-root ignore-geometry?]
|
||||
(check-delta shape root transformed-shape transformed-root objects modif-tree)
|
||||
|
||||
ignore-tree (assoc ignore-tree shape-id ignore-geometry?)
|
||||
|
||||
set-child
|
||||
(fn [ignore-tree child]
|
||||
(get-ignore-tree modif-tree objects child root transformed-root ignore-tree))]
|
||||
|
||||
(reduce set-child ignore-tree children))))
|
||||
|
||||
(defn- update-grow-type
|
||||
[shape old-shape]
|
||||
(let [auto-width? (= :auto-width (:grow-type shape))
|
||||
auto-height? (= :auto-height (:grow-type shape))
|
||||
|
||||
changed-width? (not (mth/close? (:width shape) (:width old-shape)))
|
||||
changed-height? (not (mth/close? (:height shape) (:height old-shape)))
|
||||
|
||||
change-to-fixed? (or (and auto-width? (or changed-height? changed-width?))
|
||||
(and auto-height? changed-height?))]
|
||||
(cond-> shape
|
||||
change-to-fixed?
|
||||
(assoc :grow-type :fixed))))
|
||||
|
||||
(defn- clear-local-transform []
|
||||
(ptk/reify ::clear-local-transform
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(dissoc :workspace-modifiers)
|
||||
(dissoc :app.main.data.workspace.transforms/current-move-selected)))))
|
||||
|
||||
(defn create-modif-tree
|
||||
[ids modifiers]
|
||||
(us/verify (s/coll-of uuid?) ids)
|
||||
(into {} (map #(vector % {:modifiers modifiers})) ids))
|
||||
|
||||
(defn build-modif-tree
|
||||
[ids objects get-modifier]
|
||||
(us/verify (s/coll-of uuid?) ids)
|
||||
(into {} (map #(vector % {:modifiers (get-modifier (get objects %))})) ids))
|
||||
|
||||
(defn build-change-frame-modifiers
|
||||
[modif-tree objects selected target-frame drop-index]
|
||||
|
||||
(let [origin-frame-ids (->> selected (group-by #(get-in objects [% :frame-id])))
|
||||
child-set (set (get-in objects [target-frame :shapes]))
|
||||
layout? (ctl/layout? objects target-frame)
|
||||
|
||||
set-parent-ids
|
||||
(fn [modif-tree shapes target-frame]
|
||||
(reduce
|
||||
(fn [modif-tree id]
|
||||
(update-in
|
||||
modif-tree
|
||||
[id :modifiers]
|
||||
#(-> %
|
||||
(ctm/change-property :frame-id target-frame)
|
||||
(ctm/change-property :parent-id target-frame))))
|
||||
modif-tree
|
||||
shapes))
|
||||
|
||||
update-frame-modifiers
|
||||
(fn [modif-tree [original-frame shapes]]
|
||||
(let [shapes (->> shapes (d/removev #(= target-frame %)))
|
||||
shapes (cond->> shapes
|
||||
(and layout? (= original-frame target-frame))
|
||||
;; When movining inside a layout frame remove the shapes that are not immediate children
|
||||
(filterv #(contains? child-set %)))]
|
||||
(cond-> modif-tree
|
||||
(not= original-frame target-frame)
|
||||
(-> (update-in [original-frame :modifiers] ctm/remove-children shapes)
|
||||
(update-in [target-frame :modifiers] ctm/add-children shapes drop-index)
|
||||
(set-parent-ids shapes target-frame))
|
||||
|
||||
(and layout? (= original-frame target-frame))
|
||||
(update-in [target-frame :modifiers] ctm/add-children shapes drop-index))))]
|
||||
|
||||
(reduce update-frame-modifiers modif-tree origin-frame-ids)))
|
||||
|
||||
(defn modif->js
|
||||
[modif-tree objects]
|
||||
(clj->js (into {}
|
||||
(map (fn [[k v]]
|
||||
[(get-in objects [k :name]) v]))
|
||||
modif-tree)))
|
||||
|
||||
(defn set-modifiers
|
||||
([modif-tree]
|
||||
(set-modifiers modif-tree false))
|
||||
|
||||
([modif-tree ignore-constraints]
|
||||
(set-modifiers modif-tree ignore-constraints false))
|
||||
|
||||
([modif-tree ignore-constraints ignore-snap-pixel]
|
||||
(ptk/reify ::set-modifiers
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [objects
|
||||
(wsh/lookup-page-objects state)
|
||||
|
||||
snap-pixel?
|
||||
(and (not ignore-snap-pixel) (contains? (:workspace-layout state) :snap-pixel-grid))
|
||||
|
||||
modif-tree
|
||||
(gsh/set-objects-modifiers modif-tree objects ignore-constraints snap-pixel?)]
|
||||
|
||||
(assoc state :workspace-modifiers modif-tree))))))
|
||||
|
||||
;; Rotation use different algorithm to calculate children modifiers (and do not use child constraints).
|
||||
(defn set-rotation-modifiers
|
||||
([angle shapes]
|
||||
(set-rotation-modifiers angle shapes (-> shapes gsh/selection-rect gsh/center-selrect)))
|
||||
|
||||
([angle shapes center]
|
||||
(ptk/reify ::set-rotation-modifiers
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [objects (wsh/lookup-page-objects state)
|
||||
ids
|
||||
(->> shapes
|
||||
(remove #(get % :blocked false))
|
||||
(filter #((cpc/editable-attrs (:type %)) :rotation))
|
||||
(map :id))
|
||||
|
||||
get-modifier
|
||||
(fn [shape]
|
||||
(ctm/rotation-modifiers shape center angle))
|
||||
|
||||
modif-tree
|
||||
(-> (build-modif-tree ids objects get-modifier)
|
||||
(gsh/set-objects-modifiers objects false false))]
|
||||
|
||||
(assoc state :workspace-modifiers modif-tree))))))
|
||||
|
||||
|
||||
(defn apply-modifiers
|
||||
([]
|
||||
(apply-modifiers nil))
|
||||
|
||||
([{:keys [undo-transation?] :or {undo-transation? true}}]
|
||||
(ptk/reify ::apply-modifiers
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [objects (wsh/lookup-page-objects state)
|
||||
object-modifiers (get state :workspace-modifiers)
|
||||
|
||||
ids (or (keys object-modifiers) [])
|
||||
ids-with-children (into (vec ids) (mapcat #(cph/get-children-ids objects %)) ids)
|
||||
|
||||
shapes (map (d/getf objects) ids)
|
||||
ignore-tree (->> (map #(get-ignore-tree object-modifiers objects %) shapes)
|
||||
(reduce merge {}))]
|
||||
|
||||
(rx/concat
|
||||
(if undo-transation?
|
||||
(rx/of (dwu/start-undo-transaction))
|
||||
(rx/empty))
|
||||
(rx/of (ptk/event ::dwg/move-frame-guides ids-with-children)
|
||||
(ptk/event ::dwcm/move-frame-comment-threads ids-with-children)
|
||||
(dch/update-shapes
|
||||
ids
|
||||
(fn [shape]
|
||||
(let [modif (get-in object-modifiers [(:id shape) :modifiers])
|
||||
text-shape? (cph/text-shape? shape)]
|
||||
(-> shape
|
||||
(gsh/transform-shape modif)
|
||||
(cond-> text-shape?
|
||||
(update-grow-type shape)))))
|
||||
{:reg-objects? true
|
||||
:ignore-tree ignore-tree
|
||||
;; Attributes that can change in the transform. This way we don't have to check
|
||||
;; all the attributes
|
||||
:attrs [:selrect
|
||||
:points
|
||||
:x
|
||||
:y
|
||||
:width
|
||||
:height
|
||||
:content
|
||||
:transform
|
||||
:transform-inverse
|
||||
:rotation
|
||||
:position-data
|
||||
:flip-x
|
||||
:flip-y
|
||||
:grow-type
|
||||
:layout-item-h-sizing
|
||||
:layout-item-v-sizing
|
||||
]})
|
||||
(clear-local-transform))
|
||||
(if undo-transation?
|
||||
(rx/of (dwu/commit-undo-transaction))
|
||||
(rx/empty))))))))
|
|
@ -21,6 +21,7 @@
|
|||
[app.main.data.modal :as md]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.collapse :as dwc]
|
||||
[app.main.data.workspace.shapes-update-layout :as dwul]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.main.data.workspace.thumbnails :as dwt]
|
||||
[app.main.data.workspace.zoom :as dwz]
|
||||
|
@ -552,8 +553,11 @@
|
|||
(filter #(= :frame (get-in % [:obj :type])))
|
||||
(map #(vector (:old-id %) (get-in % [:obj :id]))))
|
||||
|
||||
id-duplicated (first new-selected)]
|
||||
id-duplicated (first new-selected)
|
||||
|
||||
frames (into #{}
|
||||
(map #(get-in objects [% :frame-id]))
|
||||
selected)]
|
||||
(rx/concat
|
||||
(->> (rx/from dup-frames)
|
||||
(rx/map (fn [[old-id new-id]] (dwt/duplicate-thumbnail old-id new-id))))
|
||||
|
@ -561,6 +565,7 @@
|
|||
;; Warning: This order is important for the focus mode.
|
||||
(rx/of (dch/commit-changes changes)
|
||||
(select-shapes new-selected)
|
||||
(dwul/update-layout-positions frames)
|
||||
(memorize-duplicated id-original id-duplicated))))))))))
|
||||
|
||||
(defn change-hover-state
|
||||
|
|
|
@ -8,9 +8,13 @@
|
|||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.workspace.changes :as dwc]
|
||||
[app.main.data.workspace.selection :as dwse]
|
||||
[app.main.data.workspace.shapes :as dws]
|
||||
[app.main.data.workspace.shapes-update-layout :as wsul]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.main.data.workspace.transforms :as dwt]
|
||||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]))
|
||||
|
||||
|
@ -25,8 +29,7 @@
|
|||
:layout-wrap-type
|
||||
:layout-padding-type
|
||||
:layout-padding
|
||||
])
|
||||
|
||||
:layout-gap-type])
|
||||
|
||||
(def initial-flex-layout
|
||||
{:layout :flex
|
||||
|
@ -35,7 +38,7 @@
|
|||
:layout-gap {:row-gap 0 :column-gap 0}
|
||||
:layout-align-items :start
|
||||
:layout-justify-content :start
|
||||
:layout-align-content :strech
|
||||
:layout-align-content :stretch
|
||||
:layout-wrap-type :no-wrap
|
||||
:layout-padding-type :simple
|
||||
:layout-padding {:p1 0 :p2 0 :p3 0 :p4 0}})
|
||||
|
@ -43,30 +46,50 @@
|
|||
(def initial-grid-layout ;; TODO
|
||||
{:layout :grid})
|
||||
|
||||
(defn update-layout-positions
|
||||
[ids]
|
||||
(ptk/reify ::update-layout-positions
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [objects (wsh/lookup-page-objects state)
|
||||
ids (->> ids (filter #(get-in objects [% :layout])))]
|
||||
(if (d/not-empty? ids)
|
||||
(rx/of (dwt/set-modifiers ids)
|
||||
(dwt/apply-modifiers))
|
||||
(rx/empty))))))
|
||||
(defn get-layout-initializer
|
||||
[type]
|
||||
(let [initial-layout-data (if (= type :flex) initial-flex-layout initial-grid-layout)]
|
||||
(fn [shape]
|
||||
(-> shape
|
||||
(merge shape initial-layout-data)))))
|
||||
|
||||
;; TODO: Remove constraints from children
|
||||
(defn create-layout
|
||||
[ids type]
|
||||
(ptk/reify ::create-layout
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(if (= type :flex)
|
||||
(rx/of (dwc/update-shapes ids #(merge % initial-flex-layout))
|
||||
(update-layout-positions ids))
|
||||
(rx/of (dwc/update-shapes ids #(merge % initial-grid-layout))
|
||||
(update-layout-positions ids))))))
|
||||
(watch [_ state _]
|
||||
(let [objects (wsh/lookup-page-objects state)
|
||||
children-ids (into [] (mapcat #(get-in objects [% :shapes])) ids)]
|
||||
(rx/of (dwc/update-shapes ids (get-layout-initializer type))
|
||||
(wsul/update-layout-positions ids)
|
||||
(dwc/update-shapes children-ids #(dissoc % :constraints-h :constraints-v)))))))
|
||||
|
||||
(defn create-layout-from-selection
|
||||
[type]
|
||||
(ptk/reify ::create-layout-from-selection
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
|
||||
(let [page-id (:current-page-id state)
|
||||
objects (wsh/lookup-page-objects state page-id)
|
||||
selected (wsh/lookup-selected state)
|
||||
selected-shapes (map (d/getf objects) selected)
|
||||
single? (= (count selected-shapes) 1)
|
||||
has-group? (->> selected-shapes (d/seek cph/group-shape?))
|
||||
is-group? (and single? has-group?)]
|
||||
(if is-group?
|
||||
(let [new-shape-id (uuid/next)
|
||||
parent-id (:parent-id (first selected-shapes))
|
||||
shapes-ids (:shapes (first selected-shapes))
|
||||
ordered-ids (into (d/ordered-set) shapes-ids)]
|
||||
(rx/of (dwse/select-shapes ordered-ids)
|
||||
(dws/create-artboard-from-selection new-shape-id parent-id)
|
||||
(create-layout [new-shape-id] type)
|
||||
(dws/delete-shapes page-id selected)))
|
||||
|
||||
(let [new-shape-id (uuid/next)]
|
||||
(rx/of (dws/create-artboard-from-selection new-shape-id)
|
||||
(create-layout [new-shape-id] type))))))))
|
||||
|
||||
(defn remove-layout
|
||||
[ids]
|
||||
|
@ -74,7 +97,23 @@
|
|||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(rx/of (dwc/update-shapes ids #(apply dissoc % layout-keys))
|
||||
(update-layout-positions ids)))))
|
||||
(wsul/update-layout-positions ids)))))
|
||||
|
||||
(defn toogle-layout-flex
|
||||
[]
|
||||
(ptk/reify ::toogle-layout-flex
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [page-id (:current-page-id state)
|
||||
objects (wsh/lookup-page-objects state page-id)
|
||||
selected (wsh/lookup-selected state)
|
||||
selected-shapes (map (d/getf objects) selected)
|
||||
single? (= (count selected-shapes) 1)
|
||||
has-flex-layout? (and single? (= :flex (:layout (first selected-shapes))))]
|
||||
|
||||
(if has-flex-layout?
|
||||
(rx/of (remove-layout selected))
|
||||
(rx/of (create-layout-from-selection :flex)))))))
|
||||
|
||||
(defn update-layout
|
||||
[ids changes]
|
||||
|
@ -82,7 +121,7 @@
|
|||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(rx/of (dwc/update-shapes ids #(d/deep-merge % changes))
|
||||
(update-layout-positions ids)))))
|
||||
(wsul/update-layout-positions ids)))))
|
||||
|
||||
(defn update-layout-child
|
||||
[ids changes]
|
||||
|
@ -90,6 +129,7 @@
|
|||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [objects (wsh/lookup-page-objects state)
|
||||
parent-ids (->> ids (map #(cph/get-parent-id objects %)))]
|
||||
parent-ids (->> ids (map #(cph/get-parent-id objects %)))
|
||||
layout-ids (->> ids (filter (comp ctl/layout? (d/getf objects))))]
|
||||
(rx/of (dwc/update-shapes ids #(d/deep-merge (or % {}) changes))
|
||||
(update-layout-positions parent-ids))))))
|
||||
(wsul/update-layout-positions (d/concat-vec layout-ids parent-ids)))))))
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.geom.proportions :as gpr]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.pages.changes-builder :as pcb]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.spec :as us]
|
||||
|
@ -16,13 +17,15 @@
|
|||
[app.common.types.shape :as cts]
|
||||
[app.common.types.shape-tree :as ctst]
|
||||
[app.common.types.shape.interactions :as ctsi]
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.comments :as dc]
|
||||
[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.shapes-update-layout :as dwsul]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.features :as features]
|
||||
[app.main.streams :as ms]
|
||||
[beicon.core :as rx]
|
||||
|
@ -101,7 +104,7 @@
|
|||
|
||||
(rx/concat
|
||||
(rx/of (dch/commit-changes changes)
|
||||
(dwsl/update-layout-positions [(:parent-id shape)])
|
||||
(dwsul/update-layout-positions [(:parent-id shape)])
|
||||
(when-not no-select?
|
||||
(dws/select-shapes (d/ordered-set id))))
|
||||
(when (= :text (:type attrs))
|
||||
|
@ -146,6 +149,10 @@
|
|||
ids (cph/clean-loops objects ids)
|
||||
lookup (d/getf objects)
|
||||
|
||||
layout-ids (->> ids
|
||||
(mapcat (partial cph/get-parent-ids objects))
|
||||
(filter (partial ctl/layout? objects)))
|
||||
|
||||
components-v2 (features/active-feature? state :components-v2)
|
||||
|
||||
groups-to-unmask
|
||||
|
@ -265,8 +272,9 @@
|
|||
(reduce ctp/remove-flow flows))))))]
|
||||
|
||||
(rx/of (dc/detach-comment-thread ids)
|
||||
(dwsl/update-layout-positions all-parents)
|
||||
(dch/commit-changes changes)))))))
|
||||
(dwsul/update-layout-positions all-parents)
|
||||
(dch/commit-changes changes)
|
||||
(dwsul/update-layout-positions layout-ids)))))))
|
||||
|
||||
(defn- viewport-center
|
||||
[state]
|
||||
|
@ -292,3 +300,38 @@
|
|||
(assoc :frame-id frame-id)
|
||||
(cts/setup-rect-selrect))]
|
||||
(rx/of (add-shape shape))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Artboard
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn create-artboard-from-selection
|
||||
([]
|
||||
(create-artboard-from-selection nil))
|
||||
([id]
|
||||
(create-artboard-from-selection id nil))
|
||||
([id parent-id]
|
||||
(ptk/reify ::create-artboard-from-selection
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [page-id (:current-page-id state)
|
||||
objects (wsh/lookup-page-objects state page-id)
|
||||
selected (wsh/lookup-selected state)
|
||||
selected-objs (map #(get objects %) selected)]
|
||||
(when (d/not-empty? selected)
|
||||
(let [srect (gsh/selection-rect selected-objs)
|
||||
frame-id (get-in objects [(first selected) :frame-id])
|
||||
parent-id (or parent-id (get-in objects [(first selected) :parent-id]))
|
||||
shape (-> (cts/make-minimal-shape :frame)
|
||||
(merge {:x (:x srect) :y (:y srect) :width (:width srect) :height (:height srect)})
|
||||
(cond-> id
|
||||
(assoc :id id))
|
||||
(assoc :frame-id frame-id :parent-id parent-id)
|
||||
(cond-> (not= frame-id uuid/zero)
|
||||
(assoc :fills [] :hide-in-viewer true))
|
||||
(cts/setup-rect-selrect))]
|
||||
(rx/of
|
||||
(dwu/start-undo-transaction)
|
||||
(add-shape shape)
|
||||
(move-shapes-into-frame (:id shape) selected)
|
||||
(dwu/commit-undo-transaction)))))))))
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
;; 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.main.data.workspace.shapes-update-layout
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.types.modifiers :as ctm]
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.main.data.workspace.modifiers :as dwm]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]))
|
||||
|
||||
(defn update-layout-positions
|
||||
[ids]
|
||||
(ptk/reify ::update-layout-positions
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [objects (wsh/lookup-page-objects state)
|
||||
ids (->> ids (filter (partial ctl/layout? objects)))]
|
||||
(if (d/not-empty? ids)
|
||||
(let [modif-tree (dwm/create-modif-tree ids (ctm/reflow-modifiers))]
|
||||
(rx/of (dwm/set-modifiers modif-tree)
|
||||
(dwm/apply-modifiers)))
|
||||
(rx/empty))))))
|
|
@ -15,6 +15,8 @@
|
|||
[app.main.data.workspace.drawing :as dwd]
|
||||
[app.main.data.workspace.layers :as dwly]
|
||||
[app.main.data.workspace.libraries :as dwl]
|
||||
[app.main.data.workspace.shape-layout :as dwsl]
|
||||
[app.main.data.workspace.shapes :as dws]
|
||||
[app.main.data.workspace.texts :as dwtxt]
|
||||
[app.main.data.workspace.transforms :as dwt]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
|
@ -204,7 +206,12 @@
|
|||
:artboard-selection {:tooltip (ds/meta (ds/alt "G"))
|
||||
:command (ds/c-mod "alt+g")
|
||||
:subsections [:modify-layers]
|
||||
:fn #(st/emit! (dw/create-artboard-from-selection))}
|
||||
:fn #(st/emit! (dws/create-artboard-from-selection))}
|
||||
|
||||
:toogle-layout-flex {:tooltip (ds/shift "F")
|
||||
:command "shift+f"
|
||||
:subsections [:modify-layers]
|
||||
:fn #(st/emit! (dwsl/toogle-layout-flex))}
|
||||
|
||||
;; TOOLS
|
||||
|
||||
|
@ -382,7 +389,7 @@
|
|||
:show-shortcuts {:tooltip "?"
|
||||
:command "?"
|
||||
:subsections [:main-menu]
|
||||
:fn #(st/emit! (toggle-layout-flag :shortcuts)) }
|
||||
:fn #(st/emit! (toggle-layout-flag :shortcuts))}
|
||||
|
||||
;; PANELS
|
||||
|
||||
|
|
|
@ -127,15 +127,15 @@
|
|||
(defn select-bool-children
|
||||
[parent-id state]
|
||||
(let [objects (lookup-page-objects state)
|
||||
selected (lookup-selected-raw state)
|
||||
modifiers (:workspace-modifiers state)
|
||||
|
||||
children-ids (cph/get-children-ids objects parent-id)
|
||||
selected-children (into [] (filter selected) children-ids)
|
||||
|
||||
modifiers (select-keys modifiers selected-children)
|
||||
children (select-keys objects children-ids)]
|
||||
children
|
||||
(-> (select-keys objects children-ids)
|
||||
(d/update-vals
|
||||
(fn [child]
|
||||
(cond-> child
|
||||
(contains? modifiers (:id child))
|
||||
(gsh/transform-shape (get-in modifiers [(:id child) :modifiers]))))))]
|
||||
|
||||
(as-> children $
|
||||
(gsh/merge-modifiers $ modifiers)
|
||||
(d/mapm (set-content-modifiers state) $))))
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
[app.common.math :as mth]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.text :as txt]
|
||||
[app.common.types.modifiers :as ctm]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.common :as dwc]
|
||||
|
@ -318,18 +319,14 @@
|
|||
(watch [_ _ _]
|
||||
(letfn [(update-fn [shape]
|
||||
(let [{:keys [selrect grow-type]} shape
|
||||
{shape-width :width shape-height :height} selrect
|
||||
modifier-width (gsh/resize-modifiers shape :width new-width)
|
||||
modifier-height (gsh/resize-modifiers shape :height new-height)]
|
||||
{shape-width :width shape-height :height} selrect]
|
||||
(cond-> shape
|
||||
(and (not-changed? shape-width new-width) (= grow-type :auto-width))
|
||||
(-> (assoc :modifiers modifier-width)
|
||||
(gsh/transform-shape))
|
||||
(gsh/transform-shape (ctm/change-dimensions-modifiers shape :width new-width))
|
||||
|
||||
(and (not-changed? shape-height new-height)
|
||||
(or (= grow-type :auto-height) (= grow-type :auto-width)))
|
||||
(-> (assoc :modifiers modifier-height)
|
||||
(gsh/transform-shape)))))]
|
||||
(gsh/transform-shape (ctm/change-dimensions-modifiers shape :height new-height)))))]
|
||||
|
||||
(rx/of (dch/update-shapes [id] update-fn {:reg-objects? true :save-undo? false}))))))
|
||||
|
||||
|
@ -346,18 +343,13 @@
|
|||
(defn apply-text-modifier
|
||||
[shape {:keys [width height position-data]}]
|
||||
|
||||
(let [modifier-width (when width (gsh/resize-modifiers shape :width width))
|
||||
modifier-height (when height (gsh/resize-modifiers shape :height height))
|
||||
|
||||
new-shape
|
||||
(let [new-shape
|
||||
(cond-> shape
|
||||
(some? modifier-width)
|
||||
(-> (assoc :modifiers modifier-width)
|
||||
(gsh/transform-shape))
|
||||
(some? width)
|
||||
(gsh/transform-shape (ctm/change-dimensions-modifiers shape :width width))
|
||||
|
||||
(some? modifier-height)
|
||||
(-> (assoc :modifiers modifier-height)
|
||||
(gsh/transform-shape))
|
||||
(some? height)
|
||||
(gsh/transform-shape (ctm/change-dimensions-modifiers shape :height height))
|
||||
|
||||
(some? position-data)
|
||||
(assoc :position-data position-data))
|
||||
|
@ -402,7 +394,7 @@
|
|||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [ids (keys (::update-position-data state))]
|
||||
(update state :workspace-text-modifiers #(apply dissoc % ids))))
|
||||
(update state :workspace-text-modifier #(apply dissoc % ids))))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
|
|
|
@ -11,16 +11,17 @@
|
|||
[app.common.geom.matrix :as gmt]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.geom.shapes.flex-layout :as gsl]
|
||||
[app.common.math :as mth]
|
||||
[app.common.pages.changes-builder :as pcb]
|
||||
[app.common.pages.common :as cpc]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.spec :as us]
|
||||
[app.common.types.modifiers :as ctm]
|
||||
[app.common.types.shape-tree :as ctst]
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.collapse :as dwc]
|
||||
[app.main.data.workspace.comments :as-alias dwcm]
|
||||
[app.main.data.workspace.guides :as-alias dwg]
|
||||
[app.main.data.workspace.modifiers :as dwm]
|
||||
[app.main.data.workspace.selection :as dws]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
|
@ -95,233 +96,12 @@
|
|||
(update state :workspace-local dissoc :transform))))
|
||||
|
||||
|
||||
;; -- Temporary modifiers -------------------------------------------
|
||||
|
||||
;; During an interactive transformation of shapes (e.g. when resizing or rotating
|
||||
;; a group with the mouse), there are a lot of objects that need to be modified
|
||||
;; (in this case, the group and all its children).
|
||||
;;
|
||||
;; To avoid updating the shapes theirselves, and forcing redraw of all components
|
||||
;; that depend on the "objects" global state, we set a "modifiers" structure, with
|
||||
;; the changes that need to be applied, and store it in :workspace-modifiers global
|
||||
;; variable. The viewport reads this and merges it into the objects list it uses to
|
||||
;; paint the viewport content, redrawing only the objects that have new modifiers.
|
||||
;;
|
||||
;; When the interaction is finished (e.g. user releases mouse button), the
|
||||
;; apply-modifiers event is done, that consolidates all modifiers into the base
|
||||
;; geometric attributes of the shapes.
|
||||
|
||||
(declare clear-local-transform)
|
||||
|
||||
(declare get-ignore-tree)
|
||||
|
||||
(defn set-modifiers
|
||||
([ids]
|
||||
(set-modifiers ids nil false))
|
||||
|
||||
([ids modifiers]
|
||||
(set-modifiers ids modifiers false))
|
||||
|
||||
([ids modifiers ignore-constraints]
|
||||
(set-modifiers ids modifiers ignore-constraints false))
|
||||
|
||||
([ids modifiers ignore-constraints ignore-snap-pixel]
|
||||
(us/verify (s/coll-of uuid?) ids)
|
||||
(ptk/reify ::set-modifiers
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [objects (wsh/lookup-page-objects state)
|
||||
ids (into #{} (remove #(get-in objects [% :blocked] false)) ids)
|
||||
|
||||
snap-pixel? (and (not ignore-snap-pixel)
|
||||
(contains? (:workspace-layout state) :snap-pixel-grid))
|
||||
|
||||
modif-tree
|
||||
(gsh/set-objects-modifiers ids objects (constantly modifiers) ignore-constraints snap-pixel?)]
|
||||
|
||||
(update state :workspace-modifiers merge modif-tree))))))
|
||||
|
||||
;; Rotation use different algorithm to calculate children modifiers (and do not use child constraints).
|
||||
(defn- set-rotation-modifiers
|
||||
([angle shapes]
|
||||
(set-rotation-modifiers angle shapes (-> shapes gsh/selection-rect gsh/center-selrect)))
|
||||
|
||||
([angle shapes center]
|
||||
(ptk/reify ::set-rotation-modifiers
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [objects (wsh/lookup-page-objects state)
|
||||
ids
|
||||
(->> shapes
|
||||
(remove #(get % :blocked false))
|
||||
(mapcat #(cph/get-children objects (:id %)))
|
||||
(concat shapes)
|
||||
(filter #((cpc/editable-attrs (:type %)) :rotation))
|
||||
(map :id))
|
||||
|
||||
get-modifier
|
||||
(fn [shape]
|
||||
(gsh/rotation-modifiers shape center angle))
|
||||
|
||||
modif-tree
|
||||
(gsh/set-objects-modifiers ids objects get-modifier false false)]
|
||||
|
||||
(update state :workspace-modifiers merge modif-tree))))))
|
||||
|
||||
(defn- update-grow-type
|
||||
[shape old-shape]
|
||||
(let [auto-width? (= :auto-width (:grow-type shape))
|
||||
auto-height? (= :auto-height (:grow-type shape))
|
||||
|
||||
changed-width? (not (mth/close? (:width shape) (:width old-shape)))
|
||||
changed-height? (not (mth/close? (:height shape) (:height old-shape)))
|
||||
|
||||
change-to-fixed? (or (and auto-width? (or changed-height? changed-width?))
|
||||
(and auto-height? changed-height?))]
|
||||
(cond-> shape
|
||||
change-to-fixed?
|
||||
(assoc :grow-type :fixed))))
|
||||
|
||||
(defn apply-modifiers
|
||||
([]
|
||||
(apply-modifiers nil))
|
||||
|
||||
([{:keys [undo-transation?] :or {undo-transation? true}}]
|
||||
(ptk/reify ::apply-modifiers
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [objects (wsh/lookup-page-objects state)
|
||||
object-modifiers (get state :workspace-modifiers)
|
||||
|
||||
ids (keys object-modifiers)
|
||||
ids-with-children (into (vec ids) (mapcat #(cph/get-children-ids objects %)) ids)
|
||||
|
||||
shapes (map (d/getf objects) ids)
|
||||
ignore-tree (->> (map #(get-ignore-tree object-modifiers objects %) shapes)
|
||||
(reduce merge {}))]
|
||||
|
||||
(rx/concat
|
||||
(if undo-transation?
|
||||
(rx/of (dwu/start-undo-transaction))
|
||||
(rx/empty))
|
||||
(rx/of (ptk/event ::dwg/move-frame-guides ids-with-children)
|
||||
(ptk/event ::dwcm/move-frame-comment-threads ids-with-children)
|
||||
(dch/update-shapes
|
||||
ids
|
||||
(fn [shape]
|
||||
(let [modif (get object-modifiers (:id shape))
|
||||
text-shape? (cph/text-shape? shape)]
|
||||
(-> shape
|
||||
(merge modif)
|
||||
(gsh/transform-shape)
|
||||
(cond-> text-shape?
|
||||
(update-grow-type shape)))))
|
||||
{:reg-objects? true
|
||||
:ignore-tree ignore-tree
|
||||
;; Attributes that can change in the transform. This way we don't have to check
|
||||
;; all the attributes
|
||||
:attrs [:selrect
|
||||
:points
|
||||
:x
|
||||
:y
|
||||
:width
|
||||
:height
|
||||
:content
|
||||
:transform
|
||||
:transform-inverse
|
||||
:rotation
|
||||
:position-data
|
||||
:flip-x
|
||||
:flip-y
|
||||
:grow-type]})
|
||||
(clear-local-transform))
|
||||
(if undo-transation?
|
||||
(rx/of (dwu/commit-undo-transaction))
|
||||
(rx/empty))))))))
|
||||
|
||||
(defn- check-delta
|
||||
"If the shape is a component instance, check its relative position respect the
|
||||
root of the component, and see if it changes after applying a transformation."
|
||||
[shape root transformed-shape transformed-root objects modif-tree]
|
||||
(let [root
|
||||
(cond
|
||||
(:component-root? shape)
|
||||
shape
|
||||
|
||||
(nil? root)
|
||||
(cph/get-root-shape objects shape)
|
||||
|
||||
:else root)
|
||||
|
||||
transformed-root
|
||||
(cond
|
||||
(:component-root? transformed-shape)
|
||||
transformed-shape
|
||||
|
||||
(nil? transformed-root)
|
||||
(as-> (cph/get-root-shape objects transformed-shape) $
|
||||
(gsh/transform-shape (merge $ (get modif-tree (:id $)))))
|
||||
|
||||
:else transformed-root)
|
||||
|
||||
shape-delta
|
||||
(when root
|
||||
(gpt/point (- (gsh/left-bound shape) (gsh/left-bound root))
|
||||
(- (gsh/top-bound shape) (gsh/top-bound root))))
|
||||
|
||||
transformed-shape-delta
|
||||
(when transformed-root
|
||||
(gpt/point (- (gsh/left-bound transformed-shape) (gsh/left-bound transformed-root))
|
||||
(- (gsh/top-bound transformed-shape) (gsh/top-bound transformed-root))))
|
||||
|
||||
;; There are cases in that the coordinates change slightly (e.g. when
|
||||
;; rounding to pixel, or when recalculating text positions in different
|
||||
;; zoom levels). To take this into account, we ignore movements smaller
|
||||
;; than 1 pixel.
|
||||
distance (if (and shape-delta transformed-shape-delta)
|
||||
(gpt/distance-vector shape-delta transformed-shape-delta)
|
||||
(gpt/point 0 0))
|
||||
|
||||
ignore-geometry? (and (< (:x distance) 1) (< (:y distance) 1))]
|
||||
|
||||
[root transformed-root ignore-geometry?]))
|
||||
|
||||
(defn- get-ignore-tree
|
||||
"Retrieves a map with the flag `ignore-geometry?` given a tree of modifiers"
|
||||
([modif-tree objects shape]
|
||||
(get-ignore-tree modif-tree objects shape nil nil {}))
|
||||
|
||||
([modif-tree objects shape root transformed-root ignore-tree]
|
||||
(let [children (map (d/getf objects) (:shapes shape))
|
||||
|
||||
shape-id (:id shape)
|
||||
transformed-shape (gsh/transform-shape (merge shape (get modif-tree shape-id)))
|
||||
|
||||
[root transformed-root ignore-geometry?]
|
||||
(check-delta shape root transformed-shape transformed-root objects modif-tree)
|
||||
|
||||
ignore-tree (assoc ignore-tree shape-id ignore-geometry?)
|
||||
|
||||
set-child
|
||||
(fn [ignore-tree child]
|
||||
(get-ignore-tree modif-tree objects child root transformed-root ignore-tree))]
|
||||
|
||||
(reduce set-child ignore-tree children))))
|
||||
|
||||
(defn- clear-local-transform []
|
||||
(ptk/reify ::clear-local-transform
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(dissoc :workspace-modifiers)
|
||||
(dissoc ::current-move-selected)))))
|
||||
|
||||
;; -- Resize --------------------------------------------------------
|
||||
|
||||
(defn start-resize
|
||||
"Enter mouse resize mode, until mouse button is released."
|
||||
[handler ids shape]
|
||||
(letfn [(resize [shape initial layout [point lock? center? point-snap]]
|
||||
(letfn [(resize [shape objects initial layout [point lock? center? point-snap]]
|
||||
(let [{:keys [width height]} (:selrect shape)
|
||||
{:keys [rotation]} shape
|
||||
|
||||
|
@ -331,10 +111,10 @@
|
|||
|
||||
rotation (or rotation 0)
|
||||
|
||||
initial (gsh/transform-point-center initial shape-center shape-transform-inverse)
|
||||
initial (gmt/transform-point-center initial shape-center shape-transform-inverse)
|
||||
initial (fix-init-point initial handler shape)
|
||||
|
||||
point (gsh/transform-point-center (if (= rotation 0) point-snap point)
|
||||
point (gmt/transform-point-center (if (= rotation 0) point-snap point)
|
||||
shape-center shape-transform-inverse)
|
||||
|
||||
shapev (-> (gpt/point width height))
|
||||
|
@ -381,20 +161,46 @@
|
|||
(gpt/transform shape-transform)))
|
||||
|
||||
resize-origin
|
||||
(cond-> (gsh/transform-point-center handler-origin shape-center shape-transform)
|
||||
(cond-> (gmt/transform-point-center handler-origin shape-center shape-transform)
|
||||
(some? displacement)
|
||||
(gpt/add displacement))
|
||||
|
||||
displacement (when (some? displacement)
|
||||
(gmt/translate-matrix displacement))]
|
||||
;; When the horizontal/vertical scale a flex children with auto/fill
|
||||
;; we change it too fixed
|
||||
layout? (ctl/layout? shape)
|
||||
layout-child? (ctl/layout-child? objects shape)
|
||||
auto-width? (ctl/auto-width? shape)
|
||||
fill-width? (ctl/fill-width? shape)
|
||||
auto-height? (ctl/auto-height? shape)
|
||||
fill-height? (ctl/fill-height? shape)
|
||||
|
||||
(rx/of (set-modifiers ids
|
||||
{:displacement displacement
|
||||
:resize-vector scalev
|
||||
:resize-origin resize-origin
|
||||
:resize-transform shape-transform
|
||||
:resize-scale-text scale-text
|
||||
:resize-transform-inverse shape-transform-inverse}))))
|
||||
set-fix-width?
|
||||
(and (not (mth/close? (:x scalev) 1))
|
||||
(or (and (or layout? layout-child?) auto-width?)
|
||||
(and layout-child? fill-width?)))
|
||||
|
||||
set-fix-height?
|
||||
(and (not (mth/close? (:y scalev) 1))
|
||||
(or (and (or layout? layout-child?) auto-height?)
|
||||
(and layout-child? fill-height?)))
|
||||
|
||||
modifiers
|
||||
(-> (ctm/empty)
|
||||
(cond-> displacement
|
||||
(ctm/move displacement))
|
||||
(ctm/resize scalev resize-origin shape-transform shape-transform-inverse)
|
||||
|
||||
(cond-> set-fix-width?
|
||||
(ctm/change-property :layout-item-h-sizing :fix))
|
||||
|
||||
(cond-> set-fix-height?
|
||||
(ctm/change-property :layout-item-v-sizing :fix))
|
||||
|
||||
(cond-> scale-text
|
||||
(ctm/scale-content (:x scalev))))
|
||||
|
||||
modif-tree (dwm/create-modif-tree ids modifiers)]
|
||||
(rx/of (dwm/set-modifiers modif-tree))))
|
||||
|
||||
;; Unifies the instantaneous proportion lock modifier
|
||||
;; activated by Shift key and the shapes own proportion
|
||||
|
@ -418,6 +224,7 @@
|
|||
zoom (get-in state [:workspace-local :zoom] 1)
|
||||
objects (wsh/lookup-page-objects state page-id)
|
||||
resizing-shapes (map #(get objects %) ids)]
|
||||
|
||||
(rx/concat
|
||||
(->> ms/mouse-position
|
||||
(rx/with-latest-from ms/mouse-position-shift ms/mouse-position-alt)
|
||||
|
@ -425,9 +232,9 @@
|
|||
(rx/switch-map (fn [[point _ _ :as current]]
|
||||
(->> (snap/closest-snap-point page-id resizing-shapes objects layout zoom focus point)
|
||||
(rx/map #(conj current %)))))
|
||||
(rx/mapcat (partial resize shape initial-position layout))
|
||||
(rx/mapcat (partial resize shape objects initial-position layout))
|
||||
(rx/take-until stoper))
|
||||
(rx/of (apply-modifiers)
|
||||
(rx/of (dwm/apply-modifiers)
|
||||
(finish-transform))))))))
|
||||
|
||||
(defn update-dimensions
|
||||
|
@ -444,16 +251,17 @@
|
|||
snap-pixel? (and (contains? (:workspace-layout state) :snap-pixel-grid)
|
||||
(int? value))
|
||||
get-modifier
|
||||
(fn [shape] (gsh/resize-modifiers shape attr value))
|
||||
(fn [shape] (ctm/change-dimensions-modifiers shape attr value))
|
||||
|
||||
modif-tree
|
||||
(gsh/set-objects-modifiers ids objects get-modifier false snap-pixel?)]
|
||||
(-> (dwm/build-modif-tree ids objects get-modifier)
|
||||
(gsh/set-objects-modifiers objects false snap-pixel?))]
|
||||
|
||||
(assoc state :workspace-modifiers modif-tree)))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(rx/of (apply-modifiers)))))
|
||||
(rx/of (dwm/apply-modifiers)))))
|
||||
|
||||
(defn change-orientation
|
||||
"Change orientation of shapes, from the sidebar options form.
|
||||
|
@ -468,16 +276,17 @@
|
|||
snap-pixel? (contains? (get state :workspace-layout) :snap-pixel-grid)
|
||||
|
||||
get-modifier
|
||||
(fn [shape] (gsh/change-orientation-modifiers shape orientation))
|
||||
(fn [shape] (ctm/change-orientation-modifiers shape orientation))
|
||||
|
||||
modif-tree
|
||||
(gsh/set-objects-modifiers ids objects get-modifier false snap-pixel?)]
|
||||
(-> (dwm/build-modif-tree ids objects get-modifier)
|
||||
(gsh/set-objects-modifiers objects false snap-pixel?))]
|
||||
|
||||
(assoc state :workspace-modifiers modif-tree)))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(rx/of (apply-modifiers)))))
|
||||
(rx/of (dwm/apply-modifiers)))))
|
||||
|
||||
;; -- Rotate --------------------------------------------------------
|
||||
|
||||
|
@ -518,9 +327,9 @@
|
|||
(rx/map
|
||||
(fn [[[pos mod?] shift?]]
|
||||
(let [delta-angle (calculate-angle pos mod? shift?)]
|
||||
(set-rotation-modifiers delta-angle shapes group-center))))
|
||||
(dwm/set-rotation-modifiers delta-angle shapes group-center))))
|
||||
(rx/take-until stoper))
|
||||
(rx/of (apply-modifiers)
|
||||
(rx/of (dwm/apply-modifiers)
|
||||
(finish-transform)))))))
|
||||
|
||||
(defn increase-rotation
|
||||
|
@ -534,17 +343,17 @@
|
|||
objects (wsh/lookup-page-objects state page-id)
|
||||
rotate-shape (fn [shape]
|
||||
(let [delta (- rotation (:rotation shape))]
|
||||
(set-rotation-modifiers delta [shape])))]
|
||||
(dwm/set-rotation-modifiers delta [shape])))]
|
||||
(rx/concat
|
||||
(rx/from (->> ids (map #(get objects %)) (map rotate-shape)))
|
||||
(rx/of (apply-modifiers)))))))
|
||||
(rx/of (dwm/apply-modifiers)))))))
|
||||
|
||||
|
||||
;; -- Move ----------------------------------------------------------
|
||||
|
||||
(declare start-move)
|
||||
(declare start-move-duplicate)
|
||||
(declare calculate-frame-for-move)
|
||||
(declare move-shapes-to-frame)
|
||||
(declare get-displacement)
|
||||
|
||||
(defn start-move-selected
|
||||
|
@ -626,13 +435,18 @@
|
|||
zoom (get-in state [:workspace-local :zoom] 1)
|
||||
focus (:workspace-focus-selected state)
|
||||
|
||||
fix-axis (fn [[position shift?]]
|
||||
(let [delta (gpt/to-vec from-position position)]
|
||||
(if shift?
|
||||
(if (> (mth/abs (:x delta)) (mth/abs (:y delta)))
|
||||
(gpt/point (:x delta) 0)
|
||||
(gpt/point 0 (:y delta)))
|
||||
delta)))
|
||||
exclude-frames (into #{}
|
||||
(filter (partial cph/frame-shape? objects))
|
||||
(cph/selected-with-children objects selected))
|
||||
|
||||
fix-axis
|
||||
(fn [[position shift?]]
|
||||
(let [delta (gpt/to-vec from-position position)]
|
||||
(if shift?
|
||||
(if (> (mth/abs (:x delta)) (mth/abs (:y delta)))
|
||||
(gpt/point (:x delta) 0)
|
||||
(gpt/point 0 (:y delta)))
|
||||
delta)))
|
||||
|
||||
position (->> ms/mouse-position
|
||||
(rx/with-latest-from ms/mouse-position-shift)
|
||||
|
@ -649,21 +463,43 @@
|
|||
(rx/map #(vector pos %)))))))]
|
||||
(if (empty? shapes)
|
||||
(rx/of (finish-transform))
|
||||
(rx/concat
|
||||
(->> position
|
||||
;; We ask for the snap position but we continue even if the result is not available
|
||||
(rx/with-latest vector snap-delta)
|
||||
;; We try to use the previous snap so we don't have to wait for the result of the new
|
||||
(rx/map snap/correct-snap-point)
|
||||
(rx/map #(hash-map :displacement (gmt/translate-matrix %)))
|
||||
(rx/map (partial set-modifiers ids))
|
||||
(rx/take-until stopper))
|
||||
(let [move-stream
|
||||
(->> position
|
||||
;; We ask for the snap position but we continue even if the result is not available
|
||||
(rx/with-latest vector snap-delta)
|
||||
|
||||
(rx/of (dwu/start-undo-transaction)
|
||||
(calculate-frame-for-move ids)
|
||||
(apply-modifiers {:undo-transation? false})
|
||||
(finish-transform)
|
||||
(dwu/commit-undo-transaction)))))))))
|
||||
;; We try to use the previous snap so we don't have to wait for the result of the new
|
||||
(rx/map snap/correct-snap-point)
|
||||
|
||||
(rx/map
|
||||
(fn [move-vector]
|
||||
(let [position (gpt/add from-position move-vector)
|
||||
target-frame (ctst/top-nested-frame objects position exclude-frames)
|
||||
layout? (ctl/layout? objects target-frame)
|
||||
drop-index (when layout? (gsl/get-drop-index target-frame objects position))]
|
||||
[move-vector target-frame drop-index])))
|
||||
|
||||
(rx/take-until stopper))]
|
||||
|
||||
(rx/merge
|
||||
;; Temporary modifiers stream
|
||||
(->> move-stream
|
||||
(rx/map
|
||||
(fn [[move-vector target-frame drop-index]]
|
||||
(-> (dwm/create-modif-tree ids (ctm/move-modifiers move-vector))
|
||||
(dwm/build-change-frame-modifiers objects selected target-frame drop-index)
|
||||
(dwm/set-modifiers)))))
|
||||
|
||||
;; Last event will write the modifiers creating the changes
|
||||
(->> move-stream
|
||||
(rx/last)
|
||||
(rx/mapcat
|
||||
(fn [[_ target-frame drop-index]]
|
||||
(rx/of (dwu/start-undo-transaction)
|
||||
(move-shapes-to-frame ids target-frame drop-index)
|
||||
(dwm/apply-modifiers {:undo-transation? false})
|
||||
(finish-transform)
|
||||
(dwu/commit-undo-transaction)))))))))))))
|
||||
|
||||
(s/def ::direction #{:up :down :right :left})
|
||||
|
||||
|
@ -704,12 +540,12 @@
|
|||
(rx/merge
|
||||
(->> move-events
|
||||
(rx/scan #(gpt/add %1 mov-vec) (gpt/point 0 0))
|
||||
(rx/map #(hash-map :displacement (gmt/translate-matrix %)))
|
||||
(rx/map (partial set-modifiers selected))
|
||||
(rx/map #(dwm/create-modif-tree selected (ctm/move-modifiers %)))
|
||||
(rx/map (partial dwm/set-modifiers))
|
||||
(rx/take-until stopper))
|
||||
(rx/of (move-selected direction shift?)))
|
||||
|
||||
(rx/of (apply-modifiers)
|
||||
(rx/of (dwm/apply-modifiers)
|
||||
(finish-transform))))
|
||||
(rx/empty))))))
|
||||
|
||||
|
@ -736,34 +572,39 @@
|
|||
pos (gpt/point (or (:x position) (:x bbox))
|
||||
(or (:y position) (:y bbox)))
|
||||
delta (gpt/subtract pos cpos)
|
||||
displ (gmt/translate-matrix delta)]
|
||||
|
||||
(rx/of (set-modifiers [id] {:displacement displ} false true)
|
||||
(apply-modifiers [id]))))))
|
||||
modif-tree (dwm/create-modif-tree [id] (ctm/move-modifiers delta))]
|
||||
|
||||
(defn- calculate-frame-for-move
|
||||
[ids]
|
||||
(ptk/reify ::calculate-frame-for-move
|
||||
(rx/of (dwm/set-modifiers modif-tree)
|
||||
(dwm/apply-modifiers))))))
|
||||
|
||||
(defn- move-shapes-to-frame
|
||||
[ids frame-id drop-index]
|
||||
(ptk/reify ::move-shapes-to-frame
|
||||
ptk/WatchEvent
|
||||
(watch [it state _]
|
||||
(let [position @ms/mouse-position
|
||||
page-id (:current-page-id state)
|
||||
(let [page-id (:current-page-id state)
|
||||
objects (wsh/lookup-page-objects state page-id)
|
||||
frame-id (ctst/top-nested-frame objects position)
|
||||
layout? (get-in objects [frame-id :layout])
|
||||
lookup (d/getf objects)
|
||||
|
||||
shapes (->> ids (cph/clean-loops objects) (keep lookup))
|
||||
|
||||
moving-shapes
|
||||
(->> ids
|
||||
(cph/clean-loops objects)
|
||||
(keep lookup)
|
||||
(remove #(= (:frame-id %) frame-id)))
|
||||
(cond->> shapes
|
||||
(not layout?)
|
||||
(remove #(= (:frame-id %) frame-id))
|
||||
|
||||
layout?
|
||||
(remove #(and (= (:frame-id %) frame-id)
|
||||
(not= (:parent-id %) frame-id))))
|
||||
|
||||
changes
|
||||
(-> (pcb/empty-changes it page-id)
|
||||
(pcb/with-objects objects)
|
||||
(pcb/change-parent frame-id moving-shapes))]
|
||||
(pcb/change-parent frame-id moving-shapes drop-index))]
|
||||
|
||||
(when-not (empty? changes)
|
||||
(when (and (some? frame-id) (d/not-empty? changes))
|
||||
(rx/of (dch/commit-changes changes)
|
||||
(dwc/expand-collapse frame-id)))))))
|
||||
|
||||
|
@ -787,15 +628,17 @@
|
|||
(let [objects (wsh/lookup-page-objects state)
|
||||
selected (wsh/lookup-selected state {:omit-blocked? true})
|
||||
shapes (map #(get objects %) selected)
|
||||
selrect (gsh/selection-rect (->> shapes (map gsh/transform-shape)))
|
||||
origin (gpt/point (:x selrect) (+ (:y selrect) (/ (:height selrect) 2)))]
|
||||
selrect (gsh/selection-rect shapes)
|
||||
origin (gpt/point (:x selrect) (+ (:y selrect) (/ (:height selrect) 2)))
|
||||
|
||||
(rx/of (set-modifiers selected
|
||||
{:resize-vector (gpt/point -1.0 1.0)
|
||||
:resize-origin origin
|
||||
:displacement (gmt/translate-matrix (gpt/point (- (:width selrect)) 0))}
|
||||
true)
|
||||
(apply-modifiers))))))
|
||||
modif-tree (dwm/create-modif-tree
|
||||
selected
|
||||
(-> (ctm/empty)
|
||||
(ctm/resize (gpt/point -1.0 1.0) origin)
|
||||
(ctm/move (gpt/point (:width selrect) 0))))]
|
||||
|
||||
(rx/of (dwm/set-modifiers modif-tree true)
|
||||
(dwm/apply-modifiers))))))
|
||||
|
||||
(defn flip-vertical-selected []
|
||||
(ptk/reify ::flip-vertical-selected
|
||||
|
@ -804,12 +647,14 @@
|
|||
(let [objects (wsh/lookup-page-objects state)
|
||||
selected (wsh/lookup-selected state {:omit-blocked? true})
|
||||
shapes (map #(get objects %) selected)
|
||||
selrect (gsh/selection-rect (->> shapes (map gsh/transform-shape)))
|
||||
origin (gpt/point (+ (:x selrect) (/ (:width selrect) 2)) (:y selrect))]
|
||||
selrect (gsh/selection-rect shapes)
|
||||
origin (gpt/point (+ (:x selrect) (/ (:width selrect) 2)) (:y selrect))
|
||||
|
||||
(rx/of (set-modifiers selected
|
||||
{:resize-vector (gpt/point 1.0 -1.0)
|
||||
:resize-origin origin
|
||||
:displacement (gmt/translate-matrix (gpt/point 0 (- (:height selrect))))}
|
||||
true)
|
||||
(apply-modifiers))))))
|
||||
modif-tree (dwm/create-modif-tree
|
||||
selected
|
||||
(-> (ctm/empty)
|
||||
(ctm/resize (gpt/point 1.0 -1.0) origin)
|
||||
(ctm/move (gpt/point 0 (:height selrect)))))]
|
||||
|
||||
(rx/of (dwm/set-modifiers modif-tree true)
|
||||
(dwm/apply-modifiers))))))
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
[app.common.data.macros :as dm]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.types.shape-tree :as ctt]
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.main.store :as st]
|
||||
[okulary.core :as l]))
|
||||
|
@ -265,6 +266,14 @@
|
|||
[ids]
|
||||
(l/derived #(into [] (keep (d/getf %)) ids) workspace-page-objects =))
|
||||
|
||||
(defn parents-by-ids
|
||||
[ids]
|
||||
(l/derived
|
||||
(fn [objects]
|
||||
(let [parent-ids (into #{} (keep #(get-in objects [% :parent-id])) ids)]
|
||||
(into [] (keep #(get objects %)) parent-ids)))
|
||||
workspace-page-objects =))
|
||||
|
||||
(defn children-objects
|
||||
[id]
|
||||
(l/derived
|
||||
|
@ -434,7 +443,8 @@
|
|||
(l/derived
|
||||
(fn [objects]
|
||||
(->> ids
|
||||
(some #(-> (cph/get-parent objects %) :layout))))
|
||||
(map (d/getf objects))
|
||||
(some (partial ctl/layout-child? objects))))
|
||||
workspace-page-objects))
|
||||
|
||||
(defn get-flex-child-viewer?
|
||||
|
@ -442,7 +452,10 @@
|
|||
(l/derived
|
||||
(fn [state]
|
||||
(let [objects (wsh/lookup-viewer-objects state page-id)]
|
||||
(filterv #(= :flex (:layout (cph/get-parent objects %))) ids)))
|
||||
(into []
|
||||
(comp (filter (partial ctl/layout-child? objects))
|
||||
(map (d/getf objects)))
|
||||
ids)))
|
||||
st/state =))
|
||||
|
||||
(def colorpicker
|
||||
|
|
|
@ -15,12 +15,12 @@
|
|||
["react-dom/server" :as rds]
|
||||
[app.common.colors :as clr]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.geom.matrix :as gmt]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.geom.shapes.bounds :as gsb]
|
||||
[app.common.math :as mth]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.types.modifiers :as ctm]
|
||||
[app.common.types.shape-tree :as ctst]
|
||||
[app.config :as cfg]
|
||||
[app.main.fonts :as fonts]
|
||||
|
@ -81,8 +81,7 @@
|
|||
[{:keys [shape] :as props}]
|
||||
|
||||
(let [render-thumbnails? (mf/use-ctx muc/render-thumbnails)
|
||||
childs (mapv #(get objects %) (:shapes shape))
|
||||
shape (gsh/transform-shape shape)]
|
||||
childs (mapv #(get objects %) (:shapes shape))]
|
||||
(if (and render-thumbnails? (some? (:thumbnail shape)))
|
||||
[:& frame/frame-thumbnail {:shape shape :bounds (:children-bounds shape)}]
|
||||
[:& frame-shape {:shape shape :childs childs}])))))
|
||||
|
@ -135,8 +134,7 @@
|
|||
bool-wrapper (mf/use-memo (mf/deps objects) #(bool-wrapper-factory objects))
|
||||
frame-wrapper (mf/use-memo (mf/deps objects) #(frame-wrapper-factory objects))]
|
||||
(when (and shape (not (:hidden shape)))
|
||||
(let [shape (gsh/transform-shape shape)
|
||||
opts #js {:shape shape}
|
||||
(let [opts #js {:shape shape}
|
||||
svg-raw? (= :svg-raw (:type shape))]
|
||||
(if-not svg-raw?
|
||||
[:> shape-container {:shape shape}
|
||||
|
@ -166,8 +164,7 @@
|
|||
[objects object]
|
||||
(let [shapes (cph/get-immediate-children objects)
|
||||
srect (gsh/selection-rect shapes)
|
||||
object (merge object (select-keys srect [:x :y :width :height]))
|
||||
object (gsh/transform-shape object)]
|
||||
object (merge object (select-keys srect [:x :y :width :height]))]
|
||||
(assoc object :fill-color "#f0f0f0")))
|
||||
|
||||
(defn adapt-objects-for-shape
|
||||
|
@ -180,14 +177,12 @@
|
|||
;; Replace the previous object with the new one
|
||||
objects (assoc objects object-id object)
|
||||
|
||||
modifier (-> (gpt/point (:x object) (:y object))
|
||||
(gpt/negate)
|
||||
(gmt/translate-matrix))
|
||||
vector (-> (gpt/point (:x object) (:y object))
|
||||
(gpt/negate))
|
||||
|
||||
mod-ids (cons object-id (cph/get-children-ids objects object-id))
|
||||
updt-fn #(-> %1
|
||||
(assoc-in [%2 :modifiers :displacement] modifier)
|
||||
(update %2 gsh/transform-shape))]
|
||||
|
||||
updt-fn #(update %1 %2 gsh/transform-shape (ctm/move-modifiers vector))]
|
||||
|
||||
(reduce updt-fn objects mod-ids)))
|
||||
|
||||
|
@ -247,24 +242,21 @@
|
|||
bounds2 (gsb/get-object-bounds objects (dissoc frame :shadow :blur))
|
||||
|
||||
delta-bounds (gpt/point (:x bounds) (:y bounds))
|
||||
|
||||
modifier (gmt/translate-matrix (gpt/negate delta-bounds))
|
||||
vector (gpt/negate delta-bounds)
|
||||
|
||||
children-ids
|
||||
(cph/get-children-ids objects frame-id)
|
||||
|
||||
objects
|
||||
(mf/with-memo [frame-id objects modifier]
|
||||
(let [update-fn #(assoc-in %1 [%2 :modifiers :displacement] modifier)]
|
||||
(mf/with-memo [frame-id objects vector]
|
||||
(let [update-fn #(update %1 %2 gsh/transform-shape (ctm/move-modifiers vector))]
|
||||
(->> children-ids
|
||||
(into [frame-id])
|
||||
(reduce update-fn objects))))
|
||||
|
||||
frame
|
||||
(mf/with-memo [modifier]
|
||||
(-> frame
|
||||
(assoc-in [:modifiers :displacement] modifier)
|
||||
(gsh/transform-shape)))
|
||||
(mf/with-memo [vector]
|
||||
(gsh/transform-shape frame (ctm/move-modifiers vector)))
|
||||
|
||||
frame
|
||||
(cond-> frame
|
||||
|
@ -293,7 +285,6 @@
|
|||
:xmlnsXlink "http://www.w3.org/1999/xlink"
|
||||
:xmlns:penpot (when include-metadata? "https://penpot.app/xmlns")
|
||||
:fill "none"}
|
||||
|
||||
[:& shape-wrapper {:shape frame}]]]))
|
||||
|
||||
|
||||
|
@ -305,22 +296,20 @@
|
|||
(let [group-id (:id group)
|
||||
include-metadata? (mf/use-ctx export/include-metadata-ctx)
|
||||
|
||||
modifier
|
||||
vector
|
||||
(mf/use-memo
|
||||
(mf/deps (:x group) (:y group))
|
||||
(fn []
|
||||
(-> (gpt/point (:x group) (:y group))
|
||||
(gpt/negate)
|
||||
(gmt/translate-matrix))))
|
||||
(gpt/negate))))
|
||||
|
||||
objects
|
||||
(mf/use-memo
|
||||
(mf/deps modifier objects group-id)
|
||||
(mf/deps vector objects group-id)
|
||||
(fn []
|
||||
(let [modifier-ids (cons group-id (cph/get-children-ids objects group-id))
|
||||
update-fn #(assoc-in %1 [%2 :modifiers :displacement] modifier)
|
||||
modifiers (reduce update-fn {} modifier-ids)]
|
||||
(gsh/merge-modifiers objects modifiers))))
|
||||
(let [children-ids (cons group-id (cph/get-children-ids objects group-id))
|
||||
update-fn #(update %1 %2 gsh/transform-shape (ctm/move-modifiers vector))]
|
||||
(reduce update-fn objects children-ids))))
|
||||
|
||||
group (get objects group-id)
|
||||
width (* (:width group) zoom)
|
||||
|
|
|
@ -358,7 +358,8 @@
|
|||
"Snaps a position given an old snap to a different position. We use this to provide a temporal
|
||||
snap while the new is being processed."
|
||||
[[position [snap-pos snap-delta]]]
|
||||
(if (some? snap-delta)
|
||||
(if (nil? snap-delta)
|
||||
position
|
||||
(let [dx (if (not= 0 (:x snap-delta))
|
||||
(- (+ (:x snap-pos) (:x snap-delta)) (:x position))
|
||||
0)
|
||||
|
@ -372,6 +373,4 @@
|
|||
dy (if (> (mth/abs dy) snap-accuracy) 0 dy)]
|
||||
(-> position
|
||||
(update :x + dx)
|
||||
(update :y + dy)))
|
||||
|
||||
position))
|
||||
(update :y + dy)))))
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
(ns app.main.ui.components.shape-icon
|
||||
(:require
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.main.ui.icons :as i]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
|
@ -13,7 +14,15 @@
|
|||
(mf/defc element-icon
|
||||
[{:keys [shape main-instance?] :as props}]
|
||||
(case (:type shape)
|
||||
:frame i/artboard
|
||||
:frame (cond
|
||||
(and (ctl/layout? shape) (ctl/col? shape))
|
||||
i/layout-columns
|
||||
|
||||
(and (ctl/layout? shape) (ctl/row? shape))
|
||||
i/layout-rows
|
||||
|
||||
:else
|
||||
i/artboard)
|
||||
:image i/image
|
||||
:line i/line
|
||||
:circle i/circle
|
||||
|
|
|
@ -133,6 +133,8 @@
|
|||
(def justify-content-row-end (icon-xref :justify-content-row-end))
|
||||
(def justify-content-row-start (icon-xref :justify-content-row-start))
|
||||
(def layers (icon-xref :layers))
|
||||
(def layout-columns (icon-xref :layout-columns))
|
||||
(def layout-rows (icon-xref :layout-rows))
|
||||
(def letter-spacing (icon-xref :letter-spacing))
|
||||
(def libraries (icon-xref :libraries))
|
||||
(def library (icon-xref :library))
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
(ns app.main.ui.shapes.bool
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.main.ui.hooks :refer [use-equal-memo]]
|
||||
|
@ -34,9 +33,7 @@
|
|||
(:bool-content shape)
|
||||
|
||||
(some? childs)
|
||||
(->> childs
|
||||
(d/mapm #(gsh/transform-shape %2))
|
||||
(gsh/calc-bool-content shape)))))]
|
||||
(gsh/calc-bool-content shape childs))))]
|
||||
|
||||
[:*
|
||||
(when (some? bool-content)
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
(ns app.main.ui.shapes.mask
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.main.ui.context :as muc]
|
||||
[cuerdas.core :as str]
|
||||
|
@ -50,9 +51,7 @@
|
|||
render-id (mf/use-ctx muc/render-id)
|
||||
svg-text? (and (= :text (:type mask)) (some? (:position-data mask)))
|
||||
|
||||
mask-bb (-> (gsh/transform-shape mask)
|
||||
(:points))
|
||||
|
||||
mask-bb (:points mask)
|
||||
mask-bb-rect (gsh/points->rect mask-bb)]
|
||||
[:defs
|
||||
[:filter {:id (filter-id render-id mask)}
|
||||
|
@ -68,7 +67,7 @@
|
|||
[:clipPath {:class "mask-clip-path"
|
||||
:id (clip-id render-id mask)}
|
||||
[:polyline {:points (->> mask-bb
|
||||
(map #(str (:x %) "," (:y %)))
|
||||
(map #(dm/str (:x %) "," (:y %)))
|
||||
(str/join " "))}]]
|
||||
|
||||
;; When te shape is a text we pass to the shape the info and disable the filter.
|
||||
|
|
|
@ -88,8 +88,6 @@
|
|||
[props]
|
||||
(let [shape (unchecked-get props "shape")
|
||||
childs (mapv #(get objects %) (:shapes shape))
|
||||
shape (gsh/transform-shape shape)
|
||||
|
||||
props (-> (obj/create)
|
||||
(obj/merge! props)
|
||||
(obj/merge! #js {:shape shape
|
||||
|
@ -171,8 +169,7 @@
|
|||
(mf/use-memo (mf/deps objects)
|
||||
#(svg-raw-container-factory objects))]
|
||||
(when (and shape (not (:hidden shape)))
|
||||
(let [shape (-> (gsh/transform-shape shape)
|
||||
(gsh/translate-to-frame frame))
|
||||
(let [shape (gsh/translate-to-frame shape frame)
|
||||
opts #js {:shape shape
|
||||
:frame frame}]
|
||||
(case (:type shape)
|
||||
|
|
|
@ -57,8 +57,6 @@
|
|||
|
||||
shapes (resolve-shapes objects [hover])
|
||||
hover-shape (or (first shapes) frame)
|
||||
hover-shape (gsh/translate-to-frame hover-shape size)
|
||||
|
||||
selected-shapes (resolve-shapes objects selected)
|
||||
selrect (gsh/selection-rect selected-shapes)]
|
||||
|
||||
|
|
|
@ -8,9 +8,10 @@
|
|||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.geom.matrix :as gmt]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.types.modifiers :as ctm]
|
||||
[app.common.types.page :as ctp]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.comments :as dcm]
|
||||
|
@ -29,14 +30,10 @@
|
|||
|
||||
(defn prepare-objects
|
||||
[frame size objects]
|
||||
(let [
|
||||
frame-id (:id frame)
|
||||
modifier (-> (gpt/point (:x size) (:y size))
|
||||
(gpt/negate)
|
||||
(gmt/translate-matrix))
|
||||
|
||||
update-fn #(d/update-when %1 %2 assoc-in [:modifiers :displacement] modifier)]
|
||||
|
||||
(let [frame-id (:id frame)
|
||||
vector (-> (gpt/point (:x size) (:y size))
|
||||
(gpt/negate))
|
||||
update-fn #(d/update-when %1 %2 gsh/transform-shape (ctm/move-modifiers vector))]
|
||||
(->> (cph/get-children-ids objects frame-id)
|
||||
(into [frame-id])
|
||||
(reduce update-fn objects))))
|
||||
|
|
|
@ -350,7 +350,6 @@
|
|||
[props]
|
||||
(let [shape (obj/get props "shape")
|
||||
childs (mapv #(get objects %) (:shapes shape))
|
||||
shape (gsh/transform-shape shape)
|
||||
props (obj/merge! #js {} props
|
||||
#js {:shape shape
|
||||
:childs childs
|
||||
|
@ -429,7 +428,8 @@
|
|||
(mf/with-memo [objects]
|
||||
(svg-raw-container-factory objects))]
|
||||
(when (and shape (not (:hidden shape)))
|
||||
(let [shape (-> (gsh/transform-shape shape)
|
||||
(let [shape (-> shape
|
||||
#_(gsh/transform-shape)
|
||||
(gsh/translate-to-frame frame))
|
||||
|
||||
opts #js {:shape shape
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
[app.main.data.workspace.interactions :as dwi]
|
||||
[app.main.data.workspace.libraries :as dwl]
|
||||
[app.main.data.workspace.selection :as dws]
|
||||
[app.main.data.workspace.shape-layout :as dwsl]
|
||||
[app.main.data.workspace.shapes :as dwsh]
|
||||
[app.main.data.workspace.shortcuts :as sc]
|
||||
[app.main.features :as features]
|
||||
[app.main.refs :as refs]
|
||||
|
@ -212,7 +214,7 @@
|
|||
|
||||
(let [multiple? (> (count shapes) 1)
|
||||
single? (= (count shapes) 1)
|
||||
do-create-artboard-from-selection #(st/emit! (dw/create-artboard-from-selection))
|
||||
do-create-artboard-from-selection #(st/emit! (dwsh/create-artboard-from-selection))
|
||||
|
||||
has-frame? (->> shapes (d/seek cph/frame-shape?))
|
||||
has-group? (->> shapes (d/seek cph/group-shape?))
|
||||
|
@ -364,6 +366,31 @@
|
|||
|
||||
[:& menu-entry {:title (tr "workspace.shape.menu.flow-start")
|
||||
:on-click do-add-flow}])))))
|
||||
(mf/defc context-menu-flex
|
||||
[{:keys [shapes]}]
|
||||
(let [single? (= (count shapes) 1)
|
||||
has-frame? (->> shapes (d/seek cph/frame-shape?))
|
||||
is-frame? (and single? has-frame?)
|
||||
is-flex-container? (and is-frame? (= :flex (:layout (first shapes))))
|
||||
has-group? (->> shapes (d/seek cph/group-shape?))
|
||||
is-group? (and single? has-group?)
|
||||
ids (->> shapes (map :id))
|
||||
add-flex #(st/emit! (dwsl/create-layout-from-selection :flex))
|
||||
remove-flex #(st/emit! (dwsl/remove-layout ids))]
|
||||
(cond
|
||||
(or (not single?) (and is-frame? (not is-flex-container?)) is-group?)
|
||||
[:*
|
||||
[:& menu-separator]
|
||||
[:& menu-entry {:title (tr "workspace.shape.menu.add-flex")
|
||||
:shortcut (sc/get-tooltip :toogle-layout-flex)
|
||||
:on-click add-flex}]]
|
||||
|
||||
is-flex-container?
|
||||
[:*
|
||||
[:& menu-separator]
|
||||
[:& menu-entry {:title (tr "workspace.shape.menu.remove-flex")
|
||||
:shortcut (sc/get-tooltip :toogle-layout-flex)
|
||||
:on-click remove-flex}]])))
|
||||
|
||||
(mf/defc context-menu-component
|
||||
[{:keys [shapes]}]
|
||||
|
@ -517,6 +544,7 @@
|
|||
[:> context-menu-path props]
|
||||
[:> context-menu-layer-options props]
|
||||
[:> context-menu-prototype props]
|
||||
[:> context-menu-flex props]
|
||||
[:> context-menu-component props]
|
||||
[:> context-menu-delete props]])))
|
||||
|
||||
|
|
|
@ -12,7 +12,9 @@
|
|||
others are defined using a generic wrapper implemented in
|
||||
common."
|
||||
(:require
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.ui.context :as ctx]
|
||||
[app.main.ui.shapes.circle :as circle]
|
||||
[app.main.ui.shapes.image :as image]
|
||||
|
@ -55,33 +57,33 @@
|
|||
(mf/deps objects)
|
||||
#(cph/objects-by-frame objects))]
|
||||
|
||||
[:& (mf/provider ctx/active-frames) {:value active-frames}
|
||||
;; Render font faces only for shapes that are part of the root
|
||||
;; frame but don't belongs to any other frame.
|
||||
(let [xform (comp
|
||||
(remove cph/frame-shape?)
|
||||
(mapcat #(cph/get-children-with-self objects (:id %))))]
|
||||
[:& ff/fontfaces-style {:shapes (into [] xform shapes)}])
|
||||
[:g {:id (dm/str "shape-" uuid/zero)}
|
||||
[:& (mf/provider ctx/active-frames) {:value active-frames}
|
||||
;; Render font faces only for shapes that are part of the root
|
||||
;; frame but don't belongs to any other frame.
|
||||
(let [xform (comp
|
||||
(remove cph/frame-shape?)
|
||||
(mapcat #(cph/get-children-with-self objects (:id %))))]
|
||||
[:& ff/fontfaces-style {:shapes (into [] xform shapes)}])
|
||||
|
||||
(for [shape shapes]
|
||||
(cond
|
||||
(not (cph/frame-shape? shape))
|
||||
[:& shape-wrapper
|
||||
{:shape shape
|
||||
:key (:id shape)}]
|
||||
[:g.frame-children
|
||||
(for [shape shapes]
|
||||
[:g.ws-shape-wrapper {:key (:id shape)}
|
||||
(cond
|
||||
(not (cph/frame-shape? shape))
|
||||
[:& shape-wrapper
|
||||
{:shape shape}]
|
||||
|
||||
(cph/root-frame? shape)
|
||||
[:& root-frame-wrapper
|
||||
{:shape shape
|
||||
:key (:id shape)
|
||||
:objects (get frame-objects (:id shape))
|
||||
:thumbnail? (not (contains? active-frames (:id shape)))}]
|
||||
(cph/root-frame? shape)
|
||||
[:& root-frame-wrapper
|
||||
{:shape shape
|
||||
:objects (get frame-objects (:id shape))
|
||||
:thumbnail? (not (contains? active-frames (:id shape)))}]
|
||||
|
||||
:else
|
||||
[:& nested-frame-wrapper
|
||||
{:shape shape
|
||||
:key (:id shape)
|
||||
:objects (get frame-objects (:id shape))}]))]))
|
||||
:else
|
||||
[:& nested-frame-wrapper
|
||||
{:shape shape
|
||||
:objects (get frame-objects (:id shape))}])])]]]))
|
||||
|
||||
(mf/defc shape-wrapper
|
||||
{::mf/wrap [#(mf/memo' % (mf/check-props ["shape"]))]
|
||||
|
@ -98,20 +100,21 @@
|
|||
|
||||
opts #js {:shape shape :thumbnail? thumbnail?}]
|
||||
(when (and (some? shape) (not (:hidden shape)))
|
||||
(case (:type shape)
|
||||
:path [:> path/path-wrapper opts]
|
||||
:text [:> text/text-wrapper opts]
|
||||
:group [:> group-wrapper opts]
|
||||
:rect [:> rect-wrapper opts]
|
||||
:image [:> image-wrapper opts]
|
||||
:circle [:> circle-wrapper opts]
|
||||
:svg-raw [:> svg-raw-wrapper opts]
|
||||
:bool [:> bool-wrapper opts]
|
||||
[:g.workspace-shape-wrapper
|
||||
(case (:type shape)
|
||||
:path [:> path/path-wrapper opts]
|
||||
:text [:> text/text-wrapper opts]
|
||||
:group [:> group-wrapper opts]
|
||||
:rect [:> rect-wrapper opts]
|
||||
:image [:> image-wrapper opts]
|
||||
:circle [:> circle-wrapper opts]
|
||||
:svg-raw [:> svg-raw-wrapper opts]
|
||||
:bool [:> bool-wrapper opts]
|
||||
|
||||
;; Only used when drawing a new frame.
|
||||
:frame [:> nested-frame-wrapper opts]
|
||||
;; Only used when drawing a new frame.
|
||||
:frame [:> nested-frame-wrapper opts]
|
||||
|
||||
nil))))
|
||||
nil)])))
|
||||
|
||||
(def group-wrapper (group/group-wrapper-factory shape-wrapper))
|
||||
(def svg-raw-wrapper (svg-raw/svg-raw-wrapper-factory shape-wrapper))
|
||||
|
|
|
@ -11,79 +11,28 @@
|
|||
[app.common.geom.matrix :as gmt]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.types.modifiers :as ctm]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.hooks :as hooks]
|
||||
[app.main.ui.workspace.viewport.utils :as vwu]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.globals :as globals]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(defn- transform-no-resize
|
||||
"If we apply a scale directly to the texts it will show deformed so we need to create this
|
||||
correction matrix to \"undo\" the resize but keep the other transformations."
|
||||
[{:keys [x y width height points transform transform-inverse] :as shape} current-transform modifiers]
|
||||
(defn get-shape-node
|
||||
([id]
|
||||
(get-shape-node js/document id))
|
||||
|
||||
(let [corner-pt (first points)
|
||||
corner-pt (cond-> corner-pt (some? transform-inverse) (gpt/transform transform-inverse))
|
||||
|
||||
resize-x? (some? (:resize-vector modifiers))
|
||||
resize-y? (some? (:resize-vector-2 modifiers))
|
||||
|
||||
flip-x? (neg? (get-in modifiers [:resize-vector :x]))
|
||||
flip-y? (or (neg? (get-in modifiers [:resize-vector :y]))
|
||||
(neg? (get-in modifiers [:resize-vector-2 :y])))
|
||||
|
||||
result (cond-> (gmt/matrix)
|
||||
(and (some? transform) (or resize-x? resize-y?))
|
||||
(gmt/multiply transform)
|
||||
|
||||
resize-x?
|
||||
(gmt/scale (gpt/inverse (:resize-vector modifiers)) corner-pt)
|
||||
|
||||
resize-y?
|
||||
(gmt/scale (gpt/inverse (:resize-vector-2 modifiers)) corner-pt)
|
||||
|
||||
flip-x?
|
||||
(gmt/scale (gpt/point -1 1) corner-pt)
|
||||
|
||||
flip-y?
|
||||
(gmt/scale (gpt/point 1 -1) corner-pt)
|
||||
|
||||
(and (some? transform) (or resize-x? resize-y?))
|
||||
(gmt/multiply transform-inverse))
|
||||
|
||||
[width height]
|
||||
(if (or resize-x? resize-y?)
|
||||
(let [pc (cond-> (gpt/point x y)
|
||||
(some? transform)
|
||||
(gpt/transform transform)
|
||||
|
||||
(some? current-transform)
|
||||
(gpt/transform current-transform))
|
||||
|
||||
pw (cond-> (gpt/point (+ x width) y)
|
||||
(some? transform)
|
||||
(gpt/transform transform)
|
||||
|
||||
(some? current-transform)
|
||||
(gpt/transform current-transform))
|
||||
|
||||
ph (cond-> (gpt/point x (+ y height))
|
||||
(some? transform)
|
||||
(gpt/transform transform)
|
||||
|
||||
(some? current-transform)
|
||||
(gpt/transform current-transform))]
|
||||
[(gpt/distance pc pw) (gpt/distance pc ph)])
|
||||
[width height])]
|
||||
|
||||
[result width height]))
|
||||
([base-node id]
|
||||
(if (= (.-id base-node) (dm/str "shape-" id))
|
||||
base-node
|
||||
(dom/query base-node (dm/str "#shape-" id)))))
|
||||
|
||||
(defn get-nodes
|
||||
"Retrieve the DOM nodes to apply the matrix transformation"
|
||||
[base-node {:keys [id type masked-group?] :as shape}]
|
||||
(when (some? base-node)
|
||||
(let [shape-node (if (= (.-id base-node) (dm/str "shape-" id))
|
||||
base-node
|
||||
(dom/query base-node (dm/str "#shape-" id)))
|
||||
(let [shape-node (get-shape-node base-node id)
|
||||
|
||||
frame? (= :frame type)
|
||||
group? (= :group type)
|
||||
|
@ -110,8 +59,7 @@
|
|||
(dom/query-all shape-defs ".svg-mask-wrapper")))
|
||||
|
||||
text?
|
||||
[shape-node
|
||||
(dom/query shape-node ".text-container")]
|
||||
[shape-node]
|
||||
|
||||
:else
|
||||
[shape-node]))))
|
||||
|
@ -164,7 +112,7 @@
|
|||
|
||||
(defn set-transform-att!
|
||||
[node att value]
|
||||
|
||||
|
||||
(let [old-att (dom/get-attribute node (dm/str "data-old-" att))
|
||||
new-value (if (some? old-att)
|
||||
(dm/str value " " old-att)
|
||||
|
@ -177,37 +125,20 @@
|
|||
|
||||
(defn update-transform!
|
||||
[base-node shapes transforms modifiers]
|
||||
(doseq [{:keys [id type] :as shape} shapes]
|
||||
(doseq [{:keys [id _type] :as shape} shapes]
|
||||
(when-let [nodes (get-nodes base-node shape)]
|
||||
(let [transform (get transforms id)
|
||||
modifiers (get-in modifiers [id :modifiers])
|
||||
text? (= type :text)
|
||||
transform-text? (and text? (and (nil? (:resize-vector modifiers)) (nil? (:resize-vector-2 modifiers))))]
|
||||
modifiers (get-in modifiers [id :modifiers])]
|
||||
|
||||
(doseq [node nodes]
|
||||
(cond
|
||||
;; Text shapes need special treatment because their resize only change
|
||||
;; the text area, not the change size/position
|
||||
(dom/class? node "frame-thumbnail")
|
||||
(let [[transform] (transform-no-resize shape transform modifiers)]
|
||||
(set-transform-att! node "transform" transform))
|
||||
|
||||
(dom/class? node "frame-children")
|
||||
(set-transform-att! node "transform" (gmt/inverse transform))
|
||||
|
||||
(dom/class? node "text-container")
|
||||
(let [modifiers (dissoc modifiers :displacement :rotation)]
|
||||
(when (not (gsh/empty-modifiers? modifiers))
|
||||
(let [mtx (-> shape
|
||||
(assoc :modifiers modifiers)
|
||||
(gsh/transform-shape)
|
||||
(gsh/transform-matrix {:no-flip true}))]
|
||||
(override-transform-att! node "transform" mtx))))
|
||||
|
||||
(dom/class? node "frame-title")
|
||||
(let [shape (-> shape (assoc :modifiers modifiers) gsh/transform-shape)
|
||||
zoom (get-in @st/state [:workspace-local :zoom] 1)
|
||||
mtx (vwu/title-transform shape zoom)]
|
||||
(let [shape (gsh/transform-shape shape modifiers)
|
||||
zoom (get-in @st/state [:workspace-local :zoom] 1)
|
||||
mtx (vwu/title-transform shape zoom)]
|
||||
(override-transform-att! node "transform" mtx))
|
||||
|
||||
(or (= (dom/get-tag-name node) "mask")
|
||||
|
@ -221,7 +152,7 @@
|
|||
(= (dom/get-tag-name node) "pattern")
|
||||
(set-transform-att! node "patternTransform" transform)
|
||||
|
||||
(and (some? transform) (some? node) (or (not text?) transform-text?))
|
||||
(and (some? transform) (some? node))
|
||||
(set-transform-att! node "transform" transform)))))))
|
||||
|
||||
(defn remove-transform!
|
||||
|
@ -249,6 +180,16 @@
|
|||
(dom/remove-attribute! node "data-old-transform")
|
||||
(dom/remove-attribute! node "transform")))))))))
|
||||
|
||||
(defn adapt-text-modifiers
|
||||
[modifiers shape]
|
||||
(let [shape' (gsh/transform-shape shape modifiers)
|
||||
scalev
|
||||
(gpt/point (/ (:width shape) (:width shape'))
|
||||
(/ (:height shape) (:height shape')))]
|
||||
;; Reverse the change in size so we can recalculate the layout
|
||||
(-> modifiers
|
||||
(ctm/resize scalev (-> shape' :points first) (:transform shape') (:transform-inverse shape')))))
|
||||
|
||||
(defn use-dynamic-modifiers
|
||||
[objects node modifiers]
|
||||
|
||||
|
@ -259,41 +200,71 @@
|
|||
(when (some? modifiers)
|
||||
(d/mapm (fn [id {modifiers :modifiers}]
|
||||
(let [shape (get objects id)
|
||||
center (gsh/center-shape shape)
|
||||
modifiers (cond-> modifiers
|
||||
;; For texts we only use the displacement because
|
||||
;; resize needs to recalculate the text layout
|
||||
(= :text (:type shape))
|
||||
(select-keys [:displacement :rotation]))]
|
||||
(gsh/modifiers->transform center modifiers)))
|
||||
adapt-text? (and (= :text (:type shape)) (not (ctm/only-move? modifiers)))
|
||||
modifiers (cond-> modifiers adapt-text? (adapt-text-modifiers shape))]
|
||||
(ctm/modifiers->transform modifiers)))
|
||||
modifiers))))
|
||||
|
||||
add-children (mf/use-memo (mf/deps modifiers) #(ctm/added-children-frames modifiers))
|
||||
add-children (hooks/use-equal-memo add-children)
|
||||
add-children-prev (hooks/use-previous add-children)
|
||||
|
||||
shapes
|
||||
(mf/use-memo
|
||||
(mf/deps transforms)
|
||||
(fn []
|
||||
(->> (keys transforms)
|
||||
(filter #(some? (get transforms %)))
|
||||
(mapv (d/getf objects)))))
|
||||
|
||||
prev-shapes (mf/use-var nil)
|
||||
prev-modifiers (mf/use-var nil)
|
||||
prev-transforms (mf/use-var nil)]
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps add-children)
|
||||
(fn []
|
||||
(doseq [{:keys [frame shape]} add-children-prev]
|
||||
(let [frame-node (get-shape-node node frame)
|
||||
shape-node (get-shape-node shape)
|
||||
mirror-node (dom/query frame-node (dm/fmt ".mirror-shape[href='#shape-%'" shape))]
|
||||
(when mirror-node (.remove mirror-node))
|
||||
(dom/remove-attribute! (dom/get-parent shape-node) "display")))
|
||||
|
||||
(doseq [{:keys [frame shape]} add-children]
|
||||
(let [frame-node (get-shape-node node frame)
|
||||
shape-node (get-shape-node shape)
|
||||
|
||||
use-node
|
||||
(.createElementNS globals/document "http://www.w3.org/2000/svg" "use")
|
||||
|
||||
contents-node
|
||||
(or (dom/query frame-node ".frame-children") frame-node)]
|
||||
|
||||
(dom/set-attribute! use-node "href" (dm/fmt "#shape-%" shape))
|
||||
(dom/add-class! use-node "mirror-shape")
|
||||
(dom/append-child! contents-node use-node)
|
||||
(dom/set-attribute! (dom/get-parent shape-node) "display" "none")))))
|
||||
|
||||
(mf/use-layout-effect
|
||||
(mf/deps transforms)
|
||||
(fn []
|
||||
(let [is-prev-val? (d/not-empty? @prev-modifiers)
|
||||
is-cur-val? (d/not-empty? modifiers)]
|
||||
|
||||
(when (and (not is-prev-val?) is-cur-val?)
|
||||
(start-transform! node shapes))
|
||||
(let [curr-shapes-set (into #{} (map :id) shapes)
|
||||
prev-shapes-set (into #{} (map :id) @prev-shapes)
|
||||
|
||||
(when is-cur-val?
|
||||
new-shapes (->> shapes (remove #(contains? prev-shapes-set (:id %))))
|
||||
removed-shapes (->> @prev-shapes (remove #(contains? curr-shapes-set (:id %))))]
|
||||
|
||||
(when (d/not-empty? new-shapes)
|
||||
(start-transform! node new-shapes))
|
||||
|
||||
(when (d/not-empty? shapes)
|
||||
(update-transform! node shapes transforms modifiers))
|
||||
|
||||
(when (and is-prev-val? (not is-cur-val?))
|
||||
(remove-transform! node @prev-shapes))
|
||||
(when (d/not-empty? removed-shapes)
|
||||
(remove-transform! node @prev-shapes)))
|
||||
|
||||
(reset! prev-modifiers modifiers)
|
||||
(reset! prev-transforms transforms)
|
||||
(reset! prev-shapes shapes))))))
|
||||
(reset! prev-modifiers modifiers)
|
||||
(reset! prev-transforms transforms)
|
||||
(reset! prev-shapes shapes)))))
|
||||
|
|
|
@ -199,6 +199,8 @@
|
|||
(mf/use-effect
|
||||
(mf/deps disable?)
|
||||
(fn []
|
||||
(when (and disable? (not @disable-ref?))
|
||||
(rx/push! updates-str :update))
|
||||
(reset! disable-ref? disable?)))
|
||||
|
||||
(mf/use-effect
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
(ns app.main.ui.workspace.shapes.path.editor
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes.path :as gsp]
|
||||
[app.common.path.commands :as upc]
|
||||
|
@ -183,8 +185,9 @@
|
|||
matches (concat (second (:x snap-matches)) (second (:y snap-matches)))]
|
||||
|
||||
[:g.snap-paths
|
||||
(for [[from to] matches]
|
||||
[:line {:x1 (:x from)
|
||||
(for [[idx [from to]] (d/enumerate matches)]
|
||||
[:line {:key (dm/str "snap-" idx "-" from "-" to)
|
||||
:x1 (:x from)
|
||||
:y1 (:y from)
|
||||
:x2 (:x to)
|
||||
:y2 (:y to)
|
||||
|
@ -308,7 +311,7 @@
|
|||
:start-path? start-p?
|
||||
:zoom zoom}]])
|
||||
|
||||
(for [position points]
|
||||
(for [[index position] (d/enumerate points)]
|
||||
(let [show-handler?
|
||||
(fn [[index prefix]]
|
||||
(let [handler-position (upc/handler->point content index prefix)]
|
||||
|
@ -322,7 +325,7 @@
|
|||
pos-handlers (->> pos-handlers (filter show-handler?))
|
||||
curve? (boolean (seq pos-handlers))]
|
||||
|
||||
[:g.path-node
|
||||
[:g.path-node {:key (dm/str index "-" (:x position) "-" (:y position))}
|
||||
[:g.point-handlers {:pointer-events (when (= edit-mode :draw) "none")}
|
||||
(for [[index prefix] pos-handlers]
|
||||
(let [handler-position (upc/handler->point content index prefix)
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
:height height
|
||||
:style {:fill "none" :stroke "red"}}]
|
||||
|
||||
;; Text baselineazo
|
||||
;; Text baseline
|
||||
[:line {:x1 (mth/round x)
|
||||
:y1 (mth/round (- (:y data) (:height data)))
|
||||
:x2 (mth/round (+ x width))
|
||||
|
|
|
@ -8,11 +8,13 @@
|
|||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.geom.shapes.text :as gsht]
|
||||
[app.common.math :as mth]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.text :as txt]
|
||||
[app.common.types.modifiers :as ctm]
|
||||
[app.main.data.workspace.texts :as dwt]
|
||||
[app.main.fonts :as fonts]
|
||||
[app.main.refs :as refs]
|
||||
|
@ -31,22 +33,24 @@
|
|||
(-> shape
|
||||
(cond-> (some? (meta (:position-data shape)))
|
||||
(with-meta (meta (:position-data shape))))
|
||||
(dissoc :position-data :transform :transform-inverse)))
|
||||
(dissoc :position-data)))
|
||||
|
||||
(defn strip-modifier
|
||||
[modifier]
|
||||
(if (or (some? (dm/get-in modifier [:modifiers :resize-vector]))
|
||||
(some? (dm/get-in modifier [:modifiers :resize-vector-2])))
|
||||
modifier
|
||||
(d/update-when modifier :modifiers dissoc :displacement :rotation)))
|
||||
(defn fix-position [shape modifier]
|
||||
(let [shape' (-> shape
|
||||
(assoc :grow-type :fixed)
|
||||
(gsh/transform-shape modifier))
|
||||
|
||||
deltav (gpt/to-vec (gpt/point (:selrect shape'))
|
||||
(gpt/point (:selrect shape)))]
|
||||
(gsh/transform-shape shape' (ctm/move-modifiers deltav))))
|
||||
|
||||
(defn process-shape [modifiers {:keys [id] :as shape}]
|
||||
(let [modifier (-> (get modifiers id) strip-modifier)
|
||||
shape (cond-> shape
|
||||
(not (gsh/empty-modifiers? (:modifiers modifier)))
|
||||
(-> (assoc :grow-type :fixed)
|
||||
(merge modifier) gsh/transform-shape))]
|
||||
(let [modifier (dm/get-in modifiers [id :modifiers])]
|
||||
(-> shape
|
||||
(cond-> (and (some? modifier)
|
||||
(not (ctm/only-move? modifier)))
|
||||
(fix-position modifier))
|
||||
|
||||
(cond-> (nil? (:position-data shape))
|
||||
(assoc :migrate true))
|
||||
strip-position-data)))
|
||||
|
@ -88,20 +92,26 @@
|
|||
|
||||
(defn- update-text-modifier
|
||||
[{:keys [grow-type id]} node]
|
||||
(p/let [position-data (tsp/calc-position-data id)
|
||||
props {:position-data position-data}
|
||||
(ts/raf
|
||||
#(p/let [position-data (tsp/calc-position-data id)
|
||||
props {:position-data position-data}
|
||||
|
||||
props
|
||||
(if (contains? #{:auto-height :auto-width} grow-type)
|
||||
(let [{:keys [width height]} (-> (dom/query node ".paragraph-set") (dom/get-client-size))
|
||||
width (mth/ceil width)
|
||||
height (mth/ceil height)]
|
||||
(if (and (not (mth/almost-zero? width)) (not (mth/almost-zero? height)))
|
||||
(assoc props :width width :height height)
|
||||
props))
|
||||
props)]
|
||||
props
|
||||
(if (contains? #{:auto-height :auto-width} grow-type)
|
||||
(let [{:keys [width height]} (-> (dom/query node ".paragraph-set") (dom/get-client-size))
|
||||
width (mth/ceil width)
|
||||
height (mth/ceil height)]
|
||||
(if (and (not (mth/almost-zero? width)) (not (mth/almost-zero? height)))
|
||||
(cond-> props
|
||||
(= grow-type :auto-width)
|
||||
(assoc :width width)
|
||||
|
||||
(st/emit! (dwt/update-text-modifier id props))))
|
||||
(or (= grow-type :auto-height) (= grow-type :auto-width))
|
||||
(assoc :height height))
|
||||
props))
|
||||
props)]
|
||||
|
||||
(st/emit! (dwt/update-text-modifier id props)))))
|
||||
|
||||
(mf/defc text-container
|
||||
{::mf/wrap-props false
|
||||
|
@ -136,8 +146,8 @@
|
|||
(fn [id]
|
||||
(let [new-shape (get text-shapes id)
|
||||
old-shape (get prev-text-shapes id)
|
||||
old-modifiers (-> (get prev-modifiers id) strip-modifier)
|
||||
new-modifiers (-> (get modifiers id) strip-modifier)
|
||||
old-modifiers (get prev-modifiers id)
|
||||
new-modifiers (get modifiers id)
|
||||
|
||||
remote? (some? (-> new-shape meta :session-id)) ]
|
||||
|
||||
|
@ -159,10 +169,9 @@
|
|||
|
||||
handle-update-modifier (mf/use-callback update-text-modifier)
|
||||
handle-update-shape (mf/use-callback update-text-shape)]
|
||||
|
||||
[:*
|
||||
(for [{:keys [id] :as shape} changed-texts]
|
||||
[:& text-container {:shape (gsh/transform-shape shape)
|
||||
[:& text-container {:shape shape
|
||||
:on-update (if (some? (get modifiers (:id shape)))
|
||||
handle-update-modifier
|
||||
handle-update-shape)
|
||||
|
|
|
@ -37,35 +37,35 @@
|
|||
(mf/defc shape-options
|
||||
{::mf/wrap [#(mf/throttle % 60)]}
|
||||
[{:keys [shape shapes-with-children page-id file-id shared-libs]}]
|
||||
[:*
|
||||
(case (:type shape)
|
||||
:frame [:& frame/options {:shape shape}]
|
||||
:group [:& group/options {:shape shape :shape-with-children shapes-with-children :file-id file-id :shared-libs shared-libs}]
|
||||
:text [:& text/options {:shape shape :file-id file-id :shared-libs shared-libs}]
|
||||
:rect [:& rect/options {:shape shape}]
|
||||
:circle [:& circle/options {:shape shape}]
|
||||
:path [:& path/options {:shape shape}]
|
||||
:image [:& image/options {:shape shape}]
|
||||
:svg-raw [:& svg-raw/options {:shape shape}]
|
||||
:bool [:& bool/options {:shape shape}]
|
||||
nil)
|
||||
[:& exports-menu
|
||||
{:ids [(:id shape)]
|
||||
:values (select-keys shape [:exports])
|
||||
:shape shape
|
||||
:page-id page-id
|
||||
:file-id file-id}]])
|
||||
(let [workspace-modifiers (mf/deref refs/workspace-modifiers)
|
||||
modifiers (get-in workspace-modifiers [(:id shape) :modifiers])
|
||||
shape (gsh/transform-shape shape modifiers)]
|
||||
[:*
|
||||
(case (:type shape)
|
||||
:frame [:& frame/options {:shape shape}]
|
||||
:group [:& group/options {:shape shape :shape-with-children shapes-with-children :file-id file-id :shared-libs shared-libs}]
|
||||
:text [:& text/options {:shape shape :file-id file-id :shared-libs shared-libs}]
|
||||
:rect [:& rect/options {:shape shape}]
|
||||
:circle [:& circle/options {:shape shape}]
|
||||
:path [:& path/options {:shape shape}]
|
||||
:image [:& image/options {:shape shape}]
|
||||
:svg-raw [:& svg-raw/options {:shape shape}]
|
||||
:bool [:& bool/options {:shape shape}]
|
||||
nil)
|
||||
[:& exports-menu
|
||||
{:ids [(:id shape)]
|
||||
:values (select-keys shape [:exports])
|
||||
:shape shape
|
||||
:page-id page-id
|
||||
:file-id file-id}]]))
|
||||
|
||||
(mf/defc options-content
|
||||
{::mf/wrap [mf/memo]}
|
||||
[{:keys [selected section shapes shapes-with-children page-id file-id]}]
|
||||
(let [drawing (mf/deref refs/workspace-drawing)
|
||||
base-objects (-> (mf/deref refs/workspace-page-objects))
|
||||
objects (mf/deref refs/workspace-page-objects)
|
||||
shared-libs (mf/deref refs/workspace-libraries)
|
||||
modifiers (mf/deref refs/workspace-modifiers)
|
||||
objects-modified (mf/with-memo [base-objects modifiers]
|
||||
(gsh/merge-modifiers base-objects modifiers))
|
||||
selected-shapes (into [] (keep (d/getf objects-modified)) selected)]
|
||||
selected-shapes (into [] (keep (d/getf objects)) selected)]
|
||||
[:div.tool-window
|
||||
[:div.tool-window-content
|
||||
[:& tab-container {:on-change-tab #(st/emit! (udw/set-options-mode %))
|
||||
|
|
|
@ -30,7 +30,6 @@
|
|||
frames (map #(deref (refs/object-by-id (:frame-id %))) old-shapes)
|
||||
|
||||
shapes (as-> old-shapes $
|
||||
(map gsh/transform-shape $)
|
||||
(map gsh/translate-to-frame $ frames))
|
||||
|
||||
values (let [{:keys [x y]} (-> shapes first :points gsh/points->selrect)]
|
||||
|
|
|
@ -18,12 +18,12 @@
|
|||
|
||||
(def layout-container-flex-attrs
|
||||
[:layout ;; :flex, :grid in the future
|
||||
:layout-flex-dir ;; :row, :reverse-row, :column, :reverse-column
|
||||
: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-align-items ;; :start :end :center :stretch
|
||||
:layout-justify-content ;; :start :center :end :space-between :space-around
|
||||
:layout-align-content ;; :start :center :end :space-between :space-around :strech (by default)
|
||||
:layout-align-content ;; :start :center :end :space-between :space-around :stretch (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
|
||||
|
@ -37,13 +37,13 @@
|
|||
:start i/align-items-column-start
|
||||
:end i/align-items-column-end
|
||||
:center i/align-items-column-center
|
||||
:strech i/align-items-column-strech
|
||||
:stretch i/align-items-column-strech
|
||||
:baseline i/align-items-column-baseline)
|
||||
(case val
|
||||
:start i/align-items-row-start
|
||||
:end i/align-items-row-end
|
||||
:center i/align-items-row-center
|
||||
:strech i/align-items-row-strech
|
||||
:stretch i/align-items-row-strech
|
||||
:baseline i/align-items-row-baseline))
|
||||
:justify-content (if is-col?
|
||||
(case val
|
||||
|
@ -66,29 +66,29 @@
|
|||
:center i/align-content-column-center
|
||||
:space-around i/align-content-column-around
|
||||
:space-between i/align-content-column-between
|
||||
:strech nil)
|
||||
|
||||
:stretch nil)
|
||||
|
||||
(case val
|
||||
:start i/align-content-row-start
|
||||
:end i/align-content-row-end
|
||||
:center i/align-content-row-center
|
||||
:space-around i/align-content-row-around
|
||||
:space-between i/align-content-row-between
|
||||
:strech nil))
|
||||
:stretch nil))
|
||||
|
||||
:align-self (if is-col?
|
||||
(case val
|
||||
:start i/align-self-column-top
|
||||
:end i/align-self-column-bottom
|
||||
:center i/align-self-column-center
|
||||
:strech i/align-self-column-strech
|
||||
:baseline i/align-self-column-baseline)
|
||||
(case val
|
||||
:start i/align-self-row-left
|
||||
:end i/align-self-row-right
|
||||
:center i/align-self-row-center
|
||||
:strech i/align-self-row-strech
|
||||
:baseline i/align-self-row-baseline))))
|
||||
:stretch i/align-self-row-strech
|
||||
:baseline i/align-self-row-baseline)
|
||||
(case val
|
||||
:start i/align-self-column-top
|
||||
:end i/align-self-column-bottom
|
||||
:center i/align-self-column-center
|
||||
:stretch i/align-self-column-strech
|
||||
:baseline i/align-self-column-baseline))))
|
||||
|
||||
(mf/defc direction-btn
|
||||
[{:keys [dir saved-dir set-direction] :as props}]
|
||||
|
@ -129,7 +129,7 @@
|
|||
[{:keys [is-col? align-items set-align] :as props}]
|
||||
|
||||
[:div.align-items-style
|
||||
(for [align [:start :center :end :strech]]
|
||||
(for [align [:start :center :end #_:stretch #_:baseline]]
|
||||
[:button.align-start.tooltip
|
||||
{:class (dom/classnames :active (= align-items align)
|
||||
:tooltip-bottom-left (not= align :start)
|
||||
|
@ -170,7 +170,8 @@
|
|||
[{:keys [values on-change-style on-change] :as props}]
|
||||
|
||||
(let [padding-type (:layout-padding-type values)
|
||||
rx (if (apply = (vals (:layout-padding values)))
|
||||
rx (if (and (not (= :multiple (:layout-padding values)))
|
||||
(apply = (vals (:layout-padding values))))
|
||||
(:p1 (:layout-padding values))
|
||||
"--")]
|
||||
|
||||
|
@ -274,13 +275,14 @@
|
|||
(st/emit! (dwsl/remove-layout ids))
|
||||
(reset! open? false))
|
||||
|
||||
set-flex (fn []
|
||||
(st/emit! (dwsl/remove-layout ids))
|
||||
(on-add-layout :flex))
|
||||
|
||||
set-grid (fn []
|
||||
(st/emit! (dwsl/remove-layout ids))
|
||||
(on-add-layout :grid))
|
||||
;; Uncomment when activating the grid options
|
||||
;; set-flex (fn []
|
||||
;; (st/emit! (dwsl/remove-layout ids))
|
||||
;; (on-add-layout :flex))
|
||||
;;
|
||||
;; set-grid (fn []
|
||||
;; (st/emit! (dwsl/remove-layout ids))
|
||||
;; (on-add-layout :grid))
|
||||
|
||||
;; Flex-direction
|
||||
|
||||
|
@ -312,7 +314,7 @@
|
|||
align-content (:layout-align-content values)
|
||||
set-align-content (fn [value]
|
||||
(if (= align-content value)
|
||||
(st/emit! (dwsl/update-layout ids {:layout-align-content :strech}))
|
||||
(st/emit! (dwsl/update-layout ids {:layout-align-content :stretch}))
|
||||
(st/emit! (dwsl/update-layout ids {:layout-align-content value}))))
|
||||
|
||||
;; Gap
|
||||
|
@ -365,7 +367,7 @@
|
|||
[:span "Layout"]
|
||||
(if (:layout values)
|
||||
[:div.title-actions
|
||||
[:div.layout-btns
|
||||
#_[:div.layout-btns
|
||||
[:button {:on-click set-flex
|
||||
:class (dom/classnames
|
||||
:active (= :flex layout-type))} "Flex"]
|
||||
|
|
|
@ -8,7 +8,9 @@
|
|||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.main.data.workspace.shape-layout :as dwsl]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.numeric-input :refer [numeric-input]]
|
||||
[app.main.ui.icons :as i]
|
||||
|
@ -26,13 +28,13 @@
|
|||
:layout-item-min-h ;; num
|
||||
:layout-item-max-w ;; num
|
||||
:layout-item-min-w ;; num
|
||||
:layout-item-align-self ;; :start :end :center :strech :baseline
|
||||
:layout-item-align-self ;; :start :end :center :stretch :baseline
|
||||
])
|
||||
|
||||
(mf/defc margin-section
|
||||
[{:keys [values change-margin-style on-margin-change] :as props}]
|
||||
|
||||
(let [margin-type (or (:layout-margin-type values) :simple)]
|
||||
(let [margin-type (or (:layout-item-margin-type values) :simple)]
|
||||
|
||||
[:div.margin-row
|
||||
[:div.margin-icons
|
||||
|
@ -57,7 +59,7 @@
|
|||
{:placeholder "--"
|
||||
:on-click #(dom/select-target %)
|
||||
:on-change (partial on-margin-change :simple)
|
||||
:value (or (-> values :layout-margin :m1) 0)}]]]
|
||||
:value (or (-> values :layout-item-margin :m1) 0)}]]]
|
||||
|
||||
(= margin-type :multiple)
|
||||
(for [num [:m1 :m2 :m3 :m4]]
|
||||
|
@ -73,95 +75,100 @@
|
|||
{:placeholder "--"
|
||||
:on-click #(dom/select-target %)
|
||||
:on-change (partial on-margin-change num)
|
||||
:value (or (-> values :layout-margin num) 0)}]]]))]]))
|
||||
:value (or (-> values :layout-item-margin num) 0)}]]]))]]))
|
||||
|
||||
(mf/defc element-behavior
|
||||
[{:keys [is-layout-container? is-layout-child? layout-h-behavior layout-v-behavior on-change-behavior] :as props}]
|
||||
[{:keys [is-layout-container? is-layout-child? layout-item-h-sizing layout-item-v-sizing on-change-behavior] :as props}]
|
||||
(let [fill? is-layout-child?
|
||||
auto? is-layout-container?]
|
||||
auto? is-layout-container?]
|
||||
|
||||
[:div.btn-wrapper
|
||||
[:div.layout-behavior.horizontal
|
||||
[:button.behavior-btn.tooltip.tooltip-bottom
|
||||
{:alt "Fix width"
|
||||
:class (dom/classnames :activated (= layout-h-behavior :fix))
|
||||
:class (dom/classnames :active (= layout-item-h-sizing :fix))
|
||||
:on-click #(on-change-behavior :h :fix)}
|
||||
i/auto-fix-layout]
|
||||
(when fill?
|
||||
[:button.behavior-btn.tooltip.tooltip-bottom
|
||||
{:alt "Width 100%"
|
||||
:class (dom/classnames :activated (= layout-h-behavior :fill))
|
||||
:class (dom/classnames :active (= layout-item-h-sizing :fill))
|
||||
:on-click #(on-change-behavior :h :fill)}
|
||||
i/auto-fill])
|
||||
(when auto?
|
||||
[:button.behavior-btn.tooltip.tooltip-bottom
|
||||
{:alt "Fit content"
|
||||
:class (dom/classnames :activated (= layout-v-behavior :auto))
|
||||
:class (dom/classnames :active (= layout-item-h-sizing :auto))
|
||||
:on-click #(on-change-behavior :h :auto)}
|
||||
i/auto-hug])]
|
||||
|
||||
[:div.layout-behavior
|
||||
[:button.behavior-btn.tooltip.tooltip-bottom
|
||||
{:alt "Fix height"
|
||||
:class (dom/classnames :activated (= layout-v-behavior :fix))
|
||||
:class (dom/classnames :active (= layout-item-v-sizing :fix))
|
||||
:on-click #(on-change-behavior :v :fix)}
|
||||
i/auto-fix-layout]
|
||||
(when fill?
|
||||
[:button.behavior-btn.tooltip.tooltip-bottom
|
||||
{:alt "Height 100%"
|
||||
:class (dom/classnames :activated (= layout-v-behavior :fill))
|
||||
:class (dom/classnames :active (= layout-item-v-sizing :fill))
|
||||
:on-click #(on-change-behavior :v :fill)}
|
||||
i/auto-fill])
|
||||
(when auto?
|
||||
[:button.behavior-btn.tooltip.tooltip-bottom-left
|
||||
{:alt "Fit content"
|
||||
:class (dom/classnames :activated (= layout-v-behavior :auto))
|
||||
:class (dom/classnames :active (= layout-item-v-sizing :auto))
|
||||
:on-click #(on-change-behavior :v :auto)}
|
||||
i/auto-hug])]]))
|
||||
|
||||
|
||||
(mf/defc align-self-row
|
||||
[{:keys [is-col? align-self set-align-self] :as props}]
|
||||
(let [dir-v [:start :center :end :strech :baseline]]
|
||||
(let [dir-v [:start :center :end #_:stretch #_:baseline]]
|
||||
[:div.align-self-style
|
||||
(for [align dir-v]
|
||||
[:button.align-self.tooltip.tooltip-bottom
|
||||
{:class (dom/classnames :active (= align-self align)
|
||||
:tooltip-bottom-left (not= align :start)
|
||||
:tooltip-bottom (= align :start))
|
||||
:alt (dm/str "Align self " (d/name align)) ;; TODO añadir lineas de texto a tradus
|
||||
:alt (dm/str "Align self " (d/name align))
|
||||
:on-click #(set-align-self align)
|
||||
:key (str "align-self" align)}
|
||||
(get-layout-flex-icon :align-self align is-col?)])]))
|
||||
|
||||
(mf/defc layout-item-menu
|
||||
{::mf/wrap [#(mf/memo' % (mf/check-props ["ids" "values" "type"]))]}
|
||||
[{:keys [ids _type values is-layout-child? is-layout-container?] :as props}]
|
||||
[{:keys [ids values is-layout-child? is-layout-container?] :as props}]
|
||||
|
||||
(let [open? (mf/use-state false)
|
||||
toggle-open (fn [] (swap! open? not))
|
||||
|
||||
selection-parents-ref (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids))
|
||||
selection-parents (mf/deref selection-parents-ref)
|
||||
|
||||
change-margin-style
|
||||
(fn [type]
|
||||
(st/emit! (dwsl/update-layout-child ids {:layout-margin-type type})))
|
||||
(st/emit! (dwsl/update-layout-child ids {:layout-item-margin-type type})))
|
||||
|
||||
align-self (:layout-align-self values)
|
||||
align-self (:layout-item-align-self values)
|
||||
set-align-self (fn [value]
|
||||
(st/emit! (dwsl/update-layout-child ids {:layout-align-self value})))
|
||||
(if (= align-self value)
|
||||
(st/emit! (dwsl/update-layout-child ids {:layout-item-align-self nil}))
|
||||
(st/emit! (dwsl/update-layout-child ids {:layout-item-align-self value}))))
|
||||
|
||||
saved-dir (:layout-flex-dir values)
|
||||
is-col? (or (= :column saved-dir) (= :reverse-column saved-dir))
|
||||
is-col? (every? ctl/col? selection-parents)
|
||||
|
||||
on-margin-change
|
||||
(fn [type val]
|
||||
(if (= type :simple)
|
||||
(st/emit! (dwsl/update-layout-child ids {:layout-margin {:m1 val :m2 val :m3 val :m4 val}}))
|
||||
(st/emit! (dwsl/update-layout-child ids {:layout-margin {type val}}))))
|
||||
(st/emit! (dwsl/update-layout-child ids {:layout-item-margin {:m1 val :m2 val :m3 val :m4 val}}))
|
||||
(st/emit! (dwsl/update-layout-child ids {:layout-item-margin {type val}}))))
|
||||
|
||||
on-change-behavior
|
||||
(fn [dir value]
|
||||
(if (= dir :h)
|
||||
(st/emit! (dwsl/update-layout-child ids {:layout-h-behavior value}))
|
||||
(st/emit! (dwsl/update-layout-child ids {:layout-v-behavior value}))))
|
||||
(st/emit! (dwsl/update-layout-child ids {:layout-item-h-sizing value}))
|
||||
(st/emit! (dwsl/update-layout-child ids {:layout-item-v-sizing value}))))
|
||||
|
||||
on-size-change
|
||||
(fn [measure value]
|
||||
|
@ -176,14 +183,16 @@
|
|||
[:div.row-title "Sizing"]
|
||||
[:& element-behavior {:is-layout-child? is-layout-child?
|
||||
:is-layout-container? is-layout-container?
|
||||
:layout-v-behavior (or (:layout-v-behavior values) :fix)
|
||||
:layout-h-behavior (or (:layout-h-behavior values) :fix)
|
||||
:layout-item-v-sizing (or (:layout-item-v-sizing values) :fix)
|
||||
:layout-item-h-sizing (or (:layout-item-h-sizing values) :fix)
|
||||
:on-change-behavior on-change-behavior}]]
|
||||
|
||||
|
||||
[:& margin-section {:values values
|
||||
:change-margin-style change-margin-style
|
||||
:on-margin-change on-margin-change}]
|
||||
|
||||
(when is-layout-child?
|
||||
[:& margin-section {:values values
|
||||
:change-margin-style change-margin-style
|
||||
:on-margin-change on-margin-change}])
|
||||
|
||||
[:div.advanced-ops-container
|
||||
[:button.advanced-ops.toltip.tooltip-bottom
|
||||
{:on-click toggle-open
|
||||
|
@ -193,21 +202,22 @@
|
|||
|
||||
(when @open?
|
||||
[:div.advanced-ops-body
|
||||
[:div.layout-row
|
||||
[:div.direction-wrap.row-title "Align"]
|
||||
[:div.btn-wrapper
|
||||
[:& align-self-row {:is-col? is-col?
|
||||
:align-self align-self
|
||||
:set-align-self set-align-self}]]]
|
||||
(when is-layout-child?
|
||||
[:div.layout-row
|
||||
[:div.direction-wrap.row-title "Align"]
|
||||
[:div.btn-wrapper
|
||||
[:& align-self-row {:is-col? is-col?
|
||||
:align-self align-self
|
||||
:set-align-self set-align-self}]]])
|
||||
[:div.input-wrapper
|
||||
(for [item [:layout-max-h :layout-min-h :layout-max-w :layout-min-w]]
|
||||
(for [item [:layout-item-max-h :layout-item-min-h :layout-item-max-w :layout-item-min-w]]
|
||||
[:div.tooltip.tooltip-bottom
|
||||
{:key (d/name item)
|
||||
:alt (tr (dm/str "workspace.options.layout-item.title." (d/name item)))
|
||||
:class (dom/classnames "maxH" (= item :layout-max-h)
|
||||
"minH" (= item :layout-min-h)
|
||||
"maxW" (= item :layout-max-w)
|
||||
"minW" (= item :layout-min-w))}
|
||||
:class (dom/classnames "maxH" (= item :layout-item-max-h)
|
||||
"minH" (= item :layout-item-min-h)
|
||||
"maxW" (= item :layout-item-max-w)
|
||||
"minW" (= item :layout-item-min-w))}
|
||||
[:div.input-element
|
||||
{:alt (tr (dm/str "workspace.options.layout-item." (d/name item)))}
|
||||
[:> numeric-input
|
||||
|
@ -217,6 +227,5 @@
|
|||
:placeholder "--"
|
||||
:on-click #(dom/select-target %)
|
||||
:on-change (partial on-size-change item)
|
||||
;; :value (get values item)
|
||||
:value 100}]]])]])]]
|
||||
))
|
||||
:value (get values item)
|
||||
:nillable true}]]])]])]]))
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.common.types.shape.radius :as ctsr]
|
||||
[app.main.constants :refer [size-presets]]
|
||||
[app.main.data.workspace :as udw]
|
||||
|
@ -79,11 +80,21 @@
|
|||
[shape])
|
||||
frames (map #(deref (refs/object-by-id (:frame-id %))) old-shapes)
|
||||
|
||||
selection-parents-ref (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids))
|
||||
selection-parents (mf/deref selection-parents-ref)
|
||||
|
||||
flex-child? (->> selection-parents (some ctl/layout?))
|
||||
|
||||
flex-container? (ctl/layout? shape)
|
||||
flex-auto-width? (ctl/auto-width? shape)
|
||||
flex-fill-width? (ctl/fill-width? shape)
|
||||
flex-auto-height? (ctl/auto-height? shape)
|
||||
flex-fill-height? (ctl/fill-height? shape)
|
||||
|
||||
;; To show interactively the measures while the user is manipulating
|
||||
;; the shape with the mouse, generate a copy of the shapes applying
|
||||
;; the transient transformations.
|
||||
shapes (as-> old-shapes $
|
||||
(map gsh/transform-shape $)
|
||||
(map gsh/translate-to-frame $ frames))
|
||||
|
||||
;; For rotated or stretched shapes, the origin point we show in the menu
|
||||
|
@ -296,6 +307,7 @@
|
|||
:placeholder "--"
|
||||
:on-click select-all
|
||||
:on-change on-width-change
|
||||
:disabled (and (or flex-child? flex-container?) (or flex-auto-width? flex-fill-width?))
|
||||
:value (:width values)}]]
|
||||
|
||||
[:div.input-element.height {:title (tr "workspace.options.height")}
|
||||
|
@ -304,6 +316,7 @@
|
|||
:placeholder "--"
|
||||
:on-click select-all
|
||||
:on-change on-height-change
|
||||
:disabled (and (or flex-child? flex-container?) (or flex-auto-height? flex-fill-height?))
|
||||
:value (:height values)}]]
|
||||
|
||||
[:div.lock-size {:class (dom/classnames
|
||||
|
@ -323,11 +336,13 @@
|
|||
:placeholder "--"
|
||||
:on-click select-all
|
||||
:on-change on-pos-x-change
|
||||
:disabled flex-child?
|
||||
:value (:x values)}]]
|
||||
[:div.input-element.Yaxis {:title (tr "workspace.options.y")}
|
||||
[:> numeric-input {:no-validate true
|
||||
:placeholder "--"
|
||||
:on-click select-all
|
||||
:disabled flex-child?
|
||||
:on-change on-pos-y-change
|
||||
:value (:y values)}]]])
|
||||
|
||||
|
@ -441,5 +456,3 @@
|
|||
(tr "workspace.options.show-in-viewer")]])
|
||||
|
||||
]]]))
|
||||
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
(ns app.main.ui.workspace.sidebar.options.rows.stroke-row
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.main.ui.components.dropdown :refer [dropdown]]
|
||||
[app.main.ui.components.numeric-input :refer [numeric-input]]
|
||||
[app.main.ui.hooks :as h]
|
||||
|
@ -132,9 +133,10 @@
|
|||
:on-close (close-caps-select start-caps-state)}
|
||||
[:ul.dropdown.cap-select-dropdown {:style {:top (:top @start-caps-state)
|
||||
:left (:left @start-caps-state)}}
|
||||
(for [[value label separator] (stroke-cap-names)]
|
||||
(for [[idx [value label separator]] (d/enumerate (stroke-cap-names))]
|
||||
(let [img (value->img value)]
|
||||
[:li {:class (dom/classnames :separator separator)
|
||||
[:li {:key (dm/str "start-cap-" idx)
|
||||
:class (dom/classnames :separator separator)
|
||||
:on-click #(on-stroke-cap-start-change index value)}
|
||||
(when img [:img {:src (value->img value)}])
|
||||
label]))]]
|
||||
|
@ -151,9 +153,10 @@
|
|||
:on-close (close-caps-select end-caps-state)}
|
||||
[:ul.dropdown.cap-select-dropdown {:style {:top (:top @end-caps-state)
|
||||
:left (:left @end-caps-state)}}
|
||||
(for [[value label separator] (stroke-cap-names)]
|
||||
(for [[idx [value label separator]] (d/enumerate (stroke-cap-names))]
|
||||
(let [img (value->img value)]
|
||||
[:li {:class (dom/classnames :separator separator)
|
||||
[:li {:key (dm/str "end-cap-" idx)
|
||||
:class (dom/classnames :separator separator)
|
||||
:on-click #(on-stroke-cap-end-change index value)}
|
||||
(when img [:img {:src (value->img value)}])
|
||||
label]))]]])]))
|
||||
|
|
|
@ -41,8 +41,10 @@
|
|||
:values layout-item-values
|
||||
:is-layout-child? true
|
||||
:shape shape}])
|
||||
[:& constraints-menu {:ids ids
|
||||
:values constraint-values}]
|
||||
|
||||
(when (not is-layout-child?)
|
||||
[:& constraints-menu {:ids ids
|
||||
:values constraint-values}])
|
||||
[:& layer-menu {:ids ids
|
||||
:type type
|
||||
:values layer-values}]
|
||||
|
|
|
@ -43,8 +43,9 @@
|
|||
:is-layout-child? true
|
||||
:is-layout-container? false
|
||||
:shape shape}])
|
||||
[:& constraints-menu {:ids ids
|
||||
:values constraint-values}]
|
||||
(when (not is-layout-child?)
|
||||
[:& constraints-menu {:ids ids
|
||||
:values constraint-values}])
|
||||
[:& layer-menu {:ids ids
|
||||
:type type
|
||||
:values layer-values}]
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
(ns app.main.ui.workspace.sidebar.options.shapes.frame
|
||||
(:require
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.main.features :as features]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]]
|
||||
|
@ -35,26 +36,27 @@
|
|||
layout-item-values (select-keys shape layout-item-attrs)
|
||||
|
||||
is-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids))
|
||||
is-layout-child? (mf/deref is-layout-child-ref)]
|
||||
is-layout-child? (mf/deref is-layout-child-ref)
|
||||
is-layout-container? (ctl/layout? shape)]
|
||||
[:*
|
||||
[:& measures-menu {:ids [(:id shape)]
|
||||
:values measure-values
|
||||
:type type
|
||||
:shape shape}]
|
||||
[:& constraints-menu {:ids ids
|
||||
:values constraint-values}]
|
||||
(when layout-active?
|
||||
[:*
|
||||
[:& layout-container-menu {:type type :ids [(:id shape)] :values layout-container-values}]
|
||||
(when (not is-layout-child?)
|
||||
[:& constraints-menu {:ids ids
|
||||
:values constraint-values}])
|
||||
(when (or layout-active? is-layout-container?)
|
||||
[:& layout-container-menu {:type type :ids [(:id shape)] :values layout-container-values}])
|
||||
|
||||
(when (or (:layout shape) is-layout-child?)
|
||||
[:& layout-item-menu
|
||||
{:ids ids
|
||||
:type type
|
||||
:values layout-item-values
|
||||
:is-layout-child? is-layout-child?
|
||||
:is-layout-container? (:layout shape)
|
||||
:shape shape}])])
|
||||
(when (and layout-active? (or is-layout-child? is-layout-container?))
|
||||
[:& layout-item-menu
|
||||
{:ids ids
|
||||
:type type
|
||||
:values layout-item-values
|
||||
:is-layout-child? is-layout-child?
|
||||
:is-layout-container? is-layout-container?
|
||||
:shape shape}])
|
||||
|
||||
[:& layer-menu {:ids ids
|
||||
:type type
|
||||
|
|
|
@ -62,7 +62,8 @@
|
|||
:is-layout-container? false
|
||||
:values layout-item-values}])
|
||||
|
||||
[:& constraints-menu {:ids constraint-ids :values constraint-values}]
|
||||
(when (not is-layout-child?)
|
||||
[:& constraints-menu {:ids constraint-ids :values constraint-values}])
|
||||
[:& layer-menu {:type type :ids layer-ids :values layer-values}]
|
||||
|
||||
(when-not (empty? fill-ids)
|
||||
|
|
|
@ -44,8 +44,9 @@
|
|||
:is-layout-child? true
|
||||
:shape shape}])
|
||||
|
||||
[:& constraints-menu {:ids ids
|
||||
:values constraint-values}]
|
||||
(when (not is-layout-child?)
|
||||
[:& constraints-menu {:ids ids
|
||||
:values constraint-values}])
|
||||
|
||||
[:& layer-menu {:ids ids
|
||||
:type type
|
||||
|
|
|
@ -34,116 +34,134 @@
|
|||
;; - text: read it from all the content nodes, and then merging it.
|
||||
(def type->read-mode
|
||||
{:frame
|
||||
{:measure :shape
|
||||
:layer :shape
|
||||
:constraint :shape
|
||||
:fill :shape
|
||||
:shadow :children
|
||||
:blur :children
|
||||
:stroke :shape
|
||||
:text :children
|
||||
:exports :shape}
|
||||
{:measure :shape
|
||||
:layer :shape
|
||||
:constraint :shape
|
||||
:fill :shape
|
||||
:shadow :children
|
||||
:blur :children
|
||||
:stroke :shape
|
||||
:text :children
|
||||
:exports :shape
|
||||
:layout-container :shape
|
||||
:layout-item :shape}
|
||||
|
||||
:group
|
||||
{:measure :shape
|
||||
:layer :shape
|
||||
:constraint :shape
|
||||
:fill :children
|
||||
:shadow :shape
|
||||
:blur :shape
|
||||
:stroke :children
|
||||
:text :children
|
||||
:exports :shape}
|
||||
{:measure :shape
|
||||
:layer :shape
|
||||
:constraint :shape
|
||||
:fill :children
|
||||
:shadow :shape
|
||||
:blur :shape
|
||||
:stroke :children
|
||||
:text :children
|
||||
:exports :shape
|
||||
:layout-container :ignore
|
||||
:layout-item :shape}
|
||||
|
||||
:path
|
||||
{:measure :shape
|
||||
:layer :shape
|
||||
:constraint :shape
|
||||
:fill :shape
|
||||
:shadow :shape
|
||||
:blur :shape
|
||||
:stroke :shape
|
||||
:text :ignore
|
||||
:exports :shape}
|
||||
{:measure :shape
|
||||
:layer :shape
|
||||
:constraint :shape
|
||||
:fill :shape
|
||||
:shadow :shape
|
||||
:blur :shape
|
||||
:stroke :shape
|
||||
:text :ignore
|
||||
:exports :shape
|
||||
:layout-container :ignore
|
||||
:layout-item :shape}
|
||||
|
||||
:text
|
||||
{:measure :shape
|
||||
:layer :shape
|
||||
:constraint :shape
|
||||
:fill :text
|
||||
:shadow :shape
|
||||
:blur :shape
|
||||
:stroke :shape
|
||||
:text :text
|
||||
:exports :shape}
|
||||
{:measure :shape
|
||||
:layer :shape
|
||||
:constraint :shape
|
||||
:fill :text
|
||||
:shadow :shape
|
||||
:blur :shape
|
||||
:stroke :shape
|
||||
:text :text
|
||||
:exports :shape
|
||||
:layout-container :ignore
|
||||
:layout-item :shape}
|
||||
|
||||
:image
|
||||
{:measure :shape
|
||||
:layer :shape
|
||||
:constraint :shape
|
||||
:fill :ignore
|
||||
:shadow :shape
|
||||
:blur :shape
|
||||
:stroke :ignore
|
||||
:text :ignore
|
||||
:exports :shape}
|
||||
{:measure :shape
|
||||
:layer :shape
|
||||
:constraint :shape
|
||||
:fill :ignore
|
||||
:shadow :shape
|
||||
:blur :shape
|
||||
:stroke :ignore
|
||||
:text :ignore
|
||||
:exports :shape
|
||||
:layout-container :ignore
|
||||
:layout-item :shape}
|
||||
|
||||
:rect
|
||||
{:measure :shape
|
||||
:layer :shape
|
||||
:constraint :shape
|
||||
:fill :shape
|
||||
:shadow :shape
|
||||
:blur :shape
|
||||
:stroke :shape
|
||||
:text :ignore
|
||||
:exports :shape}
|
||||
{:measure :shape
|
||||
:layer :shape
|
||||
:constraint :shape
|
||||
:fill :shape
|
||||
:shadow :shape
|
||||
:blur :shape
|
||||
:stroke :shape
|
||||
:text :ignore
|
||||
:exports :shape
|
||||
:layout-container :ignore
|
||||
:layout-item :shape}
|
||||
|
||||
:circle
|
||||
{:measure :shape
|
||||
:layer :shape
|
||||
:constraint :shape
|
||||
:fill :shape
|
||||
:shadow :shape
|
||||
:blur :shape
|
||||
:stroke :shape
|
||||
:text :ignore
|
||||
:exports :shape}
|
||||
{:measure :shape
|
||||
:layer :shape
|
||||
:constraint :shape
|
||||
:fill :shape
|
||||
:shadow :shape
|
||||
:blur :shape
|
||||
:stroke :shape
|
||||
:text :ignore
|
||||
:exports :shape
|
||||
:layout-container :ignore
|
||||
:layout-item :shape}
|
||||
|
||||
:svg-raw
|
||||
{:measure :shape
|
||||
:layer :shape
|
||||
:constraint :shape
|
||||
:fill :shape
|
||||
:shadow :shape
|
||||
:blur :shape
|
||||
:stroke :shape
|
||||
:text :ignore
|
||||
:exports :shape}
|
||||
{:measure :shape
|
||||
:layer :shape
|
||||
:constraint :shape
|
||||
:fill :shape
|
||||
:shadow :shape
|
||||
:blur :shape
|
||||
:stroke :shape
|
||||
:text :ignore
|
||||
:exports :shape
|
||||
:layout-container :ignore
|
||||
:layout-item :shape}
|
||||
|
||||
:bool
|
||||
{:measure :shape
|
||||
:layer :shape
|
||||
:constraint :shape
|
||||
:fill :shape
|
||||
:shadow :shape
|
||||
:blur :shape
|
||||
:stroke :shape
|
||||
:text :ignore
|
||||
:exports :shape}})
|
||||
{:measure :shape
|
||||
:layer :shape
|
||||
:constraint :shape
|
||||
:fill :shape
|
||||
:shadow :shape
|
||||
:blur :shape
|
||||
:stroke :shape
|
||||
:text :ignore
|
||||
:exports :shape
|
||||
:layout-container :ignore
|
||||
:layout-item :shape}})
|
||||
|
||||
(def group->attrs
|
||||
{:measure measure-attrs
|
||||
:layer layer-attrs
|
||||
:constraint constraint-attrs
|
||||
:fill fill-attrs
|
||||
:shadow shadow-attrs
|
||||
:blur blur-attrs
|
||||
:stroke stroke-attrs
|
||||
:text ot/attrs
|
||||
:exports exports-attrs
|
||||
:layout layout-container-flex-attrs
|
||||
:layout-item layout-item-attrs})
|
||||
{:measure measure-attrs
|
||||
:layer layer-attrs
|
||||
:constraint constraint-attrs
|
||||
:fill fill-attrs
|
||||
:shadow shadow-attrs
|
||||
:blur blur-attrs
|
||||
:stroke stroke-attrs
|
||||
:text ot/attrs
|
||||
:exports exports-attrs
|
||||
:layout-container layout-container-flex-attrs
|
||||
:layout-item layout-item-attrs})
|
||||
|
||||
(def shadow-keys [:style :color :offset-x :offset-y :blur :spread])
|
||||
|
||||
|
@ -235,6 +253,10 @@
|
|||
[props]
|
||||
(let [shapes (unchecked-get props "shapes")
|
||||
shapes-with-children (unchecked-get props "shapes-with-children")
|
||||
|
||||
workspace-modifiers (mf/deref refs/workspace-modifiers)
|
||||
shapes (map #(gsh/transform-shape % (get-in workspace-modifiers [(:id %) :modifiers])) shapes)
|
||||
|
||||
page-id (unchecked-get props "page-id")
|
||||
file-id (unchecked-get props "file-id")
|
||||
shared-libs (unchecked-get props "shared-libs")
|
||||
|
@ -254,20 +276,20 @@
|
|||
is-layout-child? (mf/deref is-layout-child-ref)
|
||||
|
||||
has-text? (contains? all-types :text)
|
||||
|
||||
|
||||
[measure-ids measure-values] (get-attrs shapes objects :measure)
|
||||
|
||||
|
||||
[layer-ids layer-values
|
||||
constraint-ids constraint-values
|
||||
fill-ids fill-values
|
||||
shadow-ids shadow-values
|
||||
blur-ids blur-values
|
||||
stroke-ids stroke-values
|
||||
text-ids text-values
|
||||
exports-ids exports-values
|
||||
layout-ids layout-container-values
|
||||
layout-item-ids layout-item-values]
|
||||
[layer-ids layer-values
|
||||
constraint-ids constraint-values
|
||||
fill-ids fill-values
|
||||
shadow-ids shadow-values
|
||||
blur-ids blur-values
|
||||
stroke-ids stroke-values
|
||||
text-ids text-values
|
||||
exports-ids exports-values
|
||||
layout-container-ids layout-container-values
|
||||
layout-item-ids layout-item-values]
|
||||
(mf/use-memo
|
||||
(mf/deps objects-no-measures)
|
||||
(fn []
|
||||
|
@ -282,7 +304,7 @@
|
|||
(get-attrs shapes objects-no-measures :stroke)
|
||||
(get-attrs shapes objects-no-measures :text)
|
||||
(get-attrs shapes objects-no-measures :exports)
|
||||
(get-attrs shapes objects-no-measures :layout)
|
||||
(get-attrs shapes objects-no-measures :layout-container)
|
||||
(get-attrs shapes objects-no-measures :layout-item)
|
||||
])))]
|
||||
|
||||
|
@ -291,8 +313,8 @@
|
|||
[:& measures-menu {:type type :all-types all-types :ids measure-ids :values measure-values :shape shapes}])
|
||||
|
||||
(when (:layout layout-container-values)
|
||||
[:& layout-container-menu {:type type :ids layout-ids :values layout-container-values}])
|
||||
|
||||
[:& layout-container-menu {:type type :ids layout-container-ids :values layout-container-values}])
|
||||
|
||||
(when is-layout-child?
|
||||
[:& layout-item-menu
|
||||
{:type type
|
||||
|
@ -301,7 +323,7 @@
|
|||
:is-layout-container? true
|
||||
:values layout-item-values}])
|
||||
|
||||
(when-not (empty? constraint-ids)
|
||||
(when-not (or (empty? constraint-ids) is-layout-child?)
|
||||
[:& constraints-menu {:ids constraint-ids :values constraint-values}])
|
||||
|
||||
(when-not (empty? layer-ids)
|
||||
|
|
|
@ -43,8 +43,9 @@
|
|||
:is-layout-child? true
|
||||
:is-layout-container? false
|
||||
:shape shape}])
|
||||
[:& constraints-menu {:ids ids
|
||||
:values constraint-values}]
|
||||
(when (not is-layout-child?)
|
||||
[:& constraints-menu {:ids ids
|
||||
:values constraint-values}])
|
||||
[:& layer-menu {:ids ids
|
||||
:type type
|
||||
:values layer-values}]
|
||||
|
|
|
@ -44,8 +44,10 @@
|
|||
:values layout-item-values
|
||||
:is-layout-child? true
|
||||
:shape shape}])
|
||||
[:& constraints-menu {:ids ids
|
||||
:values constraint-values}]
|
||||
|
||||
(when (not is-layout-child?)
|
||||
[:& constraints-menu {:ids ids
|
||||
:values constraint-values}])
|
||||
|
||||
[:& layer-menu {:ids ids
|
||||
:type type
|
||||
|
|
|
@ -120,8 +120,9 @@
|
|||
:is-layout-child? true
|
||||
:shape shape}])
|
||||
|
||||
[:& constraints-menu {:ids ids
|
||||
:values constraint-values}]
|
||||
(when (not is-layout-child?)
|
||||
[:& constraints-menu {:ids ids
|
||||
:values constraint-values}])
|
||||
|
||||
[:& fill-menu {:ids ids
|
||||
:type type
|
||||
|
|
|
@ -78,9 +78,11 @@
|
|||
:values layout-item-values
|
||||
:is-layout-child? true
|
||||
:shape shape}])
|
||||
[:& constraints-menu
|
||||
{:ids ids
|
||||
:values (select-keys shape constraint-attrs)}]
|
||||
|
||||
(when (not is-layout-child?)
|
||||
[:& constraints-menu
|
||||
{:ids ids
|
||||
:values (select-keys shape constraint-attrs)}])
|
||||
|
||||
[:& layer-menu {:ids ids
|
||||
:type type
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
[app.main.ui.workspace.shapes.text.viewport-texts-html :as stvh]
|
||||
[app.main.ui.workspace.viewport.actions :as actions]
|
||||
[app.main.ui.workspace.viewport.comments :as comments]
|
||||
[app.main.ui.workspace.viewport.debug :as wvd]
|
||||
[app.main.ui.workspace.viewport.drawarea :as drawarea]
|
||||
[app.main.ui.workspace.viewport.frame-grid :as frame-grid]
|
||||
[app.main.ui.workspace.viewport.gradients :as gradients]
|
||||
|
@ -79,8 +80,7 @@
|
|||
modifiers (mf/deref refs/workspace-modifiers)
|
||||
|
||||
objects-modified (mf/with-memo [base-objects modifiers]
|
||||
(gsh/merge-modifiers base-objects modifiers))
|
||||
|
||||
(gsh/apply-objects-modifiers base-objects modifiers))
|
||||
background (get options :background clr/canvas)
|
||||
|
||||
;; STATE
|
||||
|
@ -91,6 +91,7 @@
|
|||
hover-ids (mf/use-state nil)
|
||||
hover (mf/use-state nil)
|
||||
hover-disabled? (mf/use-state false)
|
||||
hover-top-frame-id (mf/use-state nil)
|
||||
frame-hover (mf/use-state nil)
|
||||
active-frames (mf/use-state #{})
|
||||
|
||||
|
@ -187,7 +188,7 @@
|
|||
(hooks/setup-viewport-size viewport-ref)
|
||||
(hooks/setup-cursor cursor alt? mod? space? panning drawing-tool drawing-path? node-editing?)
|
||||
(hooks/setup-keyboard alt? mod? space?)
|
||||
(hooks/setup-hover-shapes page-id move-stream base-objects transform selected mod? hover hover-ids @hover-disabled? focus zoom)
|
||||
(hooks/setup-hover-shapes page-id move-stream base-objects transform selected mod? hover hover-ids hover-top-frame-id @hover-disabled? focus zoom)
|
||||
(hooks/setup-viewport-modifiers modifiers base-objects)
|
||||
(hooks/setup-shortcuts node-editing? drawing-path?)
|
||||
(hooks/setup-active-frames base-objects hover-ids selected active-frames zoom transform vbox)
|
||||
|
@ -285,7 +286,7 @@
|
|||
|
||||
(when show-frame-outline?
|
||||
[:& outline/shape-outlines
|
||||
{:objects base-objects
|
||||
{:objects objects-modified
|
||||
:hover #{(->> @hover-ids
|
||||
(filter #(cph/frame-shape? (get base-objects %)))
|
||||
(remove selected)
|
||||
|
@ -336,10 +337,9 @@
|
|||
(when show-prototypes?
|
||||
[:& widgets/frame-flows
|
||||
{:flows (:flows options)
|
||||
:objects base-objects
|
||||
:objects objects-modified
|
||||
:selected selected
|
||||
:zoom zoom
|
||||
:modifiers modifiers
|
||||
:on-frame-enter on-frame-enter
|
||||
:on-frame-leave on-frame-leave
|
||||
:on-frame-select on-frame-select}])
|
||||
|
@ -348,8 +348,7 @@
|
|||
[:& drawarea/draw-area
|
||||
{:shape drawing-obj
|
||||
:zoom zoom
|
||||
:tool drawing-tool
|
||||
:modifiers modifiers}])
|
||||
:tool drawing-tool}])
|
||||
|
||||
(when show-grids?
|
||||
[:& frame-grid/frame-grid
|
||||
|
@ -371,9 +370,8 @@
|
|||
:zoom zoom
|
||||
:page-id page-id
|
||||
:selected selected
|
||||
:objects base-objects
|
||||
:focus focus
|
||||
:modifiers modifiers}])
|
||||
:objects objects-modified
|
||||
:focus focus}])
|
||||
|
||||
(when show-snap-distance?
|
||||
[:& snap-distances/snap-distances
|
||||
|
@ -416,9 +414,21 @@
|
|||
{:zoom zoom
|
||||
:vbox vbox
|
||||
:hover-frame frame-parent
|
||||
:modifiers modifiers
|
||||
:disabled-guides? disabled-guides?}])
|
||||
|
||||
;; DEBUG LAYOUT DROP-ZONES
|
||||
(when (debug? :layout-drop-zones)
|
||||
[:& wvd/debug-drop-zones {:selected-shapes selected-shapes
|
||||
:objects objects-modified
|
||||
:hover-top-frame-id @hover-top-frame-id
|
||||
:zoom zoom}])
|
||||
|
||||
(when (debug? :layout-lines)
|
||||
[:& wvd/debug-layout {:selected-shapes selected-shapes
|
||||
:objects objects-modified
|
||||
:hover-top-frame-id @hover-top-frame-id
|
||||
:zoom zoom}])
|
||||
|
||||
(when show-selection-handlers?
|
||||
[:g.selection-handlers {:clipPath "url(#clip-handlers)"}
|
||||
[:defs
|
||||
|
|
137
frontend/src/app/main/ui/workspace/viewport/debug.cljs
Normal file
137
frontend/src/app/main/ui/workspace/viewport/debug.cljs
Normal file
|
@ -0,0 +1,137 @@
|
|||
;; 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.main.ui.workspace.viewport.debug
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.geom.shapes.flex-layout :as gsl]
|
||||
[app.common.geom.shapes.points :as gpo]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[cuerdas.core :as str]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
;; Helper to debug the bounds when set the "hug" content property
|
||||
#_(mf/defc debug-layout
|
||||
"Debug component to show the auto-layout drop areas"
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
|
||||
(let [objects (unchecked-get props "objects")
|
||||
selected-shapes (unchecked-get props "selected-shapes")
|
||||
hover-top-frame-id (unchecked-get props "hover-top-frame-id")
|
||||
|
||||
selected-frame
|
||||
(when (and (= (count selected-shapes) 1) (= :frame (-> selected-shapes first :type)))
|
||||
(first selected-shapes))
|
||||
|
||||
shape (or selected-frame (get objects hover-top-frame-id))]
|
||||
|
||||
(when (and shape (:layout shape))
|
||||
(let [children (cph/get-immediate-children objects (:id shape))
|
||||
layout-data (gsl/calc-layout-data shape children)
|
||||
|
||||
{pad-top :p1 pad-right :p2 pad-bottom :p3 pad-left :p4} (:layout-padding shape)
|
||||
pad-top (or pad-top 0)
|
||||
pad-right (or pad-right 0)
|
||||
pad-bottom (or pad-bottom 0)
|
||||
pad-left (or pad-left 0)
|
||||
|
||||
layout-bounds (gsl/layout-content-bounds shape children)]
|
||||
[:g.debug-layout {:pointer-events "none"
|
||||
:transform (gsh/transform-str shape)}
|
||||
|
||||
|
||||
[:rect {:x (:x layout-bounds)
|
||||
:y (:y layout-bounds)
|
||||
:width (:width layout-bounds)
|
||||
:height (:height layout-bounds)
|
||||
:style {:stroke "red"
|
||||
:fill "none"}}]]))))
|
||||
|
||||
(mf/defc debug-layout
|
||||
"Debug component to show the auto-layout drop areas"
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
|
||||
(let [objects (unchecked-get props "objects")
|
||||
zoom (unchecked-get props "zoom")
|
||||
selected-shapes (unchecked-get props "selected-shapes")
|
||||
hover-top-frame-id (unchecked-get props "hover-top-frame-id")
|
||||
|
||||
selected-frame
|
||||
(when (and (= (count selected-shapes) 1) (= :frame (-> selected-shapes first :type)))
|
||||
(first selected-shapes))
|
||||
|
||||
shape (or selected-frame (get objects hover-top-frame-id))]
|
||||
|
||||
(when (and shape (ctl/layout? shape))
|
||||
(let [row? (ctl/row? shape)
|
||||
col? (ctl/col? shape)
|
||||
|
||||
children (cph/get-immediate-children objects (:id shape))
|
||||
layout-data (gsl/calc-layout-data shape children)
|
||||
|
||||
layout-bounds (:layout-bounds layout-data)
|
||||
xv #(gpo/start-hv layout-bounds %)
|
||||
yv #(gpo/start-vv layout-bounds %)]
|
||||
[:g.debug-layout {:pointer-events "none"}
|
||||
(for [[idx {:keys [start-p line-width line-height layout-gap-row layout-gap-col num-children]}] (d/enumerate (:layout-lines layout-data))]
|
||||
(let [line-width (if row? (+ line-width (* (dec num-children) layout-gap-row)) line-width)
|
||||
line-height (if col? (+ line-height (* (dec num-children) layout-gap-col)) line-height)
|
||||
|
||||
points [start-p
|
||||
(-> start-p (gpt/add (xv line-width)))
|
||||
(-> start-p (gpt/add (xv line-width)) (gpt/add (yv line-height)))
|
||||
(-> start-p (gpt/add (yv line-height)))
|
||||
]]
|
||||
[:g.layout-line {:key (dm/str "line-" idx)}
|
||||
[: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))}}]]))]))))
|
||||
|
||||
(mf/defc debug-drop-zones
|
||||
"Debug component to show the auto-layout drop areas"
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
|
||||
(let [objects (unchecked-get props "objects")
|
||||
zoom (unchecked-get props "objects")
|
||||
selected-shapes (unchecked-get props "selected-shapes")
|
||||
hover-top-frame-id (unchecked-get props "hover-top-frame-id")
|
||||
|
||||
selected-frame
|
||||
(when (and (= (count selected-shapes) 1) (= :frame (-> selected-shapes first :type)))
|
||||
(first selected-shapes))
|
||||
|
||||
shape (or selected-frame (get objects hover-top-frame-id))]
|
||||
|
||||
(when (and shape (:layout shape))
|
||||
(let [children (cph/get-immediate-children objects (:id shape))
|
||||
layout-data (gsl/calc-layout-data shape children)
|
||||
drop-areas (gsl/layout-drop-areas shape layout-data children)]
|
||||
[:g.debug-layout {:pointer-events "none"
|
||||
:transform (gsh/transform-str shape)}
|
||||
(for [[idx drop-area] (d/enumerate drop-areas)]
|
||||
[:g.drop-area {:key (dm/str "drop-area-" idx)}
|
||||
[:rect {:x (:x drop-area)
|
||||
:y (:y drop-area)
|
||||
:width (:width drop-area)
|
||||
:height (:height drop-area)
|
||||
:style {:fill "blue"
|
||||
:fill-opacity 0.3
|
||||
:stroke "red"
|
||||
:stroke-width (/ zoom 1)
|
||||
:stroke-dasharray (dm/str (/ 3 zoom) " " (/ 6 zoom))}}]
|
||||
[:text {:x (:x drop-area)
|
||||
:y (:y drop-area)
|
||||
:width (:width drop-area)
|
||||
:height (:height drop-area)
|
||||
:alignment-baseline "hanging"
|
||||
:fill "black"}
|
||||
(:index drop-area)]])]))))
|
|
@ -7,7 +7,6 @@
|
|||
(ns app.main.ui.workspace.viewport.drawarea
|
||||
"Drawing components."
|
||||
(:require
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.math :as mth]
|
||||
[app.main.ui.shapes.path :refer [path-shape]]
|
||||
[app.main.ui.workspace.shapes :as shapes]
|
||||
|
@ -22,7 +21,7 @@
|
|||
|
||||
[:g.draw-area
|
||||
[:g {:style {:pointer-events "none"}}
|
||||
[:& shapes/shape-wrapper {:shape (gsh/transform-shape shape)}]]
|
||||
[:& shapes/shape-wrapper {:shape shape}]]
|
||||
|
||||
(case tool
|
||||
:path [:& path-editor {:shape shape :zoom zoom}]
|
||||
|
@ -31,7 +30,7 @@
|
|||
|
||||
(mf/defc generic-draw-area
|
||||
[{:keys [shape zoom]}]
|
||||
(let [{:keys [x y width height]} (:selrect (gsh/transform-shape shape))]
|
||||
(let [{:keys [x y width height]} (:selrect shape)]
|
||||
(when (and x y
|
||||
(not (mth/nan? x))
|
||||
(not (mth/nan? y)))
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
(ns app.main.ui.workspace.viewport.frame-grid
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.math :as mth]
|
||||
[app.common.types.shape-tree :as ctst]
|
||||
[app.common.uuid :as uuid]
|
||||
|
@ -138,4 +137,4 @@
|
|||
(or (empty? focus) (contains? focus (:id frame))))
|
||||
[:& grid-display-frame {:key (str "grid-" (:id frame))
|
||||
:zoom zoom
|
||||
:frame (gsh/transform-shape frame)}]))]))
|
||||
:frame frame}]))]))
|
||||
|
|
|
@ -278,7 +278,7 @@
|
|||
frame]} (use-guide handle-change-position get-hover-frame zoom guide)
|
||||
|
||||
base-frame (or frame hover-frame)
|
||||
frame (gsh/transform-shape (merge base-frame frame-modifier))
|
||||
frame (gsh/transform-shape base-frame frame-modifier)
|
||||
|
||||
move-vec (gpt/to-vec (gpt/point (:x base-frame) (:y base-frame))
|
||||
(gpt/point (:x frame) (:y frame)))
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
[app.common.pages :as cp]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.types.shape-tree :as ctt]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.shortcuts :as dsc]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.data.workspace.path.shortcuts :as psc]
|
||||
|
@ -104,7 +105,8 @@
|
|||
(some #(cph/is-parent? objects % group-id))
|
||||
(not))))
|
||||
|
||||
(defn setup-hover-shapes [page-id move-stream objects transform selected mod? hover hover-ids hover-disabled? focus zoom]
|
||||
(defn setup-hover-shapes
|
||||
[page-id move-stream objects transform selected mod? hover hover-ids hover-top-frame-id hover-disabled? focus zoom]
|
||||
(let [;; We use ref so we don't recreate the stream on a change
|
||||
zoom-ref (mf/use-ref zoom)
|
||||
mod-ref (mf/use-ref @mod?)
|
||||
|
@ -143,9 +145,10 @@
|
|||
(rx/map #(deref last-point-ref)))
|
||||
|
||||
(->> move-stream
|
||||
(rx/tap #(reset! last-point-ref %))
|
||||
;; When transforming shapes we stop querying the worker
|
||||
(rx/merge-map query-point)
|
||||
(rx/tap #(reset! last-point-ref %))))))]
|
||||
))))]
|
||||
|
||||
;; Refresh the refs on a value change
|
||||
(mf/use-effect
|
||||
|
@ -213,7 +216,8 @@
|
|||
(first)
|
||||
(get objects))]
|
||||
(reset! hover hover-shape)
|
||||
(reset! hover-ids ids))))))
|
||||
(reset! hover-ids ids)
|
||||
(reset! hover-top-frame-id (ctt/top-nested-frame objects (deref last-point-ref))))))))
|
||||
|
||||
(defn setup-viewport-modifiers
|
||||
[modifiers objects]
|
||||
|
@ -221,7 +225,7 @@
|
|||
(mf/use-memo
|
||||
(mf/deps objects)
|
||||
#(ctt/get-root-shapes-ids objects))
|
||||
modifiers (select-keys modifiers root-frame-ids)]
|
||||
modifiers (select-keys modifiers (conj root-frame-ids uuid/zero))]
|
||||
(sfd/use-dynamic-modifiers objects globals/document modifiers)))
|
||||
|
||||
(defn inside-vbox [vbox objects frame-id]
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.types.shape.interactions :as ctsi]
|
||||
[app.main.data.workspace :as dw]
|
||||
|
@ -254,13 +253,11 @@
|
|||
(mf/defc interactions
|
||||
[{:keys [current-transform objects zoom selected hover-disabled?] :as props}]
|
||||
(let [active-shapes (into []
|
||||
(comp (filter #(seq (:interactions %)))
|
||||
(map gsh/transform-shape))
|
||||
(comp (filter #(seq (:interactions %))))
|
||||
(vals objects))
|
||||
|
||||
selected-shapes (into []
|
||||
(comp (map (d/getf objects))
|
||||
(map gsh/transform-shape))
|
||||
(map (d/getf objects))
|
||||
selected)
|
||||
|
||||
{:keys [editing-interaction-index
|
||||
|
|
|
@ -72,7 +72,7 @@
|
|||
"var(--color-primary)" "var(--color-component-highlight)")]
|
||||
(for [shape shapes]
|
||||
[:& outline {:key (str "outline-" (:id shape))
|
||||
:shape (gsh/transform-shape shape)
|
||||
:shape shape
|
||||
:zoom zoom
|
||||
:color color}])))
|
||||
|
||||
|
|
|
@ -341,7 +341,6 @@
|
|||
(let [shape (mf/use-memo
|
||||
(mf/deps shapes)
|
||||
#(->> shapes
|
||||
(map gsh/transform-shape)
|
||||
(gsh/selection-rect)
|
||||
(cts/setup-shape)))
|
||||
on-resize
|
||||
|
@ -369,7 +368,6 @@
|
|||
(let [shape (mf/use-memo
|
||||
(mf/deps shapes)
|
||||
#(->> shapes
|
||||
(map gsh/transform-shape)
|
||||
(gsh/selection-rect)
|
||||
(cts/setup-shape)))]
|
||||
|
||||
|
@ -384,7 +382,6 @@
|
|||
(mf/defc single-handlers
|
||||
[{:keys [shape zoom color disable-handlers] :as props}]
|
||||
(let [shape-id (:id shape)
|
||||
shape (gsh/transform-shape shape)
|
||||
|
||||
on-resize
|
||||
(fn [current-position _initial-position event]
|
||||
|
@ -408,14 +405,13 @@
|
|||
|
||||
(mf/defc single-selection
|
||||
[{:keys [shape zoom color disable-handlers on-move-selected on-context-menu] :as props}]
|
||||
(let [shape (gsh/transform-shape shape)]
|
||||
[:& controls-selection
|
||||
{:shape shape
|
||||
:zoom zoom
|
||||
:color color
|
||||
:disable-handlers disable-handlers
|
||||
:on-move-selected on-move-selected
|
||||
:on-context-menu on-context-menu}]))
|
||||
[:& controls-selection
|
||||
{:shape shape
|
||||
:zoom zoom
|
||||
:color color
|
||||
:disable-handlers disable-handlers
|
||||
:on-move-selected on-move-selected
|
||||
:on-context-menu on-context-menu}])
|
||||
|
||||
(mf/defc selection-area
|
||||
{::mf/wrap [mf/memo]}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.math :as mth]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.ui.formats :as fmt]
|
||||
[app.main.worker :as uw]
|
||||
|
@ -272,18 +273,20 @@
|
|||
frame-id (-> selected-shapes first :frame-id)
|
||||
frame (mf/deref (refs/object-by-id frame-id))
|
||||
selrect (gsh/selection-rect selected-shapes)]
|
||||
[:g.distance
|
||||
[:& shape-distance
|
||||
{:selrect selrect
|
||||
:page-id page-id
|
||||
:frame frame
|
||||
:zoom zoom
|
||||
:coord :x
|
||||
:selected selected}]
|
||||
[:& shape-distance
|
||||
{:selrect selrect
|
||||
:page-id page-id
|
||||
:frame frame
|
||||
:zoom zoom
|
||||
:coord :y
|
||||
:selected selected}]]))
|
||||
|
||||
(when-not (ctl/layout? frame)
|
||||
[:g.distance
|
||||
[:& shape-distance
|
||||
{:selrect selrect
|
||||
:page-id page-id
|
||||
:frame frame
|
||||
:zoom zoom
|
||||
:coord :x
|
||||
:selected selected}]
|
||||
[:& shape-distance
|
||||
{:selrect selrect
|
||||
:page-id page-id
|
||||
:frame frame
|
||||
:zoom zoom
|
||||
:coord :y
|
||||
:selected selected}]])))
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.spec :as us]
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.main.snap :as snap]
|
||||
[app.util.geom.snap-points :as sp]
|
||||
[beicon.core :as rx]
|
||||
|
@ -50,20 +51,14 @@
|
|||
:opacity line-opacity}])
|
||||
|
||||
(defn get-snap
|
||||
[coord {:keys [shapes page-id remove-snap? zoom modifiers]}]
|
||||
(let [shapes-sr
|
||||
(->> shapes
|
||||
;; Merge modifiers into shapes
|
||||
(map #(merge % (get modifiers (:id %))))
|
||||
;; Create the bounding rectangle for the shapes
|
||||
(gsh/selection-rect))
|
||||
[coord {:keys [shapes page-id remove-snap? zoom]}]
|
||||
(let [bounds (gsh/selection-rect shapes)
|
||||
frame-id (snap/snap-frame-id shapes)]
|
||||
|
||||
frame-id (snap/snap-frame-id shapes)]
|
||||
|
||||
(->> (rx/of shapes-sr)
|
||||
(->> (rx/of bounds)
|
||||
(rx/flat-map
|
||||
(fn [selrect]
|
||||
(->> (sp/selrect-snap-points selrect)
|
||||
(fn [bounds]
|
||||
(->> (sp/selrect-snap-points bounds)
|
||||
(map #(vector frame-id %)))))
|
||||
|
||||
(rx/flat-map
|
||||
|
@ -159,7 +154,7 @@
|
|||
|
||||
(mf/defc snap-points
|
||||
{::mf/wrap [mf/memo]}
|
||||
[{:keys [layout zoom objects selected page-id drawing modifiers focus] :as props}]
|
||||
[{:keys [layout zoom objects selected page-id drawing focus] :as props}]
|
||||
(us/assert set? selected)
|
||||
(let [shapes (into [] (keep (d/getf objects)) selected)
|
||||
|
||||
|
@ -178,10 +173,11 @@
|
|||
(and (= type :layout) (= grid :square))
|
||||
(= type :guide))))
|
||||
|
||||
shapes (if drawing [drawing] shapes)]
|
||||
[:& snap-feedback {:shapes shapes
|
||||
:page-id page-id
|
||||
:remove-snap? remove-snap?
|
||||
:zoom zoom
|
||||
:modifiers modifiers}]))
|
||||
shapes (if drawing [drawing] shapes)
|
||||
frame-id (snap/snap-frame-id shapes)]
|
||||
(when-not (ctl/layout? objects frame-id)
|
||||
[:& snap-feedback {:shapes shapes
|
||||
:page-id page-id
|
||||
:remove-snap? remove-snap?
|
||||
:zoom zoom}])))
|
||||
|
||||
|
|
|
@ -53,9 +53,7 @@
|
|||
(defn text-transform
|
||||
[{:keys [x y]} zoom]
|
||||
(let [inv-zoom (/ 1 zoom)]
|
||||
(str
|
||||
"scale(" inv-zoom ", " inv-zoom ") "
|
||||
"translate(" (* zoom x) ", " (* zoom y) ")")))
|
||||
(dm/fmt "scale(%, %) translate(%, %)" inv-zoom inv-zoom (* zoom x) (* zoom y))))
|
||||
|
||||
(defn title-transform [frame zoom]
|
||||
(let [frame-transform (gsh/transform-str frame {:no-flip true})
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.types.shape-tree :as ctt]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.workspace :as dw]
|
||||
|
@ -22,6 +21,7 @@
|
|||
[app.main.ui.workspace.viewport.path-actions :refer [path-actions]]
|
||||
[app.main.ui.workspace.viewport.utils :as vwu]
|
||||
[app.util.dom :as dom]
|
||||
[debug :refer [debug?]]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(mf/defc pixel-grid
|
||||
|
@ -35,8 +35,8 @@
|
|||
:pattern-units "userSpaceOnUse"}
|
||||
[:path {:d "M 1 0 L 0 0 0 1"
|
||||
:style {:fill "none"
|
||||
:stroke "var(--color-info)"
|
||||
:stroke-opacity "0.2"
|
||||
:stroke (if (debug? :pixel-grid) "red" "var(--color-info)")
|
||||
:stroke-opacity (if (debug? :pixel-grid) 1 "0.2")
|
||||
:stroke-width (str (/ 1 zoom))}}]]]
|
||||
[:rect {:x (:x vbox)
|
||||
:y (:y vbox)
|
||||
|
@ -182,8 +182,8 @@
|
|||
:on-frame-select on-frame-select}]))]))
|
||||
|
||||
(mf/defc frame-flow
|
||||
[{:keys [flow frame modifiers selected? zoom on-frame-enter on-frame-leave on-frame-select]}]
|
||||
(let [{:keys [x y]} (gsh/transform-shape frame)
|
||||
[{:keys [flow frame selected? zoom on-frame-enter on-frame-leave on-frame-select]}]
|
||||
(let [{:keys [x y]} frame
|
||||
flow-pos (gpt/point x (- y (/ 35 zoom)))
|
||||
|
||||
on-mouse-down
|
||||
|
@ -217,9 +217,7 @@
|
|||
:y -15
|
||||
:width 100000
|
||||
:height 24
|
||||
:transform (str (when (and selected? modifiers)
|
||||
(str (:displacement modifiers) " " ))
|
||||
(vwu/text-transform flow-pos zoom))}
|
||||
:transform (vwu/text-transform flow-pos zoom)}
|
||||
[:div.flow-badge {:class (dom/classnames :selected selected?)}
|
||||
[:div.content {:on-mouse-down on-mouse-down
|
||||
:on-double-click on-double-click
|
||||
|
@ -234,7 +232,6 @@
|
|||
(let [flows (unchecked-get props "flows")
|
||||
objects (unchecked-get props "objects")
|
||||
zoom (unchecked-get props "zoom")
|
||||
modifiers (unchecked-get props "modifiers")
|
||||
selected (or (unchecked-get props "selected") #{})
|
||||
|
||||
on-frame-enter (unchecked-get props "on-frame-enter")
|
||||
|
@ -248,7 +245,6 @@
|
|||
:frame frame
|
||||
:selected? (contains? selected (:id frame))
|
||||
:zoom zoom
|
||||
:modifiers modifiers
|
||||
:on-frame-enter on-frame-enter
|
||||
:on-frame-leave on-frame-leave
|
||||
:on-frame-select on-frame-select}]))]))
|
||||
|
|
|
@ -29,10 +29,9 @@
|
|||
(defn shape-snap-points
|
||||
[{:keys [hidden blocked] :as shape}]
|
||||
(when (and (not blocked) (not hidden))
|
||||
(let [shape (gsh/transform-shape shape)]
|
||||
(case (:type shape)
|
||||
:frame (-> shape :points gsh/points->selrect frame-snap-points)
|
||||
(into #{(gsh/center-shape shape)} (:points shape))))))
|
||||
(case (:type shape)
|
||||
:frame (-> shape :points gsh/points->selrect frame-snap-points)
|
||||
(into #{(gsh/center-shape shape)} (:points shape)))))
|
||||
|
||||
(defn guide-snap-points
|
||||
[guide frame]
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
[app.common.pages.diff :as diff]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.types.shape-tree :as ctst]
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.util.geom.grid :as gg]
|
||||
[app.util.geom.snap-points :as snap]
|
||||
|
@ -70,7 +71,7 @@
|
|||
(mapv grid->snap)))))
|
||||
|
||||
(defn- add-frame
|
||||
[page-data frame]
|
||||
[objects page-data frame]
|
||||
(let [frame-id (:id frame)
|
||||
parent-id (:parent-id frame)
|
||||
frame-data (->> (snap/shape-snap-points frame)
|
||||
|
@ -79,21 +80,24 @@
|
|||
:pt %)))
|
||||
grid-x-data (get-grids-snap-points frame :x)
|
||||
grid-y-data (get-grids-snap-points frame :y)]
|
||||
(-> page-data
|
||||
;; Update root frame information
|
||||
(assoc-in [uuid/zero :objects-data frame-id] frame-data)
|
||||
(update-in [parent-id :x] (make-insert-tree-data frame-data :x))
|
||||
(update-in [parent-id :y] (make-insert-tree-data frame-data :y))
|
||||
|
||||
;; Update frame information
|
||||
(assoc-in [frame-id :objects-data frame-id] (d/concat-vec frame-data grid-x-data grid-y-data))
|
||||
(update-in [frame-id :x] #(or % (rt/make-tree)))
|
||||
(update-in [frame-id :y] #(or % (rt/make-tree)))
|
||||
(update-in [frame-id :x] (make-insert-tree-data (d/concat-vec frame-data grid-x-data) :x))
|
||||
(update-in [frame-id :y] (make-insert-tree-data (d/concat-vec frame-data grid-y-data) :y)))))
|
||||
(cond-> page-data
|
||||
(not (ctl/layout-child? objects frame))
|
||||
|
||||
(-> ;; Update root frame information
|
||||
(assoc-in [uuid/zero :objects-data frame-id] frame-data)
|
||||
(update-in [parent-id :x] (make-insert-tree-data frame-data :x))
|
||||
(update-in [parent-id :y] (make-insert-tree-data frame-data :y))
|
||||
|
||||
;; Update frame information
|
||||
(assoc-in [frame-id :objects-data frame-id] (d/concat-vec frame-data grid-x-data grid-y-data))
|
||||
(update-in [frame-id :x] #(or % (rt/make-tree)))
|
||||
(update-in [frame-id :y] #(or % (rt/make-tree)))
|
||||
(update-in [frame-id :x] (make-insert-tree-data (d/concat-vec frame-data grid-x-data) :x))
|
||||
(update-in [frame-id :y] (make-insert-tree-data (d/concat-vec frame-data grid-y-data) :y))))))
|
||||
|
||||
(defn- add-shape
|
||||
[page-data shape]
|
||||
[objects page-data shape]
|
||||
(let [frame-id (:frame-id shape)
|
||||
snap-points (snap/shape-snap-points shape)
|
||||
shape-data (->> snap-points
|
||||
|
@ -101,11 +105,11 @@
|
|||
:type :shape
|
||||
:id (:id shape)
|
||||
:pt %)))]
|
||||
(-> page-data
|
||||
(assoc-in [frame-id :objects-data (:id shape)] shape-data)
|
||||
(update-in [frame-id :x] (make-insert-tree-data shape-data :x))
|
||||
(update-in [frame-id :y] (make-insert-tree-data shape-data :y)))))
|
||||
|
||||
(cond-> page-data
|
||||
(not (ctl/layout-child? objects shape))
|
||||
(-> (assoc-in [frame-id :objects-data (:id shape)] shape-data)
|
||||
(update-in [frame-id :x] (make-insert-tree-data shape-data :x))
|
||||
(update-in [frame-id :y] (make-insert-tree-data shape-data :y))))))
|
||||
|
||||
(defn- add-guide
|
||||
[objects page-data guide]
|
||||
|
@ -164,22 +168,22 @@
|
|||
(update-in [:guides (:axis guide)] (make-delete-tree-data guide-data (:axis guide)))))))
|
||||
|
||||
(defn- update-frame
|
||||
[page-data [_ new-frame]]
|
||||
[objects page-data [_ new-frame]]
|
||||
(let [frame-id (:id new-frame)
|
||||
root-data (get-in page-data [uuid/zero :objects-data frame-id])
|
||||
frame-data (get-in page-data [frame-id :objects-data frame-id])]
|
||||
(-> page-data
|
||||
(update-in [uuid/zero :x] (make-delete-tree-data root-data :x))
|
||||
(update-in [uuid/zero :y] (make-delete-tree-data root-data :y))
|
||||
(update-in [frame-id :x] (make-delete-tree-data frame-data :x))
|
||||
(update-in [frame-id :y] (make-delete-tree-data frame-data :y))
|
||||
(add-frame new-frame))))
|
||||
(as-> page-data $
|
||||
(update-in $ [uuid/zero :x] (make-delete-tree-data root-data :x))
|
||||
(update-in $ [uuid/zero :y] (make-delete-tree-data root-data :y))
|
||||
(update-in $ [frame-id :x] (make-delete-tree-data frame-data :x))
|
||||
(update-in $ [frame-id :y] (make-delete-tree-data frame-data :y))
|
||||
(add-frame objects $ new-frame))))
|
||||
|
||||
(defn- update-shape
|
||||
[page-data [old-shape new-shape]]
|
||||
(-> page-data
|
||||
(remove-shape old-shape)
|
||||
(add-shape new-shape)))
|
||||
[objects page-data [old-shape new-shape]]
|
||||
(as-> page-data $
|
||||
(remove-shape $ old-shape)
|
||||
(add-shape objects $ new-shape)))
|
||||
|
||||
(defn- update-guide
|
||||
[objects page-data [old-guide new-guide]]
|
||||
|
@ -205,8 +209,8 @@
|
|||
page-data
|
||||
(as-> {} $
|
||||
(add-root-frame $)
|
||||
(reduce add-frame $ frames)
|
||||
(reduce add-shape $ shapes)
|
||||
(reduce (partial add-frame objects) $ frames)
|
||||
(reduce (partial add-shape objects) $ shapes)
|
||||
(reduce (partial add-guide objects) $ guides))]
|
||||
(assoc snap-data (:id page) page-data)))
|
||||
|
||||
|
@ -233,16 +237,16 @@
|
|||
(diff/calculate-page-diff old-page page snap-attrs)]
|
||||
|
||||
(as-> page-data $
|
||||
(reduce update-shape $ change-frame-shapes)
|
||||
(reduce (partial update-shape objects) $ change-frame-shapes)
|
||||
(reduce remove-frame $ removed-frames)
|
||||
(reduce remove-shape $ removed-shapes)
|
||||
(reduce update-frame $ updated-frames)
|
||||
(reduce update-shape $ updated-shapes)
|
||||
(reduce add-frame $ new-frames)
|
||||
(reduce add-shape $ new-shapes)
|
||||
(reduce remove-guide $ removed-guides)
|
||||
(reduce (partial update-frame objects) $ updated-frames)
|
||||
(reduce (partial update-shape objects) $ updated-shapes)
|
||||
(reduce (partial add-frame objects) $ new-frames)
|
||||
(reduce (partial add-shape objects) $ new-shapes)
|
||||
|
||||
;; Guides functions. Need objects to get its frame data
|
||||
(reduce remove-guide $ removed-guides)
|
||||
(reduce (partial update-guide objects) $ change-frame-guides)
|
||||
(reduce (partial update-guide objects) $ updated-guides)
|
||||
(reduce (partial add-guide objects) $ new-guides)))))
|
||||
|
|
|
@ -67,6 +67,15 @@
|
|||
|
||||
;; Disable frame thumbnails
|
||||
:disable-frame-thumbnails
|
||||
|
||||
;; Enable a widget to show the auto-layout drop-zones
|
||||
:layout-drop-zones
|
||||
|
||||
;; Display the layout lines
|
||||
:layout-lines
|
||||
|
||||
;; Makes the pixel grid red so its more visibile
|
||||
:pixel-grid
|
||||
})
|
||||
|
||||
;; These events are excluded when we activate the :events flag
|
||||
|
@ -74,7 +83,7 @@
|
|||
#{:app.main.data.workspace.notifications/handle-pointer-update
|
||||
:app.main.data.workspace.selection/change-hover-state})
|
||||
|
||||
(defonce ^:dynamic *debug* (atom #{#_:events #_:text-outline}))
|
||||
(defonce ^:dynamic *debug* (atom #{#_:events}))
|
||||
|
||||
(defn debug-all! [] (reset! *debug* debug-options))
|
||||
(defn debug-none! [] (reset! *debug* #{}))
|
||||
|
@ -294,9 +303,16 @@
|
|||
num-nodes (->> (dom/seq-nodes root-node) count)]
|
||||
#js {:number num-nodes}))
|
||||
|
||||
#_(defn modif->js
|
||||
(defn modif->js
|
||||
[modif-tree objects]
|
||||
(clj->js (into {}
|
||||
(map (fn [[k v]]
|
||||
[(get-in objects [k :name]) v]))
|
||||
modif-tree)))
|
||||
|
||||
(defn ^:export dump-modifiers
|
||||
[]
|
||||
(let [page-id (get @st/state :current-page-id)
|
||||
objects (get-in @st/state [:workspace-data :pages-index page-id :objects])]
|
||||
(.log js/console (modif->js (:workspace-modifiers @st/state) objects)))
|
||||
nil)
|
||||
|
|
|
@ -3827,19 +3827,19 @@ msgid "workspace.options.layout-item.advanced-ops"
|
|||
msgstr "Erweiterte Optionen"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs
|
||||
msgid "workspace.options.layout-item.layout-max-h"
|
||||
msgid "workspace.options.layout-item.layout-item-max-h"
|
||||
msgstr "Max.Höhe"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs
|
||||
msgid "workspace.options.layout-item.layout-max-w"
|
||||
msgid "workspace.options.layout-item.layout-item-max-w"
|
||||
msgstr "Max.Breite"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs
|
||||
msgid "workspace.options.layout-item.layout-min-h"
|
||||
msgid "workspace.options.layout-item.layout-item-min-h"
|
||||
msgstr "Min.Höhe"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs
|
||||
msgid "workspace.options.layout-item.layout-min-w"
|
||||
msgid "workspace.options.layout-item.layout-item-min-w"
|
||||
msgstr "Min.Breite"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs
|
||||
|
@ -3847,19 +3847,19 @@ msgid "workspace.options.layout-item.title"
|
|||
msgstr "Elementgröße ändern"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs
|
||||
msgid "workspace.options.layout-item.title.layout-max-h"
|
||||
msgid "workspace.options.layout-item.title.layout-item-max-h"
|
||||
msgstr "Maximale Höhe"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs
|
||||
msgid "workspace.options.layout-item.title.layout-max-w"
|
||||
msgid "workspace.options.layout-item.title.layout-item-max-w"
|
||||
msgstr "Maximale Breite"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs
|
||||
msgid "workspace.options.layout-item.title.layout-min-h"
|
||||
msgid "workspace.options.layout-item.title.layout-item-min-h"
|
||||
msgstr "Mindesthöhe"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs
|
||||
msgid "workspace.options.layout-item.title.layout-min-w"
|
||||
msgid "workspace.options.layout-item.title.layout-item-min-w"
|
||||
msgstr "Mindestbreite"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/layout.cljs
|
||||
|
@ -4701,4 +4701,4 @@ msgid "workspace.updates.update"
|
|||
msgstr "Aktualisieren"
|
||||
|
||||
msgid "workspace.viewport.click-to-close-path"
|
||||
msgstr "Klicken Sie, um den Pfad zu schließen"
|
||||
msgstr "Klicken Sie, um den Pfad zu schließen"
|
||||
|
|
|
@ -3490,35 +3490,35 @@ msgid "workspace.options.layout-item.advanced-ops"
|
|||
msgstr "Advanced options"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs
|
||||
msgid "workspace.options.layout-item.layout-max-h"
|
||||
msgid "workspace.options.layout-item.layout-item-max-h"
|
||||
msgstr "Max.Height"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs
|
||||
msgid "workspace.options.layout-item.layout-max-w"
|
||||
msgid "workspace.options.layout-item.layout-item-max-w"
|
||||
msgstr "Max.Width"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs
|
||||
msgid "workspace.options.layout-item.layout-min-h"
|
||||
msgid "workspace.options.layout-item.layout-item-min-h"
|
||||
msgstr "Min.Height"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs
|
||||
msgid "workspace.options.layout-item.layout-min-w"
|
||||
msgid "workspace.options.layout-item.layout-item-min-w"
|
||||
msgstr "Min.Width"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs
|
||||
msgid "workspace.options.layout-item.title.layout-max-h"
|
||||
msgid "workspace.options.layout-item.title.layout-item-max-h"
|
||||
msgstr "Maximum height"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs
|
||||
msgid "workspace.options.layout-item.title.layout-max-w"
|
||||
msgid "workspace.options.layout-item.title.layout-item-max-w"
|
||||
msgstr "Maximum width"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs
|
||||
msgid "workspace.options.layout-item.title.layout-min-h"
|
||||
msgid "workspace.options.layout-item.title.layout-item-min-h"
|
||||
msgstr "Minimum height"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs
|
||||
msgid "workspace.options.layout-item.title.layout-min-w"
|
||||
msgid "workspace.options.layout-item.title.layout-item-min-w"
|
||||
msgstr "Minimum width"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/options/menus/layout.cljs
|
||||
|
@ -4077,6 +4077,14 @@ msgstr "Update main components"
|
|||
msgid "workspace.shape.menu.update-main"
|
||||
msgstr "Update main component"
|
||||
|
||||
#: src/app/main/ui/workspace/context_menu.cljs
|
||||
msgid "workspace.shape.menu.add-flex"
|
||||
msgstr "Add layout flex"
|
||||
|
||||
#: src/app/main/ui/workspace/context_menu.cljs
|
||||
msgid "workspace.shape.menu.remove-flex"
|
||||
msgstr "Remove layout flex"
|
||||
|
||||
msgid "workspace.sidebar.collapse"
|
||||
msgstr "Collapse sidebar"
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue