diff --git a/CHANGES.md b/CHANGES.md index 738b261ea..7e1a1760b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -20,6 +20,7 @@ - Select through stroke only rectangle [Taiga #5484](https://tree.taiga.io/project/penpot/issue/5484) - Override browser Ctrl+ and Ctrl- zoom with Penpot Zoom [Taiga #3200](https://tree.taiga.io/project/penpot/us/3200) +- Reduce handlers for the flex layout gaps and paddings ### :bug: Bugs fixed diff --git a/frontend/src/app/main/ui/flex_controls.cljs b/frontend/src/app/main/ui/flex_controls.cljs new file mode 100644 index 000000000..a21c6b9d9 --- /dev/null +++ b/frontend/src/app/main/ui/flex_controls.cljs @@ -0,0 +1,16 @@ +;; 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.flex-controls + (:require + [app.common.data.macros :as dm] + [app.main.ui.flex-controls.gap :as fcg] + [app.main.ui.flex-controls.margin :as fcm] + [app.main.ui.flex-controls.padding :as fcp])) + +(dm/export fcg/gap-control) +(dm/export fcm/margin-control) +(dm/export fcp/padding-control) diff --git a/frontend/src/app/main/ui/flex_controls/common.cljs b/frontend/src/app/main/ui/flex_controls/common.cljs new file mode 100644 index 000000000..052e501ee --- /dev/null +++ b/frontend/src/app/main/ui/flex_controls/common.cljs @@ -0,0 +1,35 @@ +(ns app.main.ui.flex-controls.common + (:require + [app.main.ui.formats :as fmt] + [rumext.v2 :as mf])) + +;; ------------------------------------------------ +;; CONSTANTS +;; ------------------------------------------------ + +(def font-size 11) +(def distance-color "var(--color-distance)") +(def distance-text-color "var(--color-white)") +(def warning-color "var(--color-warning)") +(def flex-display-pill-width 40) +(def flex-display-pill-height 20) +(def flex-display-pill-border-radius 4) + +(mf/defc flex-display-pill + [{:keys [x y width height font-size border-radius value color]}] + [:g.distance-pill + [:rect {:x x + :y y + :width width + :height height + :rx border-radius + :ry border-radius + :style {:fill color}}] + + [:text {:x (+ x (/ width 2)) + :y (+ y (/ height 2)) + :text-anchor "middle" + :dominant-baseline "central" + :style {:fill distance-text-color + :font-size font-size}} + (fmt/format-number (or value 0))]]) diff --git a/frontend/src/app/main/ui/flex_controls/gap.cljs b/frontend/src/app/main/ui/flex_controls/gap.cljs new file mode 100644 index 000000000..bd38eba10 --- /dev/null +++ b/frontend/src/app/main/ui/flex_controls/gap.cljs @@ -0,0 +1,311 @@ +;; 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.flex-controls.gap + (:require + [app.common.data :as d] + [app.common.files.helpers :as cfh] + [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.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] + [app.main.refs :as refs] + [app.main.store :as st] + [app.main.ui.css-cursors :as cur] + [app.main.ui.flex-controls.common :as fcc] + [app.main.ui.workspace.viewport.viewport-ref :refer [point->viewport]] + [app.util.dom :as dom] + [rumext.v2 :as mf])) + +(mf/defc gap-display + [{:keys [frame-id zoom gap-type gap on-pointer-enter on-pointer-leave + rect-data hover? selected? mouse-pos hover-value + on-move-selected on-context-menu]}] + (let [resizing (mf/use-var nil) + start (mf/use-var nil) + original-value (mf/use-var 0) + negate? (:resize-negate? rect-data) + axis (:resize-axis rect-data) + + on-pointer-down + (mf/use-fn + (mf/deps frame-id gap-type gap) + (fn [event] + (dom/capture-pointer event) + (reset! resizing gap-type) + (reset! start (dom/get-client-position event)) + (reset! original-value (:initial-value rect-data)))) + + on-lost-pointer-capture + (mf/use-fn + (mf/deps frame-id gap-type gap) + (fn [event] + (dom/release-pointer event) + (reset! resizing nil) + (reset! start nil) + (reset! original-value 0) + (st/emit! (dwm/apply-modifiers)))) + + on-pointer-move + (mf/use-fn + (mf/deps frame-id gap-type gap) + (fn [event] + (let [pos (dom/get-client-position event)] + (reset! mouse-pos (point->viewport pos)) + (when (= @resizing gap-type) + (let [delta (-> (gpt/to-vec @start pos) + (cond-> negate? gpt/negate) + (get axis)) + val (int (max (+ @original-value (/ delta zoom)) 0)) + layout-gap (assoc gap gap-type val) + modifiers (dwm/create-modif-tree [frame-id] (ctm/change-property (ctm/empty) :layout-gap layout-gap))] + + (reset! hover-value val) + (st/emit! (dwm/set-modifiers modifiers)))))))] + + [:g.gap-rect + [:rect.info-area + {:x (:x rect-data) + :y (:y rect-data) + :width (:width rect-data) + :height (:height rect-data) + :on-pointer-enter on-pointer-enter + :on-pointer-leave on-pointer-leave + :on-pointer-move on-pointer-move + :on-pointer-down on-move-selected + :on-context-menu on-context-menu + + :style {:fill (if (or hover? selected?) fcc/distance-color "none") + :opacity (if selected? 0.5 0.25)}}] + + (let [handle-width + (if (= axis :x) + (/ 2 zoom) + (min (* (:width rect-data) 0.5) (/ 20 zoom))) + + handle-height + (if (= axis :y) + (/ 2 zoom) + (min (* (:height rect-data) 0.5) (/ 30 zoom)))] + [:rect.handle + {:x (+ (:x rect-data) (/ (- (:width rect-data) handle-width) 2)) + :y (+ (:y rect-data) (/ (- (:height rect-data) handle-height) 2)) + :width handle-width + :height handle-height + :on-pointer-enter on-pointer-enter + :on-pointer-leave on-pointer-leave + :on-pointer-down on-pointer-down + :on-lost-pointer-capture on-lost-pointer-capture + :on-pointer-move on-pointer-move + :on-context-menu on-context-menu + :class (when (or hover? selected?) + (if (= (:resize-axis rect-data) :x) (cur/get-dynamic "resize-ew" 0) (cur/get-dynamic "resize-ew" 90))) + :style {:fill (if (or hover? selected?) fcc/distance-color "none") + :opacity (if selected? 0 1)}}])])) + +(mf/defc gap-rects + [{:keys [frame zoom on-move-selected on-context-menu]}] + (let [frame-id (:id frame) + saved-dir (:layout-flex-dir frame) + is-col? (or (= :column saved-dir) (= :column-reverse saved-dir)) + flip-x (:flip-x frame) + flip-y (:flip-y frame) + pill-width (/ fcc/flex-display-pill-width zoom) + pill-height (/ fcc/flex-display-pill-height zoom) + workspace-modifiers (mf/deref refs/workspace-modifiers) + gap-selected (mf/deref refs/workspace-gap-selected) + hover (mf/use-var nil) + hover-value (mf/use-var 0) + mouse-pos (mf/use-var nil) + padding (:layout-padding frame) + gap (:layout-gap frame) + {:keys [width height x1 y1]} (:selrect frame) + on-pointer-enter (fn [hover-type val] + (reset! hover hover-type) + (reset! hover-value val)) + + on-pointer-leave #(reset! hover nil) + negate {:column-gap (if flip-x true false) + :row-gap (if flip-y true false)} + + objects (wsh/lookup-page-objects @st/state) + children (->> (cfh/get-immediate-children objects frame-id) + (remove ctl/position-absolute?)) + + children-to-display (if (or (= :row-reverse saved-dir) + (= :column-reverse saved-dir)) + (drop-last children) + (rest children)) + children-to-display (->> children-to-display + (map #(gsh/transform-shape % (get-in workspace-modifiers [(:id %) :modifiers])))) + + wrap-blocks + (let [block-children (->> children + (map #(vector (gpo/parent-coords-bounds (:points %) (:points frame)) %))) + bounds (d/lazy-map (keys objects) #(gsh/shape->points (get objects %))) + + layout-data (gsl/calc-layout-data frame (:points frame) block-children bounds objects) + layout-bounds (:layout-bounds layout-data) + xv #(gpo/start-hv layout-bounds %) + yv #(gpo/start-vv layout-bounds %)] + (for [{:keys [start-p line-width line-height layout-gap-row layout-gap-col num-children]} (:layout-lines layout-data)] + (let [line-width (if is-col? line-width (+ line-width (* (dec num-children) layout-gap-row))) + line-height (if is-col? (+ line-height (* (dec num-children) layout-gap-col)) line-height) + end-p (-> start-p (gpt/add (xv line-width)) (gpt/add (yv line-height)))] + {:x1 (min (:x start-p) (:x end-p)) + :y1 (min (:y start-p) (:y end-p)) + :x2 (max (:x start-p) (:x end-p)) + :y2 (max (:y start-p) (:y end-p))}))) + + block-contains + (fn [x y block] + (if is-col? + (<= (:x1 block) x (:x2 block)) + (<= (:y1 block) y (:y2 block)))) + + get-container-block + (fn [shape] + (let [selrect (:selrect shape) + x (/ (+ (:x1 selrect) (:x2 selrect)) 2) + y (/ (+ (:y1 selrect) (:y2 selrect)) 2)] + (->> wrap-blocks + (filter #(block-contains x y %)) + first))) + + create-cgdd + (fn [shape] + (let [block (get-container-block shape) + x (if flip-x + (- (:x1 (:selrect shape)) + (get-in shape [:layout-item-margin :m2]) + (:column-gap gap)) + (+ (:x2 (:selrect shape)) (get-in shape [:layout-item-margin :m2]))) + y (:y1 block) + h (- (:y2 block) (:y1 block))] + {:x x + :y y + :height h + :width (:column-gap gap) + :initial-value (:column-gap gap) + :resize-type :left + :resize-axis :x + :resize-negate? (:column-gap negate) + :gap-type (if is-col? :row-gap :column-gap)})) + + create-cgdd-block + (fn [block] + (let [x (if flip-x + (- (:x1 block) (:column-gap gap)) + (:x2 block)) + y (if flip-y + (+ y1 (:p3 padding)) + (+ y1 (:p1 padding))) + h (- height (+ (:p1 padding) (:p3 padding)))] + {:x x + :y y + :width (:column-gap gap) + :height h + :initial-value (:column-gap gap) + :resize-type :left + :resize-axis :x + :resize-negate? (:column-gap negate) + :gap-type (if is-col? :column-gap :row-gap)})) + + create-rgdd + (fn [shape] + (let [block (get-container-block shape) + x (:x1 block) + y (if flip-y + (- (:y1 (:selrect shape)) + (get-in shape [:layout-item-margin :m3]) + (:row-gap gap)) + (+ (:y2 (:selrect shape)) (get-in shape [:layout-item-margin :m3]))) + w (- (:x2 block) (:x1 block))] + {:x x + :y y + :width w + :height (:row-gap gap) + :initial-value (:row-gap gap) + :resize-type :bottom + :resize-axis :y + :resize-negate? (:row-gap negate) + :gap-type (if is-col? :row-gap :column-gap)})) + + create-rgdd-block + (fn [block] + (let [x (if flip-x + (+ x1 (:p2 padding)) + (+ x1 (:p4 padding))) + y (if flip-y + (- (:y1 block) (:row-gap gap)) + (:y2 block)) + w (- width (+ (:p2 padding) (:p4 padding)))] + {:x x + :y y + :width w + :height (:row-gap gap) + :initial-value (:row-gap gap) + :resize-type :bottom + :resize-axis :y + :resize-negate? (:row-gap negate) + :gap-type (if is-col? :column-gap :row-gap)})) + + display-blocks (if is-col? + (->> (drop-last wrap-blocks) + (map create-cgdd-block)) + (->> (drop-last wrap-blocks) + (map create-rgdd-block))) + + display-children (if is-col? + (->> children-to-display + (map create-rgdd)) + (->> children-to-display + (map create-cgdd)))] + + [:g.gaps {:pointer-events "visible"} + (for [[index display-item] (d/enumerate (concat display-blocks display-children))] + (let [gap-type (:gap-type display-item)] + [:& gap-display {:key (str frame-id index) + :frame-id frame-id + :zoom zoom + :gap-type gap-type + :gap gap + :on-pointer-enter (partial on-pointer-enter gap-type (get gap gap-type)) + :on-pointer-leave on-pointer-leave + :on-move-selected on-move-selected + :on-context-menu on-context-menu + :rect-data display-item + :hover? (= @hover gap-type) + :selected? (= gap-selected gap-type) + :mouse-pos mouse-pos + :hover-value hover-value}])) + + (when @hover + [:& fcc/flex-display-pill + {:height pill-height + :width pill-width + :font-size (/ fcc/font-size zoom) + :border-radius (/ fcc/flex-display-pill-border-radius zoom) + :color fcc/distance-color + :x (:x @mouse-pos) + :y (- (:y @mouse-pos) pill-width) + :value @hover-value}])])) + + +(mf/defc gap-control + [{:keys [frame zoom on-move-selected on-context-menu]}] + (when frame + [:g.measurement-gaps {:pointer-events "none"} + [:g.hover-shapes + [:& gap-rects + {:frame frame + :zoom zoom + :on-move-selected on-move-selected + :on-context-menu on-context-menu}]]])) diff --git a/frontend/src/app/main/ui/flex_controls/margin.cljs b/frontend/src/app/main/ui/flex_controls/margin.cljs new file mode 100644 index 000000000..5ee7d33c5 --- /dev/null +++ b/frontend/src/app/main/ui/flex_controls/margin.cljs @@ -0,0 +1,185 @@ +;; 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.flex-controls.margin + (:require + [app.common.geom.point :as gpt] + [app.common.types.modifiers :as ctm] + [app.main.data.workspace.modifiers :as dwm] + [app.main.refs :as refs] + [app.main.store :as st] + [app.main.ui.css-cursors :as cur] + [app.main.ui.flex-controls.common :as fcc] + [app.main.ui.workspace.viewport.viewport-ref :refer [point->viewport]] + [app.util.dom :as dom] + [rumext.v2 :as mf])) + +(mf/defc margin-display [{:keys [shape-id zoom hover-all? hover-v? hover-h? margin-num margin on-pointer-enter on-pointer-leave + rect-data hover? selected? mouse-pos hover-value]}] + (let [resizing? (mf/use-var false) + start (mf/use-var nil) + original-value (mf/use-var 0) + negate? (true? (:resize-negate? rect-data)) + axis (:resize-axis rect-data) + + on-pointer-down + (mf/use-fn + (mf/deps shape-id margin-num margin) + (fn [event] + (dom/capture-pointer event) + (reset! resizing? true) + (reset! start (dom/get-client-position event)) + (reset! original-value (:initial-value rect-data)))) + + on-lost-pointer-capture + (mf/use-fn + (mf/deps shape-id margin-num margin) + (fn [event] + (dom/release-pointer event) + (reset! resizing? false) + (reset! start nil) + (reset! original-value 0) + (st/emit! (dwm/apply-modifiers)))) + + on-pointer-move + (mf/use-fn + (mf/deps shape-id margin-num margin hover-all? hover-v? hover-h?) + (fn [event] + (let [pos (dom/get-client-position event)] + (reset! mouse-pos (point->viewport pos)) + (when @resizing? + (let [delta (-> (gpt/to-vec @start pos) + (cond-> negate? gpt/negate) + (get axis)) + val (int (max (+ @original-value (/ delta zoom)) 0)) + layout-item-margin (cond + hover-all? (assoc margin :m1 val :m2 val :m3 val :m4 val) + hover-v? (assoc margin :m1 val :m3 val) + hover-h? (assoc margin :m2 val :m4 val) + :else (assoc margin margin-num val)) + layout-item-margin-type (if (= (:m1 margin) (:m2 margin) (:m3 margin) (:m4 margin)) :simple :multiple) + modifiers (dwm/create-modif-tree [shape-id] + (-> (ctm/empty) + (ctm/change-property :layout-item-margin layout-item-margin) + (ctm/change-property :layout-item-margin-type layout-item-margin-type)))] + (reset! hover-value val) + (st/emit! (dwm/set-modifiers modifiers)))))))] + + [:rect.margin-rect + {:x (:x rect-data) + :y (:y rect-data) + :width (:width rect-data) + :height (:height rect-data) + :on-pointer-enter on-pointer-enter + :on-pointer-leave on-pointer-leave + :on-pointer-down on-pointer-down + :on-lost-pointer-capture on-lost-pointer-capture + :on-pointer-move on-pointer-move + :class (when (or hover? selected?) + (if (= (:resize-axis rect-data) :x) (cur/get-dynamic "resize-ew" 0) (cur/get-dynamic "resize-ew" 90))) + :style {:fill (if (or hover? selected?) fcc/warning-color "none") + :opacity (if selected? 0.5 0.25)}}])) + + +(mf/defc margin-rects [{:keys [shape frame zoom alt? shift?]}] + (let [shape-id (:id shape) + pill-width (/ fcc/flex-display-pill-width zoom) + pill-height (/ fcc/flex-display-pill-height zoom) + margins-selected (mf/deref refs/workspace-margins-selected) + hover-value (mf/use-var 0) + mouse-pos (mf/use-var nil) + hover (mf/use-var nil) + hover-all? (and (not (nil? @hover)) alt?) + hover-v? (and (or (= @hover :m1) (= @hover :m3)) shift?) + hover-h? (and (or (= @hover :m2) (= @hover :m4)) shift?) + margin (:layout-item-margin shape) + {:keys [width height x1 x2 y1 y2]} (:selrect shape) + on-pointer-enter (fn [hover-type val] + (reset! hover hover-type) + (reset! hover-value val)) + on-pointer-leave #(reset! hover nil) + hover? #(or hover-all? + (and (or (= % :m1) (= % :m3)) hover-v?) + (and (or (= % :m2) (= % :m4)) hover-h?) + (= @hover %)) + margin-display-data {:m1 {:key (str shape-id "-m1") + :x x1 + :y (if (:flip-y frame) y2 (- y1 (:m1 margin))) + :width width + :height (:m1 margin) + :initial-value (:m1 margin) + :resize-type :top + :resize-axis :y + :resize-negate? (:flip-y frame)} + :m2 {:key (str shape-id "-m2") + :x (if (:flip-x frame) (- x1 (:m2 margin)) x2) + :y y1 + :width (:m2 margin) + :height height + :initial-value (:m2 margin) + :resize-type :left + :resize-axis :x + :resize-negate? (:flip-x frame)} + :m3 {:key (str shape-id "-m3") + :x x1 + :y (if (:flip-y frame) (- y1 (:m3 margin)) y2) + :width width + :height (:m3 margin) + :initial-value (:m3 margin) + :resize-type :top + :resize-axis :y + :resize-negate? (:flip-y frame)} + :m4 {:key (str shape-id "-m4") + :x (if (:flip-x frame) x2 (- x1 (:m4 margin))) + :y y1 + :width (:m4 margin) + :height height + :initial-value (:m4 margin) + :resize-type :left + :resize-axis :x + :resize-negate? (:flip-x frame)}}] + + [:g.margins {:pointer-events "visible"} + (for [[margin-num rect-data] margin-display-data] + [:& margin-display + {:key (:key rect-data) + :shape-id shape-id + :zoom zoom + :hover-all? hover-all? + :hover-v? hover-v? + :hover-h? hover-h? + :margin-num margin-num + :margin margin + :on-pointer-enter (partial on-pointer-enter margin-num (get margin margin-num)) + :on-pointer-leave on-pointer-leave + :rect-data rect-data + :hover? (hover? margin-num) + :selected? (get margins-selected margin-num) + :mouse-pos mouse-pos + :hover-value hover-value}]) + + (when @hover + [:& fcc/flex-display-pill + {:height pill-height + :width pill-width + :font-size (/ fcc/font-size zoom) + :border-radius (/ fcc/flex-display-pill-border-radius zoom) + :color fcc/warning-color + :x (:x @mouse-pos) + :y (- (:y @mouse-pos) pill-width) + :value @hover-value}])])) + +(mf/defc margin-control + [{:keys [shape parent zoom alt? shift?]}] + (when shape + [:g.measurement-gaps {:pointer-events "none"} + [:g.hover-shapes + [:& margin-rects + {:shape shape + :frame parent + :zoom zoom + :alt? alt? + :shift? shift?}]]])) diff --git a/frontend/src/app/main/ui/flex_controls/padding.cljs b/frontend/src/app/main/ui/flex_controls/padding.cljs new file mode 100644 index 000000000..5b64c94fb --- /dev/null +++ b/frontend/src/app/main/ui/flex_controls/padding.cljs @@ -0,0 +1,223 @@ +;; 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.flex-controls.padding + (:require + [app.common.geom.point :as gpt] + [app.common.types.modifiers :as ctm] + [app.main.data.workspace.modifiers :as dwm] + [app.main.refs :as refs] + [app.main.store :as st] + [app.main.ui.css-cursors :as cur] + [app.main.ui.flex-controls.common :as fcc] + [app.main.ui.workspace.viewport.viewport-ref :refer [point->viewport]] + [app.util.dom :as dom] + [rumext.v2 :as mf])) + +(mf/defc padding-display + [{:keys [frame-id zoom hover-all? hover-v? hover-h? padding-num padding on-pointer-enter on-pointer-leave + rect-data hover? selected? mouse-pos hover-value on-move-selected on-context-menu]}] + (let [resizing? (mf/use-var false) + start (mf/use-var nil) + original-value (mf/use-var 0) + negate? (true? (:resize-negate? rect-data)) + axis (:resize-axis rect-data) + + on-pointer-down + (mf/use-fn + (mf/deps frame-id rect-data padding-num) + (fn [event] + (dom/capture-pointer event) + (reset! resizing? true) + (reset! start (dom/get-client-position event)) + (reset! original-value (:initial-value rect-data)))) + + on-lost-pointer-capture + (mf/use-fn + (mf/deps frame-id padding-num padding) + (fn [event] + (dom/release-pointer event) + (reset! resizing? false) + (reset! start nil) + (reset! original-value 0) + (st/emit! (dwm/apply-modifiers)))) + + on-pointer-move + (mf/use-fn + (mf/deps frame-id padding-num padding hover-all? hover-v? hover-h?) + (fn [event] + (let [pos (dom/get-client-position event)] + (reset! mouse-pos (point->viewport pos)) + (when @resizing? + (let [delta (-> (gpt/to-vec @start pos) + (cond-> negate? gpt/negate) + (get axis)) + val (int (max (+ @original-value (/ delta zoom)) 0)) + layout-padding (cond + hover-all? (assoc padding :p1 val :p2 val :p3 val :p4 val) + hover-v? (assoc padding :p1 val :p3 val) + hover-h? (assoc padding :p2 val :p4 val) + :else (assoc padding padding-num val)) + + + layout-padding-type (if (= (:p1 padding) (:p2 padding) (:p3 padding) (:p4 padding)) :simple :multiple) + modifiers (dwm/create-modif-tree [frame-id] + (-> (ctm/empty) + (ctm/change-property :layout-padding layout-padding) + (ctm/change-property :layout-padding-type layout-padding-type)))] + (reset! hover-value val) + (st/emit! (dwm/set-modifiers modifiers)))))))] + + [:g.padding-rect + [:rect.info-area + {:x (:x rect-data) + :y (:y rect-data) + :width (max 0 (:width rect-data)) + :height (max 0 (:height rect-data)) + + :on-pointer-enter on-pointer-enter + :on-pointer-leave on-pointer-leave + :on-pointer-move on-pointer-move + :on-pointer-down on-move-selected + :on-context-menu on-context-menu + :style {:fill (if (or hover? selected?) fcc/distance-color "none") + :opacity (if selected? 0.5 0.25)}}] + + (let [handle-width + (if (= axis :x) + (/ 2 zoom) + (min (* (:width rect-data) 0.5) (/ 20 zoom))) + + handle-height + (if (= axis :y) + (/ 2 zoom) + (min (* (:height rect-data) 0.5) (/ 30 zoom)))] + [:rect.handle + {:x (+ (:x rect-data) (/ (- (:width rect-data) handle-width) 2)) + :y (+ (:y rect-data) (/ (- (:height rect-data) handle-height) 2)) + :width handle-width + :height handle-height + :on-pointer-enter on-pointer-enter + :on-pointer-leave on-pointer-leave + :on-pointer-down on-pointer-down + :on-lost-pointer-capture on-lost-pointer-capture + :on-pointer-move on-pointer-move + :on-context-menu on-context-menu + :class (when (or hover? selected?) + (if (= (:resize-axis rect-data) :x) (cur/get-dynamic "resize-ew" 0) (cur/get-dynamic "resize-ew" 90))) + :style {:fill (if (or hover? selected?) fcc/distance-color "none") + :opacity (if selected? 0 1)}}])])) + +(mf/defc padding-rects + [{:keys [frame zoom alt? shift? on-move-selected on-context-menu]}] + (let [frame-id (:id frame) + paddings-selected (mf/deref refs/workspace-paddings-selected) + hover-value (mf/use-var 0) + mouse-pos (mf/use-var nil) + hover (mf/use-var nil) + hover-all? (and (not (nil? @hover)) alt?) + hover-v? (and (or (= @hover :p1) (= @hover :p3)) shift?) + hover-h? (and (or (= @hover :p2) (= @hover :p4)) shift?) + padding (:layout-padding frame) + {:keys [width height x1 x2 y1 y2]} (:selrect frame) + on-pointer-enter (fn [hover-type val] + (reset! hover hover-type) + (reset! hover-value val)) + on-pointer-leave #(reset! hover nil) + pill-width (/ fcc/flex-display-pill-width zoom) + pill-height (/ fcc/flex-display-pill-height zoom) + hover? #(or hover-all? + (and (or (= % :p1) (= % :p3)) hover-v?) + (and (or (= % :p2) (= % :p4)) hover-h?) + (= @hover %)) + negate {:p1 (if (:flip-y frame) true false) + :p2 (if (:flip-x frame) true false) + :p3 (if (:flip-y frame) true false) + :p4 (if (:flip-x frame) true false)} + negate (cond-> negate + (not= :auto (:layout-item-h-sizing frame)) (assoc :p2 (not (:p2 negate))) + (not= :auto (:layout-item-v-sizing frame)) (assoc :p3 (not (:p3 negate)))) + + padding-rect-data {:p1 {:key (str frame-id "-p1") + :x x1 + :y (if (:flip-y frame) (- y2 (:p1 padding)) y1) + :width width + :height (:p1 padding) + :initial-value (:p1 padding) + :resize-type (if (:flip-y frame) :bottom :top) + :resize-axis :y + :resize-negate? (:p1 negate)} + :p2 {:key (str frame-id "-p2") + :x (if (:flip-x frame) x1 (- x2 (:p2 padding))) + :y y1 + :width (:p2 padding) + :height height + :initial-value (:p2 padding) + :resize-type :left + :resize-axis :x + :resize-negate? (:p2 negate)} + :p3 {:key (str frame-id "-p3") + :x x1 + :y (if (:flip-y frame) y1 (- y2 (:p3 padding))) + :width width + :height (:p3 padding) + :initial-value (:p3 padding) + :resize-type :bottom + :resize-axis :y + :resize-negate? (:p3 negate)} + :p4 {:key (str frame-id "-p4") + :x (if (:flip-x frame) (- x2 (:p4 padding)) x1) + :y y1 + :width (:p4 padding) + :height height + :initial-value (:p4 padding) + :resize-type (if (:flip-x frame) :right :left) + :resize-axis :x + :resize-negate? (:p4 negate)}}] + + [:g.paddings {:pointer-events "visible"} + (for [[padding-num rect-data] padding-rect-data] + [:& padding-display + {:key (:key rect-data) + :frame-id frame-id + :zoom zoom + :hover-all? hover-all? + :hover-v? hover-v? + :hover-h? hover-h? + :padding padding + :mouse-pos mouse-pos + :hover-value hover-value + :padding-num padding-num + :on-pointer-enter (partial on-pointer-enter padding-num (get padding padding-num)) + :on-pointer-leave on-pointer-leave + :on-move-selected on-move-selected + :on-context-menu on-context-menu + :hover? (hover? padding-num) + :selected? (get paddings-selected padding-num) + :rect-data rect-data}]) + (when @hover + [:& fcc/flex-display-pill + {:height pill-height + :width pill-width + :font-size (/ fcc/font-size zoom) + :border-radius (/ fcc/flex-display-pill-border-radius zoom) + :color fcc/distance-color + :x (:x @mouse-pos) + :y (- (:y @mouse-pos) pill-width) + :value @hover-value}])])) + +(mf/defc padding-control + [{:keys [frame zoom alt? shift? on-move-selected on-context-menu]}] + (when frame + [:g.measurement-gaps {:pointer-events "none"} + [:g.hover-shapes + [:& padding-rects + {:frame frame + :zoom zoom + :alt? alt? + :shift? shift? + :on-move-selected on-move-selected + :on-context-menu on-context-menu}]]])) diff --git a/frontend/src/app/main/ui/measurements.cljs b/frontend/src/app/main/ui/measurements.cljs index 057794e30..9c4f9719c 100644 --- a/frontend/src/app/main/ui/measurements.cljs +++ b/frontend/src/app/main/ui/measurements.cljs @@ -8,24 +8,12 @@ (:require [app.common.data :as d] [app.common.data.macros :as dm] - [app.common.files.helpers :as cfh] [app.common.geom.point :as gpt] [app.common.geom.rect :as grc] [app.common.geom.shapes :as gsh] - [app.common.geom.shapes.flex-layout :as gsl] - [app.common.geom.shapes.points :as gpo] [app.common.math :as mth] - [app.common.types.modifiers :as ctm] - [app.common.types.shape.layout :as ctl] [app.common.uuid :as uuid] - [app.main.data.workspace.modifiers :as dwm] - [app.main.data.workspace.state-helpers :as wsh] - [app.main.refs :as refs] - [app.main.store :as st] - [app.main.ui.css-cursors :as cur] [app.main.ui.formats :as fmt] - [app.main.ui.workspace.viewport.viewport-ref :refer [point->viewport]] - [app.util.dom :as dom] [rumext.v2 :as mf])) ;; ------------------------------------------------ @@ -57,9 +45,6 @@ (def distance-pill-height 16) (def distance-line-stroke 1) (def warning-color "var(--color-warning)") -(def flex-display-pill-width 40) -(def flex-display-pill-height 20) -(def flex-display-pill-border-radius 4) ;; ------------------------------------------------ ;; HELPERS @@ -277,600 +262,3 @@ [:& size-display {:selrect hover-selrect :zoom zoom}] [:& distance-display {:from hover-selrect :to selected-selrect :zoom zoom :bounds bounds-selrect}]])]))) - - -(mf/defc flex-display-pill [{:keys [x y width height font-size border-radius value color]}] - [:g.distance-pill - [:rect {:x x - :y y - :width width - :height height - :rx border-radius - :ry border-radius - :style {:fill color}}] - - [:text {:x (+ x (/ width 2)) - :y (+ y (/ height 2)) - :text-anchor "middle" - :dominant-baseline "central" - :style {:fill distance-text-color - :font-size font-size}} - (fmt/format-number (or value 0))]]) - - -(mf/defc padding-display [{:keys [frame-id zoom hover-all? hover-v? hover-h? padding-num padding on-pointer-enter on-pointer-leave - rect-data hover? selected? mouse-pos hover-value]}] - (let [resizing? (mf/use-var false) - start (mf/use-var nil) - original-value (mf/use-var 0) - negate? (true? (:resize-negate? rect-data)) - axis (:resize-axis rect-data) - - on-pointer-down - (mf/use-fn - (mf/deps frame-id rect-data padding-num) - (fn [event] - (dom/capture-pointer event) - (reset! resizing? true) - (reset! start (dom/get-client-position event)) - (reset! original-value (:initial-value rect-data)))) - - on-lost-pointer-capture - (mf/use-fn - (mf/deps frame-id padding-num padding) - (fn [event] - (dom/release-pointer event) - (reset! resizing? false) - (reset! start nil) - (reset! original-value 0) - (st/emit! (dwm/apply-modifiers)))) - - on-pointer-move - (mf/use-fn - (mf/deps frame-id padding-num padding hover-all? hover-v? hover-h?) - (fn [event] - (let [pos (dom/get-client-position event)] - (reset! mouse-pos (point->viewport pos)) - (when @resizing? - (let [delta (-> (gpt/to-vec @start pos) - (cond-> negate? gpt/negate) - (get axis)) - val (int (max (+ @original-value (/ delta zoom)) 0)) - layout-padding (cond - hover-all? (assoc padding :p1 val :p2 val :p3 val :p4 val) - hover-v? (assoc padding :p1 val :p3 val) - hover-h? (assoc padding :p2 val :p4 val) - :else (assoc padding padding-num val)) - - - layout-padding-type (if (= (:p1 padding) (:p2 padding) (:p3 padding) (:p4 padding)) :simple :multiple) - modifiers (dwm/create-modif-tree [frame-id] - (-> (ctm/empty) - (ctm/change-property :layout-padding layout-padding) - (ctm/change-property :layout-padding-type layout-padding-type)))] - (reset! hover-value val) - (st/emit! (dwm/set-modifiers modifiers)))))))] - - [:rect.padding-rect {:x (:x rect-data) - :y (:y rect-data) - :width (max 0 (:width rect-data)) - :height (max 0 (:height rect-data)) - :on-pointer-enter on-pointer-enter - :on-pointer-leave on-pointer-leave - :on-pointer-down on-pointer-down - :on-lost-pointer-capture on-lost-pointer-capture - :on-pointer-move on-pointer-move - :class (when (or hover? selected?) - (if (= (:resize-axis rect-data) :x) (cur/get-dynamic "resize-ew" 0) (cur/get-dynamic "resize-ew" 90))) - :style {:fill (if (or hover? selected?) distance-color "none") - :opacity (if selected? 0.5 0.25)}}])) - -(mf/defc padding-rects [{:keys [frame zoom alt? shift?]}] - (let [frame-id (:id frame) - paddings-selected (mf/deref refs/workspace-paddings-selected) - hover-value (mf/use-var 0) - mouse-pos (mf/use-var nil) - hover (mf/use-var nil) - hover-all? (and (not (nil? @hover)) alt?) - hover-v? (and (or (= @hover :p1) (= @hover :p3)) shift?) - hover-h? (and (or (= @hover :p2) (= @hover :p4)) shift?) - padding (:layout-padding frame) - {:keys [width height x1 x2 y1 y2]} (:selrect frame) - on-pointer-enter (fn [hover-type val] - (reset! hover hover-type) - (reset! hover-value val)) - on-pointer-leave #(reset! hover nil) - pill-width (/ flex-display-pill-width zoom) - pill-height (/ flex-display-pill-height zoom) - hover? #(or hover-all? - (and (or (= % :p1) (= % :p3)) hover-v?) - (and (or (= % :p2) (= % :p4)) hover-h?) - (= @hover %)) - negate {:p1 (if (:flip-y frame) true false) - :p2 (if (:flip-x frame) true false) - :p3 (if (:flip-y frame) true false) - :p4 (if (:flip-x frame) true false)} - negate (cond-> negate - (not= :auto (:layout-item-h-sizing frame)) (assoc :p2 (not (:p2 negate))) - (not= :auto (:layout-item-v-sizing frame)) (assoc :p3 (not (:p3 negate)))) - - padding-rect-data {:p1 {:key (str frame-id "-p1") - :x x1 - :y (if (:flip-y frame) (- y2 (:p1 padding)) y1) - :width width - :height (:p1 padding) - :initial-value (:p1 padding) - :resize-type (if (:flip-y frame) :bottom :top) - :resize-axis :y - :resize-negate? (:p1 negate)} - :p2 {:key (str frame-id "-p2") - :x (if (:flip-x frame) x1 (- x2 (:p2 padding))) - :y y1 - :width (:p2 padding) - :height height - :initial-value (:p2 padding) - :resize-type :left - :resize-axis :x - :resize-negate? (:p2 negate)} - :p3 {:key (str frame-id "-p3") - :x x1 - :y (if (:flip-y frame) y1 (- y2 (:p3 padding))) - :width width - :height (:p3 padding) - :initial-value (:p3 padding) - :resize-type :bottom - :resize-axis :y - :resize-negate? (:p3 negate)} - :p4 {:key (str frame-id "-p4") - :x (if (:flip-x frame) (- x2 (:p4 padding)) x1) - :y y1 - :width (:p4 padding) - :height height - :initial-value (:p4 padding) - :resize-type (if (:flip-x frame) :right :left) - :resize-axis :x - :resize-negate? (:p4 negate)}}] - - [:g.paddings {:pointer-events "visible"} - (for [[padding-num rect-data] padding-rect-data] - [:& padding-display {:key (:key rect-data) - :frame-id frame-id - :zoom zoom - :hover-all? hover-all? - :hover-v? hover-v? - :hover-h? hover-h? - :padding padding - :mouse-pos mouse-pos - :hover-value hover-value - :padding-num padding-num - :on-pointer-enter (partial on-pointer-enter padding-num (get padding padding-num)) - :on-pointer-leave on-pointer-leave - :hover? (hover? padding-num) - :selected? (get paddings-selected padding-num) - :rect-data rect-data}]) - (when @hover - [:& flex-display-pill {:height pill-height - :width pill-width - :font-size (/ font-size zoom) - :border-radius (/ flex-display-pill-border-radius zoom) - :color distance-color - :x (:x @mouse-pos) - :y (- (:y @mouse-pos) pill-width) - :value @hover-value}])])) - -(mf/defc margin-display [{:keys [shape-id zoom hover-all? hover-v? hover-h? margin-num margin on-pointer-enter on-pointer-leave - rect-data hover? selected? mouse-pos hover-value]}] - (let [resizing? (mf/use-var false) - start (mf/use-var nil) - original-value (mf/use-var 0) - negate? (true? (:resize-negate? rect-data)) - axis (:resize-axis rect-data) - - on-pointer-down - (mf/use-fn - (mf/deps shape-id margin-num margin) - (fn [event] - (dom/capture-pointer event) - (reset! resizing? true) - (reset! start (dom/get-client-position event)) - (reset! original-value (:initial-value rect-data)))) - - on-lost-pointer-capture - (mf/use-fn - (mf/deps shape-id margin-num margin) - (fn [event] - (dom/release-pointer event) - (reset! resizing? false) - (reset! start nil) - (reset! original-value 0) - (st/emit! (dwm/apply-modifiers)))) - - on-pointer-move - (mf/use-fn - (mf/deps shape-id margin-num margin hover-all? hover-v? hover-h?) - (fn [event] - (let [pos (dom/get-client-position event)] - (reset! mouse-pos (point->viewport pos)) - (when @resizing? - (let [delta (-> (gpt/to-vec @start pos) - (cond-> negate? gpt/negate) - (get axis)) - val (int (max (+ @original-value (/ delta zoom)) 0)) - layout-item-margin (cond - hover-all? (assoc margin :m1 val :m2 val :m3 val :m4 val) - hover-v? (assoc margin :m1 val :m3 val) - hover-h? (assoc margin :m2 val :m4 val) - :else (assoc margin margin-num val)) - layout-item-margin-type (if (= (:m1 margin) (:m2 margin) (:m3 margin) (:m4 margin)) :simple :multiple) - modifiers (dwm/create-modif-tree [shape-id] - (-> (ctm/empty) - (ctm/change-property :layout-item-margin layout-item-margin) - (ctm/change-property :layout-item-margin-type layout-item-margin-type)))] - (reset! hover-value val) - (st/emit! (dwm/set-modifiers modifiers)))))))] - - [:rect.margin-rect {:x (:x rect-data) - :y (:y rect-data) - :width (:width rect-data) - :height (:height rect-data) - :on-pointer-enter on-pointer-enter - :on-pointer-leave on-pointer-leave - :on-pointer-down on-pointer-down - :on-lost-pointer-capture on-lost-pointer-capture - :on-pointer-move on-pointer-move - :class (when (or hover? selected?) - (if (= (:resize-axis rect-data) :x) (cur/get-dynamic "resize-ew" 0) (cur/get-dynamic "resize-ew" 90))) - :style {:fill (if (or hover? selected?) warning-color "none") - :opacity (if selected? 0.5 0.25)}}])) - -(mf/defc margin-rects [{:keys [shape frame zoom alt? shift?]}] - (let [shape-id (:id shape) - pill-width (/ flex-display-pill-width zoom) - pill-height (/ flex-display-pill-height zoom) - margins-selected (mf/deref refs/workspace-margins-selected) - hover-value (mf/use-var 0) - mouse-pos (mf/use-var nil) - hover (mf/use-var nil) - hover-all? (and (not (nil? @hover)) alt?) - hover-v? (and (or (= @hover :m1) (= @hover :m3)) shift?) - hover-h? (and (or (= @hover :m2) (= @hover :m4)) shift?) - margin (:layout-item-margin shape) - {:keys [width height x1 x2 y1 y2]} (:selrect shape) - on-pointer-enter (fn [hover-type val] - (reset! hover hover-type) - (reset! hover-value val)) - on-pointer-leave #(reset! hover nil) - hover? #(or hover-all? - (and (or (= % :m1) (= % :m3)) hover-v?) - (and (or (= % :m2) (= % :m4)) hover-h?) - (= @hover %)) - margin-display-data {:m1 {:key (str shape-id "-m1") - :x x1 - :y (if (:flip-y frame) y2 (- y1 (:m1 margin))) - :width width - :height (:m1 margin) - :initial-value (:m1 margin) - :resize-type :top - :resize-axis :y - :resize-negate? (:flip-y frame)} - :m2 {:key (str shape-id "-m2") - :x (if (:flip-x frame) (- x1 (:m2 margin)) x2) - :y y1 - :width (:m2 margin) - :height height - :initial-value (:m2 margin) - :resize-type :left - :resize-axis :x - :resize-negate? (:flip-x frame)} - :m3 {:key (str shape-id "-m3") - :x x1 - :y (if (:flip-y frame) (- y1 (:m3 margin)) y2) - :width width - :height (:m3 margin) - :initial-value (:m3 margin) - :resize-type :top - :resize-axis :y - :resize-negate? (:flip-y frame)} - :m4 {:key (str shape-id "-m4") - :x (if (:flip-x frame) x2 (- x1 (:m4 margin))) - :y y1 - :width (:m4 margin) - :height height - :initial-value (:m4 margin) - :resize-type :left - :resize-axis :x - :resize-negate? (:flip-x frame)}}] - - [:g.margins {:pointer-events "visible"} - (for [[margin-num rect-data] margin-display-data] - [:& margin-display - {:key (:key rect-data) - :shape-id shape-id - :zoom zoom - :hover-all? hover-all? - :hover-v? hover-v? - :hover-h? hover-h? - :margin-num margin-num - :margin margin - :on-pointer-enter (partial on-pointer-enter margin-num (get margin margin-num)) - :on-pointer-leave on-pointer-leave - :rect-data rect-data - :hover? (hover? margin-num) - :selected? (get margins-selected margin-num) - :mouse-pos mouse-pos - :hover-value hover-value}]) - - (when @hover - [:& flex-display-pill {:height pill-height - :width pill-width - :font-size (/ font-size zoom) - :border-radius (/ flex-display-pill-border-radius zoom) - :color warning-color - :x (:x @mouse-pos) - :y (- (:y @mouse-pos) pill-width) - :value @hover-value}])])) - -(mf/defc gap-display [{:keys [frame-id zoom gap-type gap on-pointer-enter on-pointer-leave - rect-data hover? selected? mouse-pos hover-value]}] - (let [resizing (mf/use-var nil) - start (mf/use-var nil) - original-value (mf/use-var 0) - negate? (:resize-negate? rect-data) - axis (:resize-axis rect-data) - - on-pointer-down - (mf/use-fn - (mf/deps frame-id gap-type gap) - (fn [event] - (dom/capture-pointer event) - (reset! resizing gap-type) - (reset! start (dom/get-client-position event)) - (reset! original-value (:initial-value rect-data)))) - - on-lost-pointer-capture - (mf/use-fn - (mf/deps frame-id gap-type gap) - (fn [event] - (dom/release-pointer event) - (reset! resizing nil) - (reset! start nil) - (reset! original-value 0) - (st/emit! (dwm/apply-modifiers)))) - - on-pointer-move - (mf/use-fn - (mf/deps frame-id gap-type gap) - (fn [event] - (let [pos (dom/get-client-position event)] - (reset! mouse-pos (point->viewport pos)) - (when (= @resizing gap-type) - (let [delta (-> (gpt/to-vec @start pos) - (cond-> negate? gpt/negate) - (get axis)) - val (int (max (+ @original-value (/ delta zoom)) 0)) - layout-gap (assoc gap gap-type val) - modifiers (dwm/create-modif-tree [frame-id] (ctm/change-property (ctm/empty) :layout-gap layout-gap))] - - (reset! hover-value val) - (st/emit! (dwm/set-modifiers modifiers)))))))] - - [:rect.gap-rect {:x (:x rect-data) - :y (:y rect-data) - :width (:width rect-data) - :height (:height rect-data) - :on-pointer-enter on-pointer-enter - :on-pointer-leave on-pointer-leave - :on-pointer-down on-pointer-down - :on-lost-pointer-capture on-lost-pointer-capture - :on-pointer-move on-pointer-move - :class (when (or hover? selected?) - (if (= (:resize-axis rect-data) :x) (cur/get-dynamic "resize-ew" 0) (cur/get-dynamic "resize-ew" 90))) - :style {:fill (if (or hover? selected?) distance-color "none") - :opacity (if selected? 0.5 0.25)}}])) - -(mf/defc gap-rects [{:keys [frame zoom]}] - (let [frame-id (:id frame) - saved-dir (:layout-flex-dir frame) - is-col? (or (= :column saved-dir) (= :column-reverse saved-dir)) - flip-x (:flip-x frame) - flip-y (:flip-y frame) - pill-width (/ flex-display-pill-width zoom) - pill-height (/ flex-display-pill-height zoom) - workspace-modifiers (mf/deref refs/workspace-modifiers) - gap-selected (mf/deref refs/workspace-gap-selected) - hover (mf/use-var nil) - hover-value (mf/use-var 0) - mouse-pos (mf/use-var nil) - padding (:layout-padding frame) - gap (:layout-gap frame) - {:keys [width height x1 y1]} (:selrect frame) - on-pointer-enter (fn [hover-type val] - (reset! hover hover-type) - (reset! hover-value val)) - - on-pointer-leave #(reset! hover nil) - negate {:column-gap (if flip-x true false) - :row-gap (if flip-y true false)} - - objects (wsh/lookup-page-objects @st/state) - children (->> (cfh/get-immediate-children objects frame-id) - (remove ctl/position-absolute?)) - - children-to-display (if (or (= :row-reverse saved-dir) - (= :column-reverse saved-dir)) - (drop-last children) - (rest children)) - children-to-display (->> children-to-display - (map #(gsh/transform-shape % (get-in workspace-modifiers [(:id %) :modifiers])))) - - wrap-blocks - (let [block-children (->> children - (map #(vector (gpo/parent-coords-bounds (:points %) (:points frame)) %))) - bounds (d/lazy-map (keys objects) #(gsh/shape->points (get objects %))) - - layout-data (gsl/calc-layout-data frame (:points frame) block-children bounds objects) - layout-bounds (:layout-bounds layout-data) - xv #(gpo/start-hv layout-bounds %) - yv #(gpo/start-vv layout-bounds %)] - (for [{:keys [start-p line-width line-height layout-gap-row layout-gap-col num-children]} (:layout-lines layout-data)] - (let [line-width (if is-col? line-width (+ line-width (* (dec num-children) layout-gap-row))) - line-height (if is-col? (+ line-height (* (dec num-children) layout-gap-col)) line-height) - end-p (-> start-p (gpt/add (xv line-width)) (gpt/add (yv line-height)))] - {:x1 (min (:x start-p) (:x end-p)) - :y1 (min (:y start-p) (:y end-p)) - :x2 (max (:x start-p) (:x end-p)) - :y2 (max (:y start-p) (:y end-p))}))) - - block-contains - (fn [x y block] - (if is-col? - (<= (:x1 block) x (:x2 block)) - (<= (:y1 block) y (:y2 block)))) - - get-container-block - (fn [shape] - (let [selrect (:selrect shape) - x (/ (+ (:x1 selrect) (:x2 selrect)) 2) - y (/ (+ (:y1 selrect) (:y2 selrect)) 2)] - (->> wrap-blocks - (filter #(block-contains x y %)) - first))) - - create-cgdd - (fn [shape] - (let [block (get-container-block shape) - x (if flip-x - (- (:x1 (:selrect shape)) - (get-in shape [:layout-item-margin :m2]) - (:column-gap gap)) - (+ (:x2 (:selrect shape)) (get-in shape [:layout-item-margin :m2]))) - y (:y1 block) - h (- (:y2 block) (:y1 block))] - {:x x - :y y - :height h - :width (:column-gap gap) - :initial-value (:column-gap gap) - :resize-type :left - :resize-axis :x - :resize-negate? (:column-gap negate) - :gap-type (if is-col? :row-gap :column-gap)})) - - create-cgdd-block - (fn [block] - (let [x (if flip-x - (- (:x1 block) (:column-gap gap)) - (:x2 block)) - y (if flip-y - (+ y1 (:p3 padding)) - (+ y1 (:p1 padding))) - h (- height (+ (:p1 padding) (:p3 padding)))] - {:x x - :y y - :width (:column-gap gap) - :height h - :initial-value (:column-gap gap) - :resize-type :left - :resize-axis :x - :resize-negate? (:column-gap negate) - :gap-type (if is-col? :column-gap :row-gap)})) - - create-rgdd - (fn [shape] - (let [block (get-container-block shape) - x (:x1 block) - y (if flip-y - (- (:y1 (:selrect shape)) - (get-in shape [:layout-item-margin :m3]) - (:row-gap gap)) - (+ (:y2 (:selrect shape)) (get-in shape [:layout-item-margin :m3]))) - w (- (:x2 block) (:x1 block))] - {:x x - :y y - :width w - :height (:row-gap gap) - :initial-value (:row-gap gap) - :resize-type :bottom - :resize-axis :y - :resize-negate? (:row-gap negate) - :gap-type (if is-col? :row-gap :column-gap)})) - - create-rgdd-block - (fn [block] - (let [x (if flip-x - (+ x1 (:p2 padding)) - (+ x1 (:p4 padding))) - y (if flip-y - (- (:y1 block) (:row-gap gap)) - (:y2 block)) - w (- width (+ (:p2 padding) (:p4 padding)))] - {:x x - :y y - :width w - :height (:row-gap gap) - :initial-value (:row-gap gap) - :resize-type :bottom - :resize-axis :y - :resize-negate? (:row-gap negate) - :gap-type (if is-col? :column-gap :row-gap)})) - - display-blocks (if is-col? - (->> (drop-last wrap-blocks) - (map create-cgdd-block)) - (->> (drop-last wrap-blocks) - (map create-rgdd-block))) - - display-children (if is-col? - (->> children-to-display - (map create-rgdd)) - (->> children-to-display - (map create-cgdd)))] - - [:g.gaps {:pointer-events "visible"} - (for [[index display-item] (d/enumerate (concat display-blocks display-children))] - (let [gap-type (:gap-type display-item)] - [:& gap-display {:key (str frame-id index) - :frame-id frame-id - :zoom zoom - :gap-type gap-type - :gap gap - :on-pointer-enter (partial on-pointer-enter gap-type (get gap gap-type)) - :on-pointer-leave on-pointer-leave - :rect-data display-item - :hover? (= @hover gap-type) - :selected? (= gap-selected gap-type) - :mouse-pos mouse-pos - :hover-value hover-value}])) - - (when @hover - [:& flex-display-pill {:height pill-height - :width pill-width - :font-size (/ font-size zoom) - :border-radius (/ flex-display-pill-border-radius zoom) - :color distance-color - :x (:x @mouse-pos) - :y (- (:y @mouse-pos) pill-width) - :value @hover-value}])])) - -(mf/defc padding - [{:keys [frame zoom alt? shift?]}] - (when frame - [:g.measurement-gaps {:pointer-events "none"} - [:g.hover-shapes - [:& padding-rects {:frame frame :zoom zoom :alt? alt? :shift? shift?}]]])) - -(mf/defc gap - [{:keys [frame zoom]}] - (when frame - [:g.measurement-gaps {:pointer-events "none"} - [:g.hover-shapes - [:& gap-rects {:frame frame :zoom zoom}]]])) - -(mf/defc margin - [{:keys [shape parent zoom alt? shift?]}] - (when shape - [:g.measurement-gaps {:pointer-events "none"} - [:g.hover-shapes - [:& margin-rects {:shape shape :frame parent :zoom zoom :alt? alt? :shift? shift?}]]])) - - diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index 35cb52be7..7fdc82f6c 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -16,6 +16,7 @@ [app.main.data.workspace.modifiers :as dwm] [app.main.refs :as refs] [app.main.ui.context :as ctx] + [app.main.ui.flex-controls :as mfc] [app.main.ui.hooks :as ui-hooks] [app.main.ui.measurements :as msr] [app.main.ui.shapes.export :as use] @@ -232,31 +233,32 @@ disabled-guides? (or drawing-tool transform drawing-path? node-editing?) - one-selected-shape? (= (count selected-shapes) 1) + single-select? (= (count selected-shapes) 1) - show-padding? (and (nil? transform) - one-selected-shape? - (= (:type (first selected-shapes)) :frame) - (= (:layout (first selected-shapes)) :flex) - (zero? (:rotation (first selected-shapes)))) + first-shape (first selected-shapes) + show-padding? + (and (nil? transform) + single-select? + (= (:type first-shape) :frame) + (= (:layout first-shape) :flex) + (zero? (:rotation first-shape))) - show-margin? (and (nil? transform) - one-selected-shape? - (= (:layout selected-frame) :flex) - (zero? (:rotation (first selected-shapes)))) + show-margin? + (and (nil? transform) + single-select? + (= (:layout selected-frame) :flex) + (zero? (:rotation first-shape))) - first-selected-shape (first selected-shapes) - selecting-first-level-frame? (and one-selected-shape? - (cfh/root-frame? first-selected-shape)) + selecting-first-level-frame? (and single-select? (cfh/root-frame? first-shape)) offset-x (if selecting-first-level-frame? - (:x first-selected-shape) + (:x first-shape) (:x selected-frame)) offset-y (if selecting-first-level-frame? - (:y (first selected-shapes)) + (:y first-shape) (:y selected-frame)) rule-area-size (/ rules/rule-area-size zoom)] @@ -439,24 +441,28 @@ :zoom zoom}]) (when show-padding? - [:* - [:& msr/padding - {:frame (first selected-shapes) - :hover @frame-hover - :zoom zoom - :alt? @alt? - :shift? @shift?}] + [:& mfc/padding-control + {:frame first-shape + :hover @frame-hover + :zoom zoom + :alt? @alt? + :shift? @shift? + :on-move-selected on-move-selected + :on-context-menu on-menu-selected}]) - [:& msr/gap - {:frame (first selected-shapes) - :hover @frame-hover - :zoom zoom - :alt? @alt? - :shift? @shift?}]]) + (when show-padding? + [:& mfc/gap-control + {:frame first-shape + :hover @frame-hover + :zoom zoom + :alt? @alt? + :shift? @shift? + :on-move-selected on-move-selected + :on-context-menu on-menu-selected}]) (when show-margin? - [:& msr/margin - {:shape (first selected-shapes) + [:& mfc/margin-control + {:shape first-shape :parent selected-frame :hover @frame-hover :zoom zoom diff --git a/frontend/src/app/main/ui/workspace/viewport/actions.cljs b/frontend/src/app/main/ui/workspace/viewport/actions.cljs index 64fe00e77..3a9174a9d 100644 --- a/frontend/src/app/main/ui/workspace/viewport/actions.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/actions.cljs @@ -43,7 +43,6 @@ (mf/deps id blocked hidden type selected edition drawing-tool text-editing? node-editing? grid-editing? drawing-path? create-comment? @z? @space? panning workspace-read-only?) - (fn [bevent] ;; We need to handle editor related stuff here because ;; handling on editor dom node does not works properly. @@ -127,6 +126,7 @@ (not mod?) (not shift?) (not @space?)) + (dom/prevent-default bevent) (dom/stop-propagation bevent) (when-not (or workspace-read-only? @z?)