diff --git a/common/src/app/common/pages/helpers.cljc b/common/src/app/common/pages/helpers.cljc index a3f48bf16..111eb6194 100644 --- a/common/src/app/common/pages/helpers.cljc +++ b/common/src/app/common/pages/helpers.cljc @@ -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)))) diff --git a/frontend/src/app/main/data/workspace/transforms.cljs b/frontend/src/app/main/data/workspace/transforms.cljs index 89bd28301..5b043d8b6 100644 --- a/frontend/src/app/main/data/workspace/transforms.cljs +++ b/frontend/src/app/main/data/workspace/transforms.cljs @@ -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 diff --git a/frontend/src/app/main/ui/workspace/context_menu.cljs b/frontend/src/app/main/ui/workspace/context_menu.cljs index 669e5cf91..0cfc56436 100644 --- a/frontend/src/app/main/ui/workspace/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/context_menu.cljs @@ -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 []}] diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/frame.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/frame.cljs index 79e1bb268..13aa47a60 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/frame.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/frame.cljs @@ -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}]) diff --git a/frontend/src/app/main/ui/workspace/viewport/frame_grid.cljs b/frontend/src/app/main/ui/workspace/viewport/frame_grid.cljs index 013c1cee5..ece6b20b2 100644 --- a/frontend/src/app/main/ui/workspace/viewport/frame_grid.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/frame_grid.cljs @@ -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)}]))])) diff --git a/frontend/src/app/main/ui/workspace/viewport/guides.cljs b/frontend/src/app/main/ui/workspace/viewport/guides.cljs index 434458d31..363feed7b 100644 --- a/frontend/src/app/main/ui/workspace/viewport/guides.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/guides.cljs @@ -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)] diff --git a/frontend/src/app/util/geom/snap_points.cljs b/frontend/src/app/util/geom/snap_points.cljs index 17df6445a..1940deaf7 100644 --- a/frontend/src/app/util/geom/snap_points.cljs +++ b/frontend/src/app/util/geom/snap_points.cljs @@ -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))})) diff --git a/frontend/src/app/util/snap_data.cljs b/frontend/src/app/util/snap_data.cljs index 597e6ad4a..81f60b952 100644 --- a/frontend/src/app/util/snap_data.cljs +++ b/frontend/src/app/util/snap_data.cljs @@ -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)))