Drop-zone autolayout calculation

This commit is contained in:
alonso.torres 2022-10-10 11:51:52 +02:00
parent 8bcb9e1976
commit 025cac0228
7 changed files with 339 additions and 13 deletions

View file

@ -8,6 +8,7 @@
(:require
[app.common.data :as d]
[app.common.geom.point :as gpt]
[app.common.geom.shapes.rect :as gsr]
[app.common.geom.shapes.transforms :as gst]))
;; :layout ;; true if active, false if not
@ -571,3 +572,136 @@
(conj {:type :move :vector move-vec}))]
[modifiers layout-line]))
(defn drop-areas
[{:keys [margin-x margin-y] :as frame} layout-data children]
(let [col? (col? frame)
row? (row? frame)
h-center? (and row? (h-center? frame))
h-end? (and row? (h-end? frame))
v-center? (and col? (v-center? frame))
v-end? (and row? (v-end? frame))
layout-gap (:layout-gap frame 0)
children (vec (cond->> children
(:reverse? layout-data) reverse))
redfn-child
(fn [[result parent-rect prev-x prev-y] [child next]]
(let [prev-x (or prev-x (:x parent-rect))
prev-y (or prev-y (:y parent-rect))
last? (nil? next)
box-x (-> child :selrect :x)
box-y (-> child :selrect :y)
box-width (-> child :selrect :width)
box-height(-> child :selrect :height)
x (if row? (:x parent-rect) prev-x)
y (if col? (:y parent-rect) prev-y)
width (cond
(and col? last?)
(- (+ (:x parent-rect) (:width parent-rect)) x)
row?
(:width parent-rect)
:else
(+ box-width (- box-x prev-x) (/ layout-gap 2)))
height (cond
(and row? last?)
(- (+ (:y parent-rect) (:height parent-rect)) y)
col?
(:height parent-rect)
:else
(+ box-height (- box-y prev-y) (/ layout-gap 2)))
line-area (gsr/make-rect x y width height)
result (conj result line-area)]
[result parent-rect (+ x width) (+ y height)]))
redfn-lines
(fn [[result from-idx prev-x prev-y] [{:keys [start-p layout-gap num-children line-width line-height]} next]]
(let [prev-x (or prev-x (:x frame))
prev-y (or prev-y (:y frame))
last? (nil? next)
line-width
(if col?
(:width frame)
(+ line-width margin-x
(if col? (* layout-gap (dec num-children)) 0)))
line-height
(if row?
(:height frame)
(+ line-height margin-y
(if row?
(* layout-gap (dec num-children))
0)))
box-x
(- (:x start-p)
(cond
h-center? (/ line-width 2)
h-end? line-width
:else 0))
box-y
(- (:y start-p)
(cond
v-center? (/ line-height 2)
v-end? line-height
:else 0))
x (if col? (:x frame) prev-x)
y (if row? (:y frame) prev-y)
width (cond
(and row? last?)
(- (+ (:x frame) (:width frame)) x)
col?
(:width frame)
:else
(+ line-width (- box-x prev-x) (/ layout-gap 2)))
height (cond
(and col? last?)
(- (+ (:y frame) (:height frame)) y)
row?
(:height frame)
:else
(+ line-height (- box-y prev-y) (/ layout-gap 2)))
line-area (gsr/make-rect x y width height)
children (subvec children from-idx (+ from-idx num-children))
;; To debug the lines
;;result (conj result line-area)
result (first (reduce redfn-child [result line-area] (d/with-next children)))]
[result (+ from-idx num-children) (+ x width) (+ y height)]))
ret (first (reduce redfn-lines [[] 0] (d/with-next (:layout-lines layout-data))))
]
;;(.log js/console "RET" (clj->js ret))
ret
))

View file

@ -28,8 +28,10 @@
(= frame-id uuid/zero)))
(defn frame-shape?
[{:keys [type]}]
(= type :frame))
([objects id]
(= (get-in objects [id :type]) id))
([{:keys [type]}]
(= type :frame)))
(defn group-shape?
[{:keys [type]}]

View file

@ -234,6 +234,20 @@
(if (nil? child-frame-id)
(or current-id uuid/zero)
(recur child-frame-id))))))
(defn top-nested-frame-ids
"Search the top nested frame in a list of ids"
[objects ids]
(let [frame-ids (->> ids (filter #(cph/frame-shape? objects %)))
frame-set (set frame-ids)]
(loop [current-id (first frame-ids)]
(let [current-shape (get objects current-id)
child-frame-id (d/seek #(contains? frame-set %)
(-> (:shapes current-shape) reverse))]
(if (nil? child-frame-id)
(or current-id uuid/zero)
(recur child-frame-id)))))
)
(defn get-viewer-frames
([objects]

View file

@ -24,7 +24,87 @@
[app.main.ui.workspace.shapes.frame.node-store :as fns]
[app.main.ui.workspace.shapes.frame.thumbnail-render :as ftr]
[beicon.core :as rx]
[rumext.v2 :as mf]))
[rumext.v2 :as mf]
[app.common.geom.shapes :as gsh]
[app.common.geom.shapes.layout :as gsl]
[app.main.data.workspace.state-helpers :as wsh]
[app.main.store :as st]))
(mf/defc debug-layout
{::mf/wrap-props false}
[props]
(let [shape (unchecked-get props "shape")
children (-> (wsh/lookup-page-objects @st/state)
(cph/get-immediate-children (:id shape)))
layout-data (gsl/calc-layout-data shape children)
drop-areas
(gsl/drop-areas shape layout-data children)
]
[:g.debug-layout {:pointer-events "none"}
(for [[idx drop-area] (d/enumerate drop-areas)]
[:rect {:x (:x drop-area)
:y (:y drop-area)
:width (:width drop-area)
:height (:height drop-area)
:style {:fill "blue"
:fill-opacity 0.3
:stroke "red"
:stroke-width 1
:stroke-dasharray "3 6"}}])
#_(for [[idx layout-line] (d/enumerate (:layout-lines layout-data))]
(let [col? (gsl/col? shape)
row? (gsl/row? shape)
h-center? (and row? (gsl/h-center? shape))
h-end? (and row? (gsl/h-end? shape))
v-center? (and col? (gsl/v-center? shape))
v-end? (and row? (gsl/v-end? shape))
line-width
(+ (-> layout-line :line-width)
(:margin-x shape)
(if col?
(* (:layout-gap layout-line) (dec (-> layout-line :num-children)))
0))
line-height
(+ (-> layout-line :line-height)
(:margin-y shape)
(if row?
(* (:layout-gap layout-line) (dec (-> layout-line :num-children)))
0))
]
[:g {:key (dm/str "line-" idx)}
[:rect {:x (- (-> layout-line :start-p :x)
(cond
h-center? (/ line-width 2)
h-end? line-width
:else 0))
:y (- (-> layout-line :start-p :y)
(cond
v-center? (/ line-height 2)
v-end? line-height
:else 0))
:width line-width
:height line-height
:style {:fill "blue"
:fill-opacity 0.3}
}]
#_[:line {:x1 (-> layout-line :start-p :x)
:y1 (-> layout-line :start-p :y)
:x2 (+ (-> layout-line :start-p :x) (if col? line-width 0))
:y2 (+ (-> layout-line :start-p :y) (if row? line-height 0))
:transform (gsh/transform-str shape)
:style {:fill "none"
:stroke "red"
:stroke-width 2}}]]))]))
(defn frame-shape-factory
[shape-wrapper]
@ -39,9 +119,12 @@
childs-ref (mf/use-memo (mf/deps (:id shape)) #(refs/children-objects (:id shape)))
childs (mf/deref childs-ref)]
[:*
[:& (mf/provider embed/context) {:value true}
[:& shape-container {:shape shape :ref ref :disable-shadows? (cph/root-frame? shape)}
[:& frame-shape {:shape shape :childs childs} ]]]))))
[:& frame-shape {:shape shape :childs childs} ]]]
#_[:& debug-layout {:shape shape}]]))))
(defn check-props
[new-props old-props]

View file

@ -41,7 +41,85 @@
[app.main.ui.workspace.viewport.widgets :as widgets]
[beicon.core :as rx]
[debug :refer [debug?]]
[rumext.v2 :as mf]))
[rumext.v2 :as mf]
[app.common.uuid :as uuid]
[app.common.geom.shapes :as gsh]
[app.common.geom.shapes.layout :as gsl]
[app.main.data.workspace.state-helpers :as wsh]
[app.main.store :as st]
))
(mf/defc debug-layout
{::mf/wrap-props false}
[props]
(let [shape (unchecked-get props "shape")
objects (unchecked-get props "objects")
children (cph/get-immediate-children objects (:id shape))
layout-data (gsl/calc-layout-data shape children)
drop-areas (gsl/drop-areas shape layout-data children)]
[:g.debug-layout {:pointer-events "none"}
(for [[idx drop-area] (d/enumerate drop-areas)]
[:rect {:x (:x drop-area)
:y (:y drop-area)
:width (:width drop-area)
:height (:height drop-area)
:style {:fill "blue"
:fill-opacity 0.3
:stroke "red"
:stroke-width 1
:stroke-dasharray "3 6"}}])
#_(for [[idx layout-line] (d/enumerate (:layout-lines layout-data))]
(let [col? (gsl/col? shape)
row? (gsl/row? shape)
h-center? (and row? (gsl/h-center? shape))
h-end? (and row? (gsl/h-end? shape))
v-center? (and col? (gsl/v-center? shape))
v-end? (and row? (gsl/v-end? shape))
line-width
(+ (-> layout-line :line-width)
(:margin-x shape)
(if col?
(* (:layout-gap layout-line) (dec (-> layout-line :num-children)))
0))
line-height
(+ (-> layout-line :line-height)
(:margin-y shape)
(if row?
(* (:layout-gap layout-line) (dec (-> layout-line :num-children)))
0))
]
[:g {:key (dm/str "line-" idx)}
[:rect {:x (- (-> layout-line :start-p :x)
(cond
h-center? (/ line-width 2)
h-end? line-width
:else 0))
:y (- (-> layout-line :start-p :y)
(cond
v-center? (/ line-height 2)
v-end? line-height
:else 0))
:width line-width
:height line-height
:style {:fill "blue"
:fill-opacity 0.3}
}]
#_[:line {:x1 (-> layout-line :start-p :x)
:y1 (-> layout-line :start-p :y)
:x2 (+ (-> layout-line :start-p :x) (if col? line-width 0))
:y2 (+ (-> layout-line :start-p :y) (if row? line-height 0))
:transform (gsh/transform-str shape)
:style {:fill "none"
:stroke "red"
:stroke-width 2}}]]))]))
;; --- Viewport
@ -90,6 +168,7 @@
hover-ids (mf/use-state nil)
hover (mf/use-state nil)
hover-disabled? (mf/use-state false)
hover-top-frame-id (mf/use-state nil)
frame-hover (mf/use-state nil)
active-frames (mf/use-state #{})
@ -186,7 +265,7 @@
(hooks/setup-viewport-size viewport-ref)
(hooks/setup-cursor cursor alt? mod? space? panning drawing-tool drawing-path? node-editing?)
(hooks/setup-keyboard alt? mod? space?)
(hooks/setup-hover-shapes page-id move-stream base-objects transform selected mod? hover hover-ids @hover-disabled? focus zoom)
(hooks/setup-hover-shapes page-id move-stream base-objects transform selected mod? hover hover-ids hover-top-frame-id @hover-disabled? focus zoom)
(hooks/setup-viewport-modifiers modifiers base-objects)
(hooks/setup-shortcuts node-editing? drawing-path?)
(hooks/setup-active-frames base-objects hover-ids selected active-frames zoom transform vbox)
@ -414,6 +493,16 @@
:hover-frame frame-parent
:disabled-guides? disabled-guides?}])
(let [selected-frame (when (= 1 (count selected-shapes))
(let [selected-shape (get objects-modified (first selected))]
(when (= :frame (:type selected-shape))
selected-shape)))
top-frame (or selected-frame (get objects-modified @hover-top-frame-id))]
(when (and top-frame (not= uuid/zero top-frame) (:layout top-frame))
[:& debug-layout {:shape top-frame
:objects objects-modified}]))
(when show-selection-handlers?
[:g.selection-handlers {:clipPath "url(#clip-handlers)"}
[:defs

View file

@ -28,7 +28,8 @@
[beicon.core :as rx]
[debug :refer [debug?]]
[goog.events :as events]
[rumext.v2 :as mf])
[rumext.v2 :as mf]
[app.common.types.shape-tree :as ctst])
(:import goog.events.EventType))
(defn setup-dom-events [viewport-ref zoom disable-paste in-viewport?]
@ -104,7 +105,8 @@
(some #(cph/is-parent? objects % group-id))
(not))))
(defn setup-hover-shapes [page-id move-stream objects transform selected mod? hover hover-ids hover-disabled? focus zoom]
(defn setup-hover-shapes
[page-id move-stream objects transform selected mod? hover hover-ids hover-top-frame-id hover-disabled? focus zoom]
(let [;; We use ref so we don't recreate the stream on a change
zoom-ref (mf/use-ref zoom)
mod-ref (mf/use-ref @mod?)
@ -143,9 +145,10 @@
(rx/map #(deref last-point-ref)))
(->> move-stream
(rx/tap #(reset! last-point-ref %))
;; When transforming shapes we stop querying the worker
(rx/merge-map query-point)
(rx/tap #(reset! last-point-ref %))))))]
))))]
;; Refresh the refs on a value change
(mf/use-effect
@ -213,7 +216,8 @@
(first)
(get objects))]
(reset! hover hover-shape)
(reset! hover-ids ids))))))
(reset! hover-ids ids)
(reset! hover-top-frame-id (ctst/top-nested-frame objects (deref last-point-ref))))))))
(defn setup-viewport-modifiers
[modifiers objects]

View file

@ -74,7 +74,7 @@
#{:app.main.data.workspace.notifications/handle-pointer-update
:app.main.data.workspace.selection/change-hover-state})
(defonce ^:dynamic *debug* (atom #{#_:events #_:text-outline}))
(defonce ^:dynamic *debug* (atom #{#_:events}))
(defn debug-all! [] (reset! *debug* debug-options))
(defn debug-none! [] (reset! *debug* #{}))