Support rotated UI

This commit is contained in:
alonso.torres 2023-05-26 11:46:38 +02:00
parent b83c35b0dd
commit 03c64303f5
3 changed files with 102 additions and 82 deletions

View file

@ -242,7 +242,6 @@
children (->> (cph/get-immediate-children objects (:id frame)) children (->> (cph/get-immediate-children objects (:id frame))
(remove :hidden) (remove :hidden)
(map #(vector (gpo/parent-coords-bounds (:points %) (:points frame)) %))) (map #(vector (gpo/parent-coords-bounds (:points %) (:points frame)) %)))
layout-data (ld/calc-layout-data frame children (:points frame)) layout-data (ld/calc-layout-data frame children (:points frame))]
position (gmt/transform-point-center position (gco/center-shape frame) (:transform-inverse frame))]
(get-position-grid-coord layout-data position))) (get-position-grid-coord layout-data position)))

View file

@ -160,6 +160,7 @@
(fn [event] (fn [event]
(when (and (nil? selrect) (when (and (nil? selrect)
(or (dom/class? (dom/get-target event) "viewport-controls") (or (dom/class? (dom/get-target event) "viewport-controls")
(dom/child? (dom/get-target event) (dom/query ".viewport-controls"))
(dom/class? (dom/get-target event) "viewport-selrect"))) (dom/class? (dom/get-target event) "viewport-selrect")))
(let [ctrl? (kbd/ctrl? event) (let [ctrl? (kbd/ctrl? event)
shift? (kbd/shift? event) shift? (kbd/shift? event)
@ -233,9 +234,9 @@
(if workspace-read-only? (if workspace-read-only?
(dom/prevent-default event) (dom/prevent-default event)
(when (or (dom/class? (dom/get-target event) "viewport-controls") (when (or (dom/class? (dom/get-target event) "viewport-controls")
(dom/class? (dom/get-target event) "viewport-selrect")) (dom/child? (dom/get-target event) (dom/query ".viewport-controls"))
(dom/prevent-default event) (dom/class? (dom/get-target event) "viewport-selrect")
workspace-read-only?)
(let [position (dom/get-client-position event)] (let [position (dom/get-client-position event)]
;; Delayed callback because we need to wait to the previous context menu to be closed ;; Delayed callback because we need to wait to the previous context menu to be closed
(timers/schedule (timers/schedule

View file

@ -9,7 +9,9 @@
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.geom.matrix :as gmt]
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.common.geom.shapes :as gsh]
[app.common.geom.shapes.grid-layout :as gsg] [app.common.geom.shapes.grid-layout :as gsg]
[app.common.geom.shapes.points :as gpo] [app.common.geom.shapes.points :as gpo]
[app.common.math :as mth] [app.common.math :as mth]
@ -170,7 +172,10 @@
direction (unchecked-get props "direction") direction (unchecked-get props "direction")
layout-data (unchecked-get props "layout-data") layout-data (unchecked-get props "layout-data")
cursor (if (= direction :row) (cur/scale-ns 0) (cur/scale-ew 0)) cursor
(if (= direction :row)
(cur/scale-ns (:rotation shape))
(cur/scale-ew (:rotation shape)))
handle-drag-position handle-drag-position
(mf/use-callback (mf/use-callback
@ -277,6 +282,8 @@
cell-origin (gpo/origin cell-bounds) cell-origin (gpo/origin cell-bounds)
cell-width (gpo/width-points cell-bounds) cell-width (gpo/width-points cell-bounds)
cell-height (gpo/height-points cell-bounds) cell-height (gpo/height-points cell-bounds)
cell-center (gsh/center-points cell-bounds)
cell-origin (gpt/transform cell-origin (gmt/transform-in cell-center (:transform-inverse shape)))
handle-pointer-enter handle-pointer-enter
(mf/use-callback (mf/use-callback
@ -298,7 +305,8 @@
[:g.cell-editor [:g.cell-editor
[:rect [:rect
{:class (dom/classnames (css :grid-cell-outline) true {:transform (dm/str (gmt/transform-in cell-center (:transform shape)))
:class (dom/classnames (css :grid-cell-outline) true
(css :hover) hover? (css :hover) hover?
(css :selected) selected?) (css :selected) selected?)
:x (:x cell-origin) :x (:x cell-origin)
@ -323,7 +331,7 @@
[:right (+ (:x cell-origin) cell-width (/ -10 zoom)) (:y cell-origin) (/ 20 zoom) cell-height :column] [:right (+ (:x cell-origin) cell-width (/ -10 zoom)) (:y cell-origin) (/ 20 zoom) cell-height :column]
[:bottom (:x cell-origin) (+ (:y cell-origin) cell-height (/ -10 zoom)) cell-width (/ 20 zoom) :row] [:bottom (:x cell-origin) (+ (:y cell-origin) cell-height (/ -10 zoom)) cell-width (/ 20 zoom) :row]
[:left (+ (:x cell-origin) (/ -10 zoom)) (:y cell-origin) (/ 20 zoom) cell-height :column]]] [:left (+ (:x cell-origin) (/ -10 zoom)) (:y cell-origin) (/ 20 zoom) cell-height :column]]]
[:* [:g {:transform (dm/str (gmt/transform-in cell-center (:transform shape)))}
(for [[handler x y width height dir] handlers] (for [[handler x y width height dir] handlers]
[:& resize-cell-handler {:key (dm/str "resize-" (d/name handler) "-" (:id cell)) [:& resize-cell-handler {:key (dm/str "resize-" (d/name handler) "-" (:id cell))
:shape shape :shape shape
@ -402,6 +410,7 @@
(let [shape (unchecked-get props "shape") (let [shape (unchecked-get props "shape")
index (unchecked-get props "index") index (unchecked-get props "index")
last? (unchecked-get props "last?")
track-before (unchecked-get props "track-before") track-before (unchecked-get props "track-before")
track-after (unchecked-get props "track-after") track-after (unchecked-get props "track-after")
snap-pixel? (unchecked-get props "snap-pixel?") snap-pixel? (unchecked-get props "snap-pixel?")
@ -411,35 +420,54 @@
type (unchecked-get props "type") type (unchecked-get props "type")
zoom (unchecked-get props "zoom") zoom (unchecked-get props "zoom")
bounds (:points shape)
hv #(gpo/start-hv bounds %)
vv #(gpo/start-vv bounds %)
[layout-gap-row layout-gap-col] (ctl/gaps shape) [layout-gap-row layout-gap-col] (ctl/gaps shape)
{:keys [handle-pointer-down handle-lost-pointer-capture handle-pointer-move]} {:keys [handle-pointer-down handle-lost-pointer-capture handle-pointer-move]}
(use-resize-track type shape index track-before track-after zoom snap-pixel?) (use-resize-track type shape index track-before track-after zoom snap-pixel?)
[x y width height] [width height]
(if (= type :column) (if (= type :column)
[(- (:x start-p) layout-gap-col (/ (- (max layout-gap-col (/ 16 zoom)) layout-gap-col) 2)) [(max layout-gap-col (/ 16 zoom))
(:y start-p)
(max layout-gap-col (/ 16 zoom))
(+ row-total-size row-total-gap)] (+ row-total-size row-total-gap)]
[(:x start-p) [(+ column-total-size column-total-gap)
(- (:y start-p) layout-gap-row (/ (- (max layout-gap-row (/ 16 zoom)) layout-gap-row) 2)) (max layout-gap-row (/ 16 zoom))])
(+ column-total-size column-total-gap)
(max layout-gap-row (/ 16 zoom))])] start-p
(cond-> start-p
(and (= type :column) (= index 0))
(gpt/subtract (hv width))
(and (= type :row) (= index 0))
(gpt/subtract (vv height))
(and (= type :column) (not= index 0) (not last?))
(-> (gpt/subtract (hv (/ layout-gap-col 2)))
(gpt/subtract (hv (/ width 2))))
(and (= type :row) (not= index 0) (not last?))
(-> (gpt/subtract (vv (/ layout-gap-row 2)))
(gpt/subtract (vv (/ height 2)))))]
[:rect.resize-track-handler [:rect.resize-track-handler
{:x x {:x (:x start-p)
:y y :y (:y start-p)
:class (if (= type :column)
"resize-ew-0"
"resize-ns-0")
:height height :height height
:width width :width width
:on-pointer-down handle-pointer-down :on-pointer-down handle-pointer-down
:on-lost-pointer-capture handle-lost-pointer-capture :on-lost-pointer-capture handle-lost-pointer-capture
:on-pointer-move handle-pointer-move :on-pointer-move handle-pointer-move
:style {:fill "transparent"}}])) :transform (dm/str (gmt/transform-in start-p (:transform shape)))
:style {:fill "transparent"
:opacity 0.5
:stroke-width 0
:cursor (if (= type :column)
(cur/resize-ew (:rotation shape))
(cur/resize-ns (:rotation shape)))}}]))
(mf/defc track-marker (mf/defc track-marker
{::mf/wrap-props false} {::mf/wrap-props false}
@ -482,9 +510,11 @@
:on-lost-pointer-capture handle-lost-pointer-capture :on-lost-pointer-capture handle-lost-pointer-capture
:on-pointer-move handle-pointer-move :on-pointer-move handle-pointer-move
:class (css :grid-track-marker) :class (css :grid-track-marker)
:transform (dm/str (gmt/transform-in center (:transform shape)))
:style {:cursor (if (= type :column) :style {:cursor (if (= type :column)
(cur/resize-ew 0) (cur/resize-ew (:rotation shape))
(cur/resize-ns 0))}} (cur/resize-ns (:rotation shape)))}}
[:polygon {:class (css :marker-shape) [:polygon {:class (css :marker-shape)
:points (->> marker-points :points (->> marker-points
(map #(dm/fmt "%,%" (:x %) (:y %))) (map #(dm/fmt "%,%" (:x %) (:y %)))
@ -493,7 +523,7 @@
:x text-x :x text-x
:y text-y :y text-y
:width (/ 26.26 zoom) :width (/ 26.26 zoom)
:height (/ 32 zoom) :height (/ 36 zoom)
:text-anchor "middle" :text-anchor "middle"
:dominant-baseline "middle"} :dominant-baseline "middle"}
(dm/str value)]])) (dm/str value)]]))
@ -512,38 +542,28 @@
track-input-ref (mf/use-ref) track-input-ref (mf/use-ref)
[layout-gap-row layout-gap-col] (ctl/gaps shape) [layout-gap-row layout-gap-col] (ctl/gaps shape)
bounds (:points shape) bounds (:points shape)
origin (gpo/origin bounds)
vv #(gpo/start-vv bounds %) vv #(gpo/start-vv bounds %)
hv #(gpo/start-hv bounds %) hv #(gpo/start-hv bounds %)
start-p (:start-p track-data) start-p (:start-p track-data)
relative (gpt/to-vec origin start-p)
hpt (gpo/project-point bounds :h start-p)
vpt (gpo/project-point bounds :v start-p)
marker-p marker-p
(if (= type :column) (if (= type :column)
(-> origin (-> hpt
(gpt/add (hv (:x relative)))
(gpt/subtract (vv (/ 20 zoom))) (gpt/subtract (vv (/ 20 zoom)))
(cond-> (not= index 0) (cond-> (not= index 0)
(gpt/subtract (hv (/ layout-gap-col 2))))) (gpt/subtract (hv (/ layout-gap-col 2)))))
(-> origin (-> vpt
(gpt/add (vv (:y relative)))
(gpt/subtract (hv (/ 20 zoom))) (gpt/subtract (hv (/ 20 zoom)))
(cond-> (not= index 0) (cond-> (not= index 0)
(gpt/subtract (vv (/ layout-gap-row 2)))))) (gpt/subtract (vv (/ layout-gap-row 2))))))
text-p text-p (if (= type :column) hpt vpt)
(if (= type :column)
(-> origin
(gpt/add (hv (:x relative)))
(gpt/subtract (vv (/ 36 zoom))))
(-> origin
(gpt/add (vv (:y relative)))
(gpt/subtract (hv (/ (:size track-data) 2)))
(gpt/subtract (hv (/ 16 zoom)))
(gpt/add (vv (/ (:size track-data) 2)))
(gpt/subtract (vv (/ 18 zoom)))))
handle-blur-track-input handle-blur-track-input
(mf/use-callback (mf/use-callback
@ -585,8 +605,8 @@
track-list-prop (if (= type :column) :column-tracks :row-tracks) track-list-prop (if (= type :column) :column-tracks :row-tracks)
[text-x text-y text-width text-height] [text-x text-y text-width text-height]
(if (= type :column) (if (= type :column)
[(:x text-p) (:y text-p) (max 0 (- (:size track-data) 4)) (/ 32 zoom)] [(:x text-p) (- (:y text-p) (/ 36 zoom)) (max 0 (:size track-data)) (/ 36 zoom)]
[(:x text-p) (:y text-p) (:size track-data) (/ 36 zoom)]) [(- (:x text-p) (max 0 (:size track-data))) (- (:y text-p) (/ 36 zoom)) (max 0 (:size track-data)) (/ 36 zoom)])
track-before (get-in layout-data [track-list-prop (dec index)])] track-before (get-in layout-data [track-list-prop (dec index)])]
@ -596,17 +616,9 @@
(dom/set-value! (mf/ref-val track-input-ref) (format-size track-data)))) (dom/set-value! (mf/ref-val track-input-ref) (format-size track-data))))
[:g.track [:g.track
[:g {:transform (when (= type :row) (dm/fmt "rotate(-90 % %)" (:x marker-p) (:y marker-p)))} [:g {:transform (if (= type :column)
[:& track-marker {:center marker-p (dm/str (gmt/transform-in text-p (:transform shape)))
:index index (dm/str (gmt/transform-in text-p (gmt/rotate (:transform shape) -90))))}
:shape shape
:snap-pixel? snap-pixel?
:track-after track-data
:track-before track-before
:type type
:value (dm/str (inc index))
:zoom zoom}]]
[:g {:transform (when (= type :row) (dm/fmt "rotate(-90 % %)" (+ (:x text-p) (/ (:size track-data) 2)) (+ (:y text-p) (/ 36 zoom 2))))}
[:foreignObject {:x text-x :y text-y :width text-width :height text-height} [:foreignObject {:x text-x :y text-y :width text-width :height text-height}
[:input [:input
{:ref track-input-ref {:ref track-input-ref
@ -617,6 +629,18 @@
:on-key-down handle-keydown-track-input :on-key-down handle-keydown-track-input
:on-blur handle-blur-track-input}]]] :on-blur handle-blur-track-input}]]]
[:g {:transform (when (= type :row) (dm/fmt "rotate(-90 % %)" (:x marker-p) (:y marker-p)))}
[:& track-marker
{:center marker-p
:index index
:shape shape
:snap-pixel? snap-pixel?
:track-after track-data
:track-before track-before
:type type
:value (dm/str (inc index))
:zoom zoom}]]
[:& resize-track-handler [:& resize-track-handler
{:index index {:index index
:layout-data layout-data :layout-data layout-data
@ -668,8 +692,6 @@
height (gpo/height-points bounds) height (gpo/height-points bounds)
origin (gpo/origin bounds) origin (gpo/origin bounds)
[layout-gap-row layout-gap-col] (ctl/gaps shape)
{:keys [row-tracks column-tracks] :as layout-data} {:keys [row-tracks column-tracks] :as layout-data}
(mf/use-memo (mf/use-memo
(mf/deps shape children) (mf/deps shape children)
@ -705,16 +727,18 @@
[:& grid-editor-frame {:zoom zoom [:& grid-editor-frame {:zoom zoom
:bounds bounds}] :bounds bounds}]
(let [start-p (-> origin (gpt/add (hv width)))] (let [start-p (-> origin (gpt/add (hv width)))]
[:g {:transform (dm/str (gmt/transform-in start-p (:transform shape)))}
[:& plus-btn {:start-p start-p [:& plus-btn {:start-p start-p
:zoom zoom :zoom zoom
:type :column :type :column
:on-click handle-add-column}]) :on-click handle-add-column}]])
(let [start-p (-> origin (gpt/add (vv height)))] (let [start-p (-> origin (gpt/add (vv height)))]
[:g {:transform (dm/str (gmt/transform-in start-p (:transform shape)))}
[:& plus-btn {:start-p start-p [:& plus-btn {:start-p start-p
:zoom zoom :zoom zoom
:type :row :type :row
:on-click handle-add-row}]) :on-click handle-add-row}]])
(for [[idx column-data] (d/enumerate column-tracks)] (for [[idx column-data] (d/enumerate column-tracks)]
[:& track {:key (dm/str "column-track-" idx) [:& track {:key (dm/str "column-track-" idx)
@ -729,12 +753,10 @@
;; Last track resize handler ;; Last track resize handler
(when-not (empty? column-tracks) (when-not (empty? column-tracks)
(let [last-track (last column-tracks) (let [last-track (last column-tracks)
start-p (:start-p (last column-tracks)) start-p (:start-p last-track)
relative (gpt/to-vec origin start-p) end-p (gpt/add start-p (hv (:size last-track)))
marker-p (-> origin marker-p (-> (gpo/project-point bounds :h end-p)
(gpt/add (hv (:x relative))) (gpt/subtract (vv (/ 20 zoom))))]
(gpt/subtract (vv (/ 20 zoom)))
(gpt/add (hv (:size last-track))))]
[:g.track [:g.track
[:& track-marker {:center marker-p [:& track-marker {:center marker-p
:index (count column-tracks) :index (count column-tracks)
@ -746,10 +768,11 @@
:zoom zoom}] :zoom zoom}]
[:& resize-track-handler [:& resize-track-handler
{:index (count column-tracks) {:index (count column-tracks)
:last? true
:shape shape :shape shape
:layout-data layout-data :layout-data layout-data
:snap-pixel? snap-pixel? :snap-pixel? snap-pixel?
:start-p (-> start-p (gpt/add (hv (+ layout-gap-col (:size last-track))))) :start-p end-p
:type :column :type :column
:track-before (last column-tracks) :track-before (last column-tracks)
:zoom zoom}]])) :zoom zoom}]]))
@ -766,13 +789,10 @@
(when-not (empty? row-tracks) (when-not (empty? row-tracks)
(let [last-track (last row-tracks) (let [last-track (last row-tracks)
start-p (:start-p (last row-tracks)) start-p (:start-p last-track)
relative (gpt/to-vec origin start-p) end-p (gpt/add start-p (vv (:size last-track)))
marker-p marker-p (-> (gpo/project-point bounds :v end-p)
(-> origin (gpt/subtract (hv (/ 20 zoom))))]
(gpt/add (vv (:y relative)))
(gpt/subtract (hv (/ 20 zoom)))
(gpt/add (vv (:size last-track))))]
[:g.track [:g.track
[:g {:transform (dm/fmt "rotate(-90 % %)" (:x marker-p) (:y marker-p))} [:g {:transform (dm/fmt "rotate(-90 % %)" (:x marker-p) (:y marker-p))}
[:& track-marker {:center marker-p [:& track-marker {:center marker-p
@ -785,10 +805,10 @@
:zoom zoom}]] :zoom zoom}]]
[:& resize-track-handler [:& resize-track-handler
{:index (count row-tracks) {:index (count row-tracks)
:last? true
:shape shape :shape shape
:layout-data layout-data :layout-data layout-data
:start-p (-> start-p :start-p end-p
(gpt/add (vv (+ layout-gap-row (:size last-track)))))
:type :row :type :row
:track-before (last row-tracks) :track-before (last row-tracks)
:snap-pixel? snap-pixel? :snap-pixel? snap-pixel?