mirror of
https://github.com/penpot/penpot.git
synced 2025-02-10 00:58:26 -05:00
✨ Add visualization and mouse control to paddings in frames with layout
This commit is contained in:
parent
fd673b39a4
commit
4d90d36225
11 changed files with 298 additions and 7 deletions
|
@ -8,6 +8,7 @@
|
|||
- Adds more accessibility improvements in dashboard [Taiga #4577](https://tree.taiga.io/project/penpot/us/4577)
|
||||
- Adds paddings and gaps prediction on layout creation [Taiga #4838](https://tree.taiga.io/project/penpot/task/4838)
|
||||
- Add visual feedback when proportionally scaling text elements with **K** [Taiga #3415](https://tree.taiga.io/project/penpot/us/3415)
|
||||
- Add visualization and mouse control to paddings in frames with layout [Taiga #4839](https://tree.taiga.io/project/penpot/task/4839)
|
||||
|
||||
### :bug: Bugs fixed
|
||||
|
||||
|
|
|
@ -1544,7 +1544,6 @@
|
|||
;; - Respect the distance of the object to the right and bottom in the original frame
|
||||
(gpt/point paste-x paste-y))]
|
||||
[frame-id frame-id delta]))
|
||||
|
||||
(empty? page-selected)
|
||||
(let [frame-id (ctst/top-nested-frame page-objects mouse-pos)
|
||||
delta (gpt/subtract mouse-pos orig-pos)]
|
||||
|
@ -1904,6 +1903,18 @@
|
|||
(rx/empty)))))
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Measurements
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn set-paddings-selected
|
||||
[paddings-selected]
|
||||
(ptk/reify ::set-paddings-selected
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(assoc-in state [:workspace-global :paddings-selected] paddings-selected))))
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Orphan Shapes
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
|
|
@ -430,6 +430,7 @@
|
|||
:grow-type
|
||||
:layout-item-h-sizing
|
||||
:layout-item-v-sizing
|
||||
:layout-padding
|
||||
:position-data
|
||||
]})
|
||||
;; We've applied the text-modifier so we can dissoc the temporary data
|
||||
|
|
|
@ -279,6 +279,9 @@
|
|||
(def workspace-read-only?
|
||||
(l/derived :read-only? workspace-global))
|
||||
|
||||
(def workspace-paddings-selected
|
||||
(l/derived :paddings-selected workspace-global))
|
||||
|
||||
(defn object-by-id
|
||||
[id]
|
||||
(l/derived #(get % id) workspace-page-objects))
|
||||
|
|
|
@ -205,3 +205,14 @@
|
|||
(rx/dedupe))]
|
||||
(rx/subscribe-with ob sub)
|
||||
sub))
|
||||
|
||||
(defonce keyboard-shift
|
||||
(let [sub (rx/behavior-subject nil)
|
||||
ob (->> st/stream
|
||||
(rx/filter keyboard-event?)
|
||||
(rx/filter kbd/shift-key?)
|
||||
(rx/filter (comp not kbd/editing?))
|
||||
(rx/map #(= :down (:type %)))
|
||||
(rx/dedupe))]
|
||||
(rx/subscribe-with ob sub)
|
||||
sub))
|
||||
|
|
|
@ -54,7 +54,7 @@
|
|||
|
||||
on-mouse-move
|
||||
(mf/use-callback
|
||||
(mf/deps min-val max-val)
|
||||
(mf/deps min-val max-val negate?)
|
||||
(fn [event]
|
||||
(when (mf/ref-val dragging-ref)
|
||||
(let [start (mf/ref-val start-ref)
|
||||
|
|
|
@ -11,8 +11,15 @@
|
|||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.math :as mth]
|
||||
[app.common.types.modifiers :as ctm]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.workspace.modifiers :as dwm]
|
||||
[app.main.data.workspace.shape-layout :as dwsl]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.cursors :as cur]
|
||||
[app.main.ui.formats :as fmt]
|
||||
[app.util.dom :as dom]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
;; ------------------------------------------------
|
||||
|
@ -43,6 +50,8 @@
|
|||
(def distance-pill-width 50)
|
||||
(def distance-pill-height 16)
|
||||
(def distance-line-stroke 1)
|
||||
(def padding-pill-width 40)
|
||||
(def padding-pill-height 20)
|
||||
|
||||
;; ------------------------------------------------
|
||||
;; HELPERS
|
||||
|
@ -261,3 +270,230 @@
|
|||
[:& distance-display {:from hover-selrect :to selected-selrect :zoom zoom :bounds bounds-selrect}]])])))
|
||||
|
||||
|
||||
|
||||
(mf/defc padding-display-pill [{:keys [x y width height font-size value]}]
|
||||
[:g.distance-pill
|
||||
[:rect {:x x
|
||||
:y y
|
||||
:width width
|
||||
:height height
|
||||
:style {:fill distance-color}}]
|
||||
|
||||
[:text {:x (+ x (/ width 2))
|
||||
:y (+ y (/ height 2))
|
||||
:text-anchor "middle"
|
||||
:text-align "center"
|
||||
:dominant-baseline "central"
|
||||
:style {:fill distance-text-color
|
||||
:font-size font-size}}
|
||||
(fmt/format-number value)]])
|
||||
|
||||
|
||||
(mf/defc padding-display [{:keys [frame-id zoom hover-all? hover-v? hover-h? padding-num padding on-mouse-enter on-mouse-leave
|
||||
rect-data pill-data hover? selected?]}]
|
||||
(let [resizing? (mf/use-var false)
|
||||
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-callback
|
||||
(mf/deps frame-id padding-num padding)
|
||||
(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-callback
|
||||
(mf/deps frame-id padding-num padding)
|
||||
(fn [event]
|
||||
(let [padding-type (if (and (= (:p1 padding) (:p3 padding)) (= (:p2 padding) (:p4 padding))) :simple :multiple)]
|
||||
(dom/release-pointer event)
|
||||
(reset! resizing? false)
|
||||
(reset! start nil)
|
||||
(reset! original-value 0)
|
||||
(st/emit! (dwm/apply-modifiers)
|
||||
(dwsl/update-layout [frame-id] {:layout-padding-type padding-type})))))
|
||||
|
||||
on-mouse-move
|
||||
(mf/use-callback
|
||||
(mf/deps frame-id padding-num padding)
|
||||
(fn [event]
|
||||
(when @resizing?
|
||||
(let [pos (dom/get-client-position event)
|
||||
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))
|
||||
modifiers (dwm/create-modif-tree [frame-id] (ctm/change-property (ctm/empty) :layout-padding layout-padding))]
|
||||
(st/emit! (dwm/set-modifiers modifiers))))))]
|
||||
|
||||
|
||||
[:*
|
||||
[:rect.padding-rect {:x (:x rect-data)
|
||||
:y (:y rect-data)
|
||||
:width (:width rect-data)
|
||||
:height (:height rect-data)
|
||||
:on-mouse-enter on-mouse-enter
|
||||
:on-mouse-leave on-mouse-leave
|
||||
:on-pointer-down on-pointer-down
|
||||
:on-lost-pointer-capture on-lost-pointer-capture
|
||||
:on-mouse-move on-mouse-move
|
||||
:style {:fill (if (or hover? selected?) distance-color "none")
|
||||
:cursor (when (or hover? selected?)
|
||||
(if (= (:resize-axis rect-data) :x) (cur/resize-ew 0) (cur/resize-ew 90)))
|
||||
:opacity (if selected? 0.5 0.25)}}]
|
||||
(when (or hover? selected?)
|
||||
[:& padding-display-pill pill-data])]))
|
||||
|
||||
(mf/defc padding-rects [{:keys [frame zoom alt? shift?]}]
|
||||
(let [frame-id (:id frame)
|
||||
paddings-selected (mf/deref refs/workspace-paddings-selected)
|
||||
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)
|
||||
[width height x1 x2 y1 y2] ((juxt :width :height :x1 :x2 :y1 :y2) (:selrect frame))
|
||||
|
||||
pill-width (/ padding-pill-width zoom)
|
||||
pill-height (/ padding-pill-height zoom)
|
||||
pill-separation (/ pill-width 2)
|
||||
pill-data {:height pill-height
|
||||
:width pill-width
|
||||
:font-size (/ font-size zoom)}
|
||||
on-mouse-enter #(reset! hover %)
|
||||
on-mouse-leave #(reset! hover nil)
|
||||
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
|
||||
(= :fix (:layout-item-h-sizing frame)) (assoc :p2 (not (:p2 negate)))
|
||||
(= :fix (:layout-item-v-sizing frame)) (assoc :p3 (not (:p3 negate))))
|
||||
|
||||
padding-display-data [{:frame-id frame-id
|
||||
:zoom zoom
|
||||
:hover-all? hover-all?
|
||||
:hover-v? hover-v?
|
||||
:hover-h? hover-h?
|
||||
:padding-num :p1
|
||||
:padding padding
|
||||
:on-mouse-enter (partial on-mouse-enter :p1)
|
||||
:on-mouse-leave on-mouse-leave
|
||||
:rect-data {: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)}
|
||||
:pill-data (assoc pill-data
|
||||
:x (- x1 (* pill-width 1.5))
|
||||
:y (if (:flip-y frame)
|
||||
(+ (- y2 (:p1 padding)) (/ (- (:p1 padding) pill-height) 2))
|
||||
(+ y1 (/ (- (:p1 padding) pill-height) 2)))
|
||||
:value (:p1 padding))
|
||||
:hover? (or hover-all? hover-v? (= @hover :p1))
|
||||
:selected? (:p1 paddings-selected)}
|
||||
|
||||
{:frame-id frame-id
|
||||
:zoom zoom
|
||||
:hover-all? hover-all?
|
||||
:hover-v? hover-v?
|
||||
:hover-h? hover-h?
|
||||
:padding-num :p2
|
||||
:padding padding
|
||||
:on-mouse-enter (partial on-mouse-enter :p2)
|
||||
:on-mouse-leave on-mouse-leave
|
||||
:rect-data {: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)}
|
||||
:pill-data (assoc pill-data
|
||||
:x (if (:flip-x frame)
|
||||
(+ x1 (/ (- (:p2 padding) pill-width) 2))
|
||||
(+ (- x2 (:p2 padding)) (/ (- (:p2 padding) pill-width) 2)))
|
||||
:y (- y1 (+ pill-height pill-separation))
|
||||
:value (:p2 padding))
|
||||
:hover? (or hover-all? hover-h? (= @hover :p2))
|
||||
:selected? (:p2 paddings-selected)}
|
||||
|
||||
{:frame-id frame-id
|
||||
:zoom zoom
|
||||
:hover-all? hover-all?
|
||||
:hover-v? hover-v?
|
||||
:hover-h? hover-h?
|
||||
:padding-num :p3
|
||||
:padding padding
|
||||
:on-mouse-enter (partial on-mouse-enter :p3)
|
||||
:on-mouse-leave on-mouse-leave
|
||||
:rect-data {: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)}
|
||||
:pill-data (assoc pill-data
|
||||
:x (- x1 (+ pill-width pill-separation))
|
||||
:y (if (:flip-y frame)
|
||||
(+ y1 (/ (- (:p3 padding) pill-height) 2))
|
||||
(+ (- y2 (:p3 padding)) (/ (- (:p3 padding) pill-height) 2)))
|
||||
:value (:p3 padding))
|
||||
:hover? (or hover-all? hover-v? (= @hover :p3))
|
||||
:selected? (:p3 paddings-selected)}
|
||||
|
||||
{:frame-id frame-id
|
||||
:zoom zoom
|
||||
:hover-all? hover-all?
|
||||
:hover-v? hover-v?
|
||||
:hover-h? hover-h?
|
||||
:padding-num :p4
|
||||
:padding padding
|
||||
:on-mouse-enter (partial on-mouse-enter :p4)
|
||||
:on-mouse-leave on-mouse-leave
|
||||
:rect-data {: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)}
|
||||
:pill-data (assoc pill-data
|
||||
:x (if (:flip-x frame)
|
||||
(+ (- x2 (:p4 padding)) (/ (- (:p4 padding) pill-width) 2))
|
||||
(+ x1 (/ (- (:p4 padding) pill-width) 2)))
|
||||
:y (- y1 (+ pill-height pill-separation))
|
||||
:value (:p4 padding))
|
||||
:hover? (or hover-all? hover-h? (= @hover :p4))
|
||||
:selected? (:p4 paddings-selected)}]]
|
||||
|
||||
[:g.paddings {:pointer-events "visible"}
|
||||
(for [data padding-display-data]
|
||||
[:& padding-display data])]))
|
||||
|
||||
|
||||
(mf/defc padding
|
||||
[{:keys [frame zoom paddings-selected alt? shift?]}]
|
||||
(when frame
|
||||
[:g.measurement-gaps {:pointer-events "none"}
|
||||
[:g.hover-shapes
|
||||
[:& padding-rects {:frame frame :zoom zoom :alt? alt? :shift? shift? :paddings-selected paddings-selected}]]]))
|
||||
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
(ns app.main.ui.workspace.sidebar.options.menus.layout-container
|
||||
(:require [app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.main.data.workspace :as udw]
|
||||
[app.main.data.workspace.shape-layout :as dwsl]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.numeric-input :refer [numeric-input]]
|
||||
|
@ -204,7 +205,13 @@
|
|||
(= (dm/get-in values [:layout-padding :p2])
|
||||
(dm/get-in values [:layout-padding :p4])))
|
||||
(dm/get-in values [:layout-padding :p2])
|
||||
"--")]
|
||||
"--")
|
||||
|
||||
select-paddings
|
||||
(fn [p1? p2? p3? p4?]
|
||||
(st/emit! (udw/set-paddings-selected {:p1 p1? :p2 p2? :p3 p3? :p4 p4?})))
|
||||
|
||||
select-padding #(select-paddings (= % :p1) (= % :p2) (= % :p3) (= % :p4))]
|
||||
|
||||
[:div.padding-row
|
||||
(cond
|
||||
|
@ -218,6 +225,8 @@
|
|||
{:placeholder "--"
|
||||
:on-click #(dom/select-target %)
|
||||
:on-change (partial on-change :simple :p1)
|
||||
:on-focus #(select-paddings true false true false)
|
||||
:on-blur #(select-paddings false false false false)
|
||||
:value p1}]]
|
||||
|
||||
[:div.padding-item.tooltip.tooltip-bottom-left
|
||||
|
@ -227,6 +236,8 @@
|
|||
{:placeholder "--"
|
||||
:on-click #(dom/select-target %)
|
||||
:on-change (partial on-change :simple :p2)
|
||||
:on-focus #(select-paddings false true false true)
|
||||
:on-blur #(select-paddings false false false false)
|
||||
:value p2}]]]
|
||||
|
||||
(= padding-type :multiple)
|
||||
|
@ -244,6 +255,8 @@
|
|||
{:placeholder "--"
|
||||
:on-click #(dom/select-target %)
|
||||
:on-change (partial on-change :multiple num)
|
||||
:on-focus #(select-padding num)
|
||||
:on-blur #(select-paddings false false false false)
|
||||
:value (num (:layout-padding values))}]]])])
|
||||
|
||||
[:div.padding-icons
|
||||
|
|
|
@ -104,6 +104,7 @@
|
|||
|
||||
;; STATE
|
||||
alt? (mf/use-state false)
|
||||
shift? (mf/use-state false)
|
||||
mod? (mf/use-state false)
|
||||
space? (mf/use-state false)
|
||||
z? (mf/use-state false)
|
||||
|
@ -209,12 +210,16 @@
|
|||
show-rules? (and (contains? layout :rules) (not (contains? layout :hide-ui)))
|
||||
|
||||
|
||||
disabled-guides? (or drawing-tool transform)]
|
||||
disabled-guides? (or drawing-tool transform)
|
||||
|
||||
show-padding? (and (= (count selected-shapes) 1)
|
||||
(= (:type (first selected-shapes)) :frame)
|
||||
(= (:layout (first selected-shapes)) :flex))]
|
||||
|
||||
(hooks/setup-dom-events viewport-ref zoom disable-paste in-viewport? workspace-read-only?)
|
||||
(hooks/setup-viewport-size viewport-ref)
|
||||
(hooks/setup-cursor cursor alt? mod? space? panning drawing-tool drawing-path? node-editing? z? workspace-read-only?)
|
||||
(hooks/setup-keyboard alt? mod? space? z?)
|
||||
(hooks/setup-keyboard alt? mod? space? z? shift?)
|
||||
(hooks/setup-hover-shapes page-id move-stream base-objects transform selected mod? hover hover-ids hover-top-frame-id @hover-disabled? focus zoom show-measures?)
|
||||
(hooks/setup-viewport-modifiers modifiers base-objects)
|
||||
(hooks/setup-shortcuts node-editing? drawing-path? text-editing?)
|
||||
|
@ -368,6 +373,14 @@
|
|||
:hover-shape @hover
|
||||
:zoom zoom}])
|
||||
|
||||
(when show-padding?
|
||||
[:& msr/padding
|
||||
{:frame (first selected-shapes)
|
||||
:hover @frame-hover
|
||||
:zoom zoom
|
||||
:alt? @alt?
|
||||
:shift? @shift?}])
|
||||
|
||||
[:& widgets/frame-titles
|
||||
{:objects base-objects
|
||||
:selected selected
|
||||
|
|
|
@ -107,12 +107,13 @@
|
|||
(when (not= @cursor new-cursor)
|
||||
(reset! cursor new-cursor))))))
|
||||
|
||||
(defn setup-keyboard [alt? mod? space? z?]
|
||||
(defn setup-keyboard [alt? mod? space? z? shift?]
|
||||
(hooks/use-stream ms/keyboard-alt #(reset! alt? %))
|
||||
(hooks/use-stream ms/keyboard-mod #((reset! mod? %)
|
||||
(when-not % (reset! z? false)))) ;; In mac after command+z there is no event for the release of the z key
|
||||
(hooks/use-stream ms/keyboard-space #(reset! space? %))
|
||||
(hooks/use-stream ms/keyboard-z #(reset! z? %)))
|
||||
(hooks/use-stream ms/keyboard-z #(reset! z? %))
|
||||
(hooks/use-stream ms/keyboard-shift #(reset! shift? %)))
|
||||
|
||||
(defn group-empty-space?
|
||||
"Given a group `group-id` check if `hover-ids` contains any of its children. If it doesn't means
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
(def left-arrow? (is-key? "ArrowLeft"))
|
||||
(def right-arrow? (is-key? "ArrowRight"))
|
||||
(def alt-key? (is-key? "Alt"))
|
||||
(def shift-key? (is-key? "Shift"))
|
||||
(def ctrl-key? (is-key? "Control"))
|
||||
(def meta-key? (is-key? "Meta"))
|
||||
(def comma? (is-key? ","))
|
||||
|
|
Loading…
Add table
Reference in a new issue