0
Fork 0
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:
alonso.torres 2022-06-16 17:48:50 +02:00
parent 2e3f443758
commit a774f4d4fa
8 changed files with 135 additions and 93 deletions

View file

@ -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))))

View file

@ -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

View file

@ -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 []}]

View file

@ -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}])

View file

@ -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)}]))]))

View file

@ -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)]

View file

@ -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))}))

View file

@ -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)))