mirror of
https://github.com/penpot/penpot.git
synced 2025-04-14 07:51:35 -05:00
✨ Fix guides, grids and constraints for nested frames
This commit is contained in:
parent
2e3f443758
commit
a774f4d4fa
8 changed files with 135 additions and 93 deletions
|
@ -9,6 +9,7 @@
|
|||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.math :as mth]
|
||||
[app.common.spec :as us]
|
||||
[app.common.spec.page :as spec.page]
|
||||
[app.common.uuid :as uuid]
|
||||
|
@ -718,3 +719,7 @@
|
|||
(defn update-object-indices
|
||||
[file page-id]
|
||||
(update-in file [:pages-index page-id :objects] update-page-index))
|
||||
|
||||
(defn rotated-frame?
|
||||
[frame]
|
||||
(not (mth/almost-zero? (:rotation frame 0))))
|
||||
|
|
|
@ -181,50 +181,58 @@
|
|||
(assoc :grow-type :fixed))))
|
||||
|
||||
(defn- apply-modifiers
|
||||
[ids]
|
||||
(us/verify (s/coll-of uuid?) ids)
|
||||
(ptk/reify ::apply-modifiers
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [objects (wsh/lookup-page-objects state)
|
||||
ids-with-children (into (vec ids) (mapcat #(cph/get-children-ids objects %)) ids)
|
||||
object-modifiers (get state :workspace-modifiers)
|
||||
shapes (map (d/getf objects) ids)
|
||||
ignore-tree (->> (map #(get-ignore-tree object-modifiers objects %) shapes)
|
||||
(reduce merge {}))]
|
||||
([ids]
|
||||
(apply-modifiers ids nil))
|
||||
|
||||
(rx/of (dwu/start-undo-transaction)
|
||||
(dwg/move-frame-guides ids-with-children)
|
||||
(dch/update-shapes
|
||||
ids-with-children
|
||||
(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)
|
||||
(dwu/commit-undo-transaction))))))
|
||||
([ids {:keys [undo-transation?] :or {undo-transation? true}}]
|
||||
(us/verify (s/coll-of uuid?) ids)
|
||||
(ptk/reify ::apply-modifiers
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [objects (wsh/lookup-page-objects state)
|
||||
ids-with-children (into (vec ids) (mapcat #(cph/get-children-ids objects %)) ids)
|
||||
object-modifiers (get state :workspace-modifiers)
|
||||
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 (dwg/move-frame-guides ids-with-children)
|
||||
(dch/update-shapes
|
||||
ids-with-children
|
||||
(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
|
||||
|
@ -762,9 +770,11 @@
|
|||
(rx/map (partial set-modifiers ids))
|
||||
(rx/take-until stopper))
|
||||
|
||||
(rx/of (calculate-frame-for-move ids)
|
||||
(apply-modifiers ids)
|
||||
(finish-transform)))))))))
|
||||
(rx/of (dwu/start-undo-transaction)
|
||||
(calculate-frame-for-move ids)
|
||||
(apply-modifiers ids {:undo-transation? false})
|
||||
(finish-transform)
|
||||
(dwu/commit-undo-transaction)))))))))
|
||||
|
||||
(s/def ::direction #{:up :down :right :left})
|
||||
|
||||
|
@ -842,6 +852,14 @@
|
|||
(rx/of (set-modifiers [id] {:displacement displ} false true)
|
||||
(apply-modifiers [id]))))))
|
||||
|
||||
(defn check-frame-move?
|
||||
[target-frame-id objects position shape]
|
||||
|
||||
(let [current-frame (get objects (:frame-id shape))]
|
||||
;; If the current frame contains the point and it's a child of the target
|
||||
(and (gsh/has-point? current-frame position)
|
||||
(cph/is-child? objects target-frame-id (:id current-frame)))))
|
||||
|
||||
(defn- calculate-frame-for-move
|
||||
[ids]
|
||||
(ptk/reify ::calculate-frame-for-move
|
||||
|
@ -855,16 +873,14 @@
|
|||
moving-shapes (->> ids
|
||||
(cph/clean-loops objects)
|
||||
(keep #(get objects %))
|
||||
(remove #(= (:frame-id %) frame-id)))
|
||||
(remove (partial check-frame-move? frame-id objects position)))
|
||||
|
||||
changes (-> (pcb/empty-changes it page-id)
|
||||
(pcb/with-objects objects)
|
||||
(pcb/change-parent frame-id moving-shapes))]
|
||||
|
||||
(when-not (empty? changes)
|
||||
(rx/of dwu/pop-undo-into-transaction
|
||||
(dch/commit-changes changes)
|
||||
(dwu/commit-undo-transaction)
|
||||
(rx/of (dch/commit-changes changes)
|
||||
(dwc/expand-collapse frame-id)))))))
|
||||
|
||||
(defn- get-displacement
|
||||
|
|
|
@ -191,7 +191,6 @@
|
|||
has-group? (->> shapes (d/seek #(= :group (:type %))))
|
||||
has-bool? (->> shapes (d/seek #(= :bool (:type %))))
|
||||
has-mask? (->> shapes (d/seek :masked-group?))
|
||||
has-frame? (->> shapes (d/seek #(= :frame (:type %))))
|
||||
|
||||
is-group? (and single? has-group?)
|
||||
is-bool? (and single? has-bool?)
|
||||
|
@ -207,10 +206,9 @@
|
|||
:shortcut (sc/get-tooltip :ungroup)
|
||||
:on-click do-remove-group}])
|
||||
|
||||
(when (not has-frame?)
|
||||
[:& menu-entry {:title (tr "workspace.shape.menu.group")
|
||||
:shortcut (sc/get-tooltip :group)
|
||||
:on-click do-create-group}])
|
||||
[:& menu-entry {:title (tr "workspace.shape.menu.group")
|
||||
:shortcut (sc/get-tooltip :group)
|
||||
:on-click do-create-group}]
|
||||
|
||||
(when (or multiple? (and is-group? (not has-mask?)) is-bool?)
|
||||
[:& menu-entry {:title (tr "workspace.shape.menu.mask")
|
||||
|
@ -222,12 +220,10 @@
|
|||
:shortcut (sc/get-tooltip :unmask)
|
||||
:on-click do-unmask-group}])
|
||||
|
||||
(when (not has-frame?)
|
||||
[:*
|
||||
[:& menu-entry {:title (tr "workspace.shape.menu.create-artboard-from-selection")
|
||||
:shortcut (sc/get-tooltip :artboard-selection)
|
||||
:on-click do-create-artboard-from-selection}]
|
||||
[:& menu-separator]])]))
|
||||
[:& menu-entry {:title (tr "workspace.shape.menu.create-artboard-from-selection")
|
||||
:shortcut (sc/get-tooltip :artboard-selection)
|
||||
:on-click do-create-artboard-from-selection}]
|
||||
[:& menu-separator]]))
|
||||
|
||||
(mf/defc context-focus-mode-menu
|
||||
[{:keys []}]
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
(:require
|
||||
[app.main.constants :refer [has-layout-item]]
|
||||
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]]
|
||||
[app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]]
|
||||
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs-shape fill-menu]]
|
||||
[app.main.ui.workspace.sidebar.options.menus.frame-grid :refer [frame-grid]]
|
||||
[app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]]
|
||||
|
@ -25,6 +26,7 @@
|
|||
stroke-values (select-keys shape stroke-attrs)
|
||||
layer-values (select-keys shape layer-attrs)
|
||||
measure-values (select-keys shape measure-attrs)
|
||||
constraint-values (select-keys shape constraint-attrs)
|
||||
layout-values (select-keys shape layout-attrs)
|
||||
layout-item-values (select-keys shape layout-item-attrs)]
|
||||
[:*
|
||||
|
@ -32,6 +34,8 @@
|
|||
:values measure-values
|
||||
:type type
|
||||
:shape shape}]
|
||||
[:& constraints-menu {:ids ids
|
||||
:values constraint-values}]
|
||||
(when has-layout-item
|
||||
[:& layout-menu {:type type :ids [(:id shape)] :values layout-values}])
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
[app.common.data :as d]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.math :as mth]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.refs :as refs]
|
||||
[app.util.geom.grid :as gg]
|
||||
|
@ -126,13 +127,15 @@
|
|||
(mf/defc frame-grid
|
||||
{::mf/wrap [mf/memo]}
|
||||
[{:keys [zoom transform selected focus]}]
|
||||
(let [frames (mf/deref refs/workspace-frames)
|
||||
moving (when (= :move transform) selected)
|
||||
is-moving? #(contains? moving (:id %))]
|
||||
(let [frames (mf/deref refs/workspace-frames)
|
||||
transforming (when (some? transform) selected)
|
||||
is-transform? #(contains? transforming (:id %))]
|
||||
|
||||
[:g.grid-display {:style {:pointer-events "none"}}
|
||||
(for [frame (remove is-moving? frames)]
|
||||
(when (or (empty? focus) (contains? focus (:id frame)))
|
||||
(for [frame frames]
|
||||
(when (and (not (is-transform? frame))
|
||||
(not (cph/rotated-frame? frame))
|
||||
(or (empty? focus) (contains? focus (:id frame))))
|
||||
[:& grid-display-frame {:key (str "grid-" (:id frame))
|
||||
:zoom zoom
|
||||
:frame (gsh/transform-shape frame)}]))]))
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.math :as mth]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.refs :as refs]
|
||||
|
@ -286,8 +287,9 @@
|
|||
guide-pill-corner-radius (/ guide-pill-corner-radius zoom)]
|
||||
|
||||
(when (or (nil? frame)
|
||||
(is-guide-inside-frame? (assoc guide :position pos) frame)
|
||||
(:hover @state true))
|
||||
(and (is-guide-inside-frame? (assoc guide :position pos) frame)
|
||||
(cph/root-frame? frame)
|
||||
(not (cph/rotated-frame? frame))))
|
||||
[:g.guide-area
|
||||
(when-not disabled-guides?
|
||||
(let [{:keys [x y width height]} (guide-area-axis pos vbox zoom frame axis)]
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
(ns app.util.geom.snap-points
|
||||
(:require
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes :as gsh]))
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.pages.helpers :as cph]))
|
||||
|
||||
(defn selrect-snap-points [{:keys [x y width height] :as selrect}]
|
||||
#{(gpt/point x y)
|
||||
|
@ -29,11 +30,20 @@
|
|||
(when (and (not blocked) (not hidden))
|
||||
(let [shape (gsh/transform-shape shape)]
|
||||
(case (:type shape)
|
||||
:frame (-> shape :selrect frame-snap-points)
|
||||
:frame (-> shape :points gsh/points->selrect frame-snap-points)
|
||||
(into #{(gsh/center-shape shape)} (:points shape))))))
|
||||
|
||||
(defn guide-snap-points
|
||||
[guide]
|
||||
(if (= :x (:axis guide))
|
||||
[guide frame]
|
||||
|
||||
(cond
|
||||
(and (some? frame)
|
||||
(not (cph/rotated-frame? frame))
|
||||
(not (cph/root-frame? frame)))
|
||||
#{}
|
||||
|
||||
(= :x (:axis guide))
|
||||
#{(gpt/point (:position guide) 0)}
|
||||
|
||||
:else
|
||||
#{(gpt/point 0 (:position guide))}))
|
||||
|
|
|
@ -55,16 +55,18 @@
|
|||
|
||||
(defn get-grids-snap-points
|
||||
[frame coord]
|
||||
(let [grid->snap (fn [[grid-type position]]
|
||||
{:type :layout
|
||||
:id (:id frame)
|
||||
:grid grid-type
|
||||
:pt position})]
|
||||
(->> (:grids frame)
|
||||
(mapcat (fn [grid]
|
||||
(->> (gg/grid-snap-points frame grid coord)
|
||||
(mapv #(vector (:type grid) %)))))
|
||||
(mapv grid->snap))))
|
||||
(if (not (cph/rotated-frame? frame))
|
||||
[]
|
||||
(let [grid->snap (fn [[grid-type position]]
|
||||
{:type :layout
|
||||
:id (:id frame)
|
||||
:grid grid-type
|
||||
:pt position})]
|
||||
(->> (:grids frame)
|
||||
(mapcat (fn [grid]
|
||||
(->> (gg/grid-snap-points frame grid coord)
|
||||
(mapv #(vector (:type grid) %)))))
|
||||
(mapv grid->snap)))))
|
||||
|
||||
(defn- add-frame
|
||||
[page-data frame]
|
||||
|
@ -105,9 +107,10 @@
|
|||
|
||||
|
||||
(defn- add-guide
|
||||
[page-data guide]
|
||||
[objects page-data guide]
|
||||
|
||||
(let [guide-data (->> (snap/guide-snap-points guide)
|
||||
(let [frame (get objects (:frame-id guide))
|
||||
guide-data (->> (snap/guide-snap-points guide frame)
|
||||
(mapv #(array-map
|
||||
:type :guide
|
||||
:id (:id guide)
|
||||
|
@ -178,10 +181,10 @@
|
|||
(add-shape new-shape)))
|
||||
|
||||
(defn- update-guide
|
||||
[page-data [old-guide new-guide]]
|
||||
(-> page-data
|
||||
(remove-guide old-guide)
|
||||
(add-guide new-guide)))
|
||||
[objects page-data [old-guide new-guide]]
|
||||
(as-> page-data $
|
||||
(remove-guide $ old-guide)
|
||||
(add-guide objects $ new-guide)))
|
||||
|
||||
;; PUBLIC API
|
||||
|
||||
|
@ -203,7 +206,7 @@
|
|||
(add-root-frame $)
|
||||
(reduce add-frame $ frames)
|
||||
(reduce add-shape $ shapes)
|
||||
(reduce add-guide $ guides))]
|
||||
(reduce (partial add-guide objects) $ guides))]
|
||||
(assoc snap-data (:id page) page-data)))
|
||||
|
||||
(defn update-page
|
||||
|
@ -214,7 +217,8 @@
|
|||
;; Update page
|
||||
(update snap-data (:id page)
|
||||
(fn [page-data]
|
||||
(let [{:keys [change-frame-shapes
|
||||
(let [{:keys [objects]} page
|
||||
{:keys [change-frame-shapes
|
||||
change-frame-guides
|
||||
removed-frames
|
||||
removed-shapes
|
||||
|
@ -235,10 +239,12 @@
|
|||
(reduce update-shape $ updated-shapes)
|
||||
(reduce add-frame $ new-frames)
|
||||
(reduce add-shape $ new-shapes)
|
||||
(reduce update-guide $ change-frame-guides)
|
||||
(reduce remove-guide $ removed-guides)
|
||||
(reduce update-guide $ updated-guides)
|
||||
(reduce add-guide $ new-guides)))))
|
||||
|
||||
;; Guides functions. Need objects to get its frame data
|
||||
(reduce (partial update-guide objects) $ change-frame-guides)
|
||||
(reduce (partial update-guide objects) $ updated-guides)
|
||||
(reduce (partial add-guide objects) $ new-guides)))))
|
||||
|
||||
;; Page doesn't exist, we create a new entry
|
||||
(add-page snap-data page)))
|
||||
|
|
Loading…
Add table
Reference in a new issue