mirror of
https://github.com/penpot/penpot.git
synced 2025-02-13 10:38:13 -05:00
✨ Move shapes in grid
This commit is contained in:
parent
2df40ad767
commit
43d1f676ef
7 changed files with 227 additions and 37 deletions
|
@ -13,7 +13,5 @@
|
|||
(dm/export glld/calc-layout-data)
|
||||
(dm/export glld/get-cell-data)
|
||||
(dm/export glp/child-modifiers)
|
||||
|
||||
(defn get-drop-index
|
||||
[frame objects _position]
|
||||
(dec (count (get-in objects [frame :shapes]))))
|
||||
(dm/export glp/get-position-grid-coord)
|
||||
(dm/export glp/get-drop-cell)
|
||||
|
|
|
@ -131,7 +131,7 @@
|
|||
(reduce (fn [tracks [child-bounds child-shape]]
|
||||
(let [cell (get shape-cells (:id child-shape))
|
||||
idx (dec (get cell prop))
|
||||
track (nth tracks idx)]
|
||||
track (get tracks idx)]
|
||||
(cond-> tracks
|
||||
(and (= (get cell prop-span) 1) (= :auto (:type track)))
|
||||
(update-in [idx :size] max (size-fn child-bounds)))))
|
||||
|
@ -190,9 +190,6 @@
|
|||
bound-width (gpo/width-points layout-bounds)
|
||||
bound-corner (gpo/origin layout-bounds)
|
||||
|
||||
grid-columns (:layout-grid-columns parent)
|
||||
grid-rows (:layout-grid-rows parent)
|
||||
|
||||
[row-gap column-gap] (ctl/gaps parent)
|
||||
|
||||
;; Map shape->cell
|
||||
|
@ -205,11 +202,11 @@
|
|||
|
||||
;; Initialize tracks
|
||||
column-tracks
|
||||
(->> grid-columns
|
||||
(->> (:layout-grid-columns parent)
|
||||
(mapv (partial calculate-initial-track-size bound-width)))
|
||||
|
||||
row-tracks
|
||||
(->> grid-rows
|
||||
(->> (:layout-grid-rows parent)
|
||||
(mapv (partial calculate-initial-track-size bound-height)))
|
||||
|
||||
;; Go through cells to adjust auto sizes for span=1. Base is the max of its children
|
||||
|
|
|
@ -6,11 +6,91 @@
|
|||
|
||||
(ns app.common.geom.shapes.grid-layout.positions
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.matrix :as gmt]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes.common :as gco]
|
||||
[app.common.geom.shapes.grid-layout.layout-data :as ld]
|
||||
[app.common.geom.shapes.points :as gpo]
|
||||
[app.common.math :as mth]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.types.modifiers :as ctm]))
|
||||
|
||||
(defn child-modifiers
|
||||
[_parent _transformed-parent-bounds _child child-bounds cell-data]
|
||||
(ctm/move-modifiers
|
||||
(gpt/subtract (:start-p cell-data) (gpo/origin child-bounds))))
|
||||
|
||||
|
||||
(defn line-value
|
||||
[[{px :x py :y} {vx :x vy :y}] {:keys [x y]}]
|
||||
(let [a vy
|
||||
b (- vx)
|
||||
c (+ (* (- vy) px) (* vx py))]
|
||||
(+ (* a x) (* b y) c)))
|
||||
|
||||
(defn is-inside-lines?
|
||||
[line-1 line-2 pos]
|
||||
(< (* (line-value line-1 pos) (line-value line-2 pos)) 0))
|
||||
|
||||
(defn get-position-grid-coord
|
||||
[{:keys [layout-bounds row-tracks column-tracks]} position]
|
||||
|
||||
(let [hv #(gpo/start-hv layout-bounds %)
|
||||
vv #(gpo/start-vv layout-bounds %)
|
||||
|
||||
;;make-is-inside-track
|
||||
;;(fn [type]
|
||||
;; (let [[vfn ofn] (if (= type :column) [vv hv] [hv vv])]
|
||||
;; (fn is-inside-track? [{:keys [start-p size] :as track}]
|
||||
;; (let [unit-v (vfn 1)
|
||||
;; end-p (gpt/add start-p (ofn size))]
|
||||
;; (is-inside-lines? [start-p unit-v] [end-p unit-v] position)))))
|
||||
|
||||
make-min-distance-track
|
||||
(fn [type]
|
||||
(let [[vfn ofn] (if (= type :column) [vv hv] [hv vv])]
|
||||
(fn [[selected selected-dist] [cur-idx {:keys [start-p size] :as track}]]
|
||||
(let [unit-v (vfn 1)
|
||||
end-p (gpt/add start-p (ofn size))
|
||||
dist-1 (mth/abs (line-value [start-p unit-v] position))
|
||||
dist-2 (mth/abs (line-value [end-p unit-v] position))]
|
||||
|
||||
(if (or (< dist-1 selected-dist) (< dist-2 selected-dist))
|
||||
[[cur-idx track] (min dist-1 dist-2)]
|
||||
[selected selected-dist])))))
|
||||
|
||||
;;[col-idx column]
|
||||
;;(->> (d/enumerate column-tracks)
|
||||
;; (d/seek (comp (make-is-inside-track :column) second)))
|
||||
;;
|
||||
;;[row-idx row]
|
||||
;;(->> (d/enumerate row-tracks)
|
||||
;; (d/seek (comp (make-is-inside-track :row) second)))
|
||||
|
||||
|
||||
[col-idx column]
|
||||
(->> (d/enumerate column-tracks)
|
||||
(reduce (make-min-distance-track :column) [[nil nil] ##Inf])
|
||||
(first))
|
||||
|
||||
[row-idx row]
|
||||
(->> (d/enumerate row-tracks)
|
||||
(reduce (make-min-distance-track :row) [[nil nil] ##Inf])
|
||||
(first))
|
||||
]
|
||||
|
||||
(when (and (some? column) (some? row))
|
||||
[(inc row-idx) (inc col-idx)])))
|
||||
|
||||
(defn get-drop-cell
|
||||
[frame-id objects position]
|
||||
|
||||
(let [frame (get objects frame-id)
|
||||
children (->> (cph/get-immediate-children objects (:id frame))
|
||||
(remove :hidden)
|
||||
(map #(vector (gpo/parent-coords-bounds (:points %) (: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)))
|
||||
|
|
|
@ -679,6 +679,22 @@
|
|||
[parent _cells]
|
||||
parent)
|
||||
|
||||
(defn get-cells
|
||||
([parent]
|
||||
(get-cells parent nil))
|
||||
|
||||
([{:keys [layout-grid-cells layout-grid-dir]} {:keys [sort?] :or {sort? false}}]
|
||||
(let [comp-fn (if (= layout-grid-dir :row)
|
||||
(juxt :row :column)
|
||||
(juxt :column :row))
|
||||
|
||||
maybe-sort?
|
||||
(if sort? (partial sort-by (comp comp-fn second)) identity)]
|
||||
|
||||
(->> layout-grid-cells
|
||||
(maybe-sort?)
|
||||
(map (fn [[id cell]] (assoc cell :id id)))))))
|
||||
|
||||
(defn get-free-cells
|
||||
([parent]
|
||||
(get-free-cells parent nil))
|
||||
|
@ -700,7 +716,7 @@
|
|||
"Clean the cells whith shapes that are no longer in the layout"
|
||||
[parent]
|
||||
|
||||
(let [child? (into #{} (:shapes parent))
|
||||
(let [child? (set (:shapes parent))
|
||||
cells (update-vals
|
||||
(:layout-grid-cells parent)
|
||||
(fn [cell] (update cell :shapes #(filterv child? %))))]
|
||||
|
@ -767,3 +783,63 @@
|
|||
(recur cells (rest free-cells) (rest pending)))))]
|
||||
|
||||
(assoc parent :layout-grid-cells cells)))))
|
||||
|
||||
(defn free-cell-push
|
||||
"Frees the cell at index and push the shapes in the order given by the `cells` attribute"
|
||||
[parent cells index]
|
||||
|
||||
(let [start-cell (get cells index)]
|
||||
(if (empty? (:shapes start-cell))
|
||||
[parent cells]
|
||||
(let [[parent result-cells]
|
||||
(loop [parent parent
|
||||
result-cells cells
|
||||
idx index]
|
||||
|
||||
(if (> idx (- (count cells) 2))
|
||||
[parent result-cells]
|
||||
|
||||
(let [cell-from (get cells idx)
|
||||
cell-to (get cells (inc idx))
|
||||
cell (assoc cell-to :shapes (:shapes cell-from))
|
||||
parent (assoc-in parent [:layout-grid-cells (:id cell)] cell)
|
||||
result-cells (assoc result-cells (inc idx) cell)]
|
||||
|
||||
(if (empty? (:shapes cell-to))
|
||||
;; to-cell was empty, so we've finished and every cell allocated
|
||||
[parent result-cells]
|
||||
|
||||
;; otherwise keep pushing cells
|
||||
(recur parent result-cells (inc idx))))))]
|
||||
|
||||
[(assoc-in parent [:layout-grid-cells (get-in cells [index :id]) :shapes] [])
|
||||
(assoc-in result-cells [index :shapes] [])]))))
|
||||
|
||||
(defn seek-indexed-cell
|
||||
[cells row column]
|
||||
(let [cells+index (d/enumerate cells)]
|
||||
(d/seek (fn [[_ {cell-row :row cell-column :column}]]
|
||||
(and (= cell-row row)
|
||||
(= cell-column column))) cells+index)))
|
||||
|
||||
(defn push-into-cell
|
||||
"Push the shapes into the row/column cell and moves the rest"
|
||||
[parent shape-ids row column]
|
||||
|
||||
(let [cells (vec (get-cells parent {:sort? true}))
|
||||
cells+index (d/enumerate cells)
|
||||
|
||||
[start-index _] (seek-indexed-cell cells row column)
|
||||
|
||||
;; start-index => to-index is the range where the shapes inserted will be added
|
||||
to-index (min (+ start-index (count shape-ids)) (dec (count cells)))]
|
||||
|
||||
;; Move shift the `shapes` attribute between cells
|
||||
(->> (range start-index (inc to-index))
|
||||
(map vector shape-ids)
|
||||
(reduce (fn [[parent cells] [shape-id idx]]
|
||||
(let [[parent cells] (free-cell-push parent cells idx)]
|
||||
[(assoc-in parent [:layout-grid-cells (get-in cells [idx :id]) :shapes] [shape-id])
|
||||
cells]))
|
||||
[parent cells])
|
||||
(first))))
|
||||
|
|
|
@ -175,8 +175,28 @@
|
|||
(update-in modif-tree [parent-id :modifiers] ctm/remove-children [child-id])))
|
||||
modif-tree)))
|
||||
|
||||
(defn add-grid-children-modifiers
|
||||
[modifiers frame-id selected objects [row column :as cell]]
|
||||
(let [frame (get objects frame-id)
|
||||
|
||||
;; Temporary remove the children when moving them
|
||||
frame (-> frame
|
||||
(update :shapes #(d/removev selected %))
|
||||
(ctl/assign-cells))
|
||||
|
||||
frame (-> frame
|
||||
(update :shapes d/concat-vec selected)
|
||||
(cond-> (some? cell)
|
||||
(ctl/push-into-cell selected row column))
|
||||
(ctl/assign-cells))]
|
||||
|
||||
(-> modifiers
|
||||
(ctm/change-property :layout-grid-rows (:layout-grid-rows frame))
|
||||
(ctm/change-property :layout-grid-columns (:layout-grid-columns frame))
|
||||
(ctm/change-property :layout-grid-cells (:layout-grid-cells frame)))))
|
||||
|
||||
(defn build-change-frame-modifiers
|
||||
[modif-tree objects selected target-frame-id drop-index]
|
||||
[modif-tree objects selected target-frame-id drop-index cell-data]
|
||||
|
||||
(let [origin-frame-ids (->> selected (group-by #(get-in objects [% :frame-id])))
|
||||
child-set (set (get-in objects [target-frame-id :shapes]))
|
||||
|
@ -209,8 +229,10 @@
|
|||
children-ids (->> (dm/get-in objects [original-frame :shapes])
|
||||
(remove (set selected)))
|
||||
|
||||
h-sizing? (ctl/change-h-sizing? original-frame objects children-ids)
|
||||
v-sizing? (ctl/change-v-sizing? original-frame objects children-ids)]
|
||||
h-sizing? (and (ctl/flex-layout? objects original-frame)
|
||||
(ctl/change-h-sizing? original-frame objects children-ids))
|
||||
v-sizing? (and (ctl/flex-layout? objects original-frame)
|
||||
(ctl/change-v-sizing? original-frame objects children-ids))]
|
||||
(cond-> modif-tree
|
||||
(not= original-frame target-frame-id)
|
||||
(-> (modifier-remove-from-parent objects shapes)
|
||||
|
@ -227,11 +249,19 @@
|
|||
(as-> modif-tree $
|
||||
(reduce update-frame-modifiers $ origin-frame-ids)
|
||||
(cond-> $
|
||||
(ctl/change-h-sizing? target-frame-id objects children-ids)
|
||||
(update-in [target-frame-id :modifiers] ctm/change-property :layout-item-h-sizing :fix))
|
||||
(cond-> $
|
||||
(ctl/change-v-sizing? target-frame-id objects children-ids)
|
||||
(update-in [target-frame-id :modifiers] ctm/change-property :layout-item-v-sizing :fix)))))
|
||||
;; Set fix position to target frame (horizontal)
|
||||
(and (ctl/flex-layout? objects target-frame-id)
|
||||
(ctl/change-h-sizing? target-frame-id objects children-ids))
|
||||
(update-in [target-frame-id :modifiers] ctm/change-property :layout-item-h-sizing :fix)
|
||||
|
||||
;; Set fix position to target frame (vertical)
|
||||
(and (ctl/flex-layout? objects target-frame-id)
|
||||
(ctl/change-v-sizing? target-frame-id objects children-ids))
|
||||
(update-in [target-frame-id :modifiers] ctm/change-property :layout-item-v-sizing :fix)
|
||||
|
||||
;; Add the object to the cell
|
||||
(ctl/grid-layout? objects target-frame-id)
|
||||
(update-in [target-frame-id :modifiers] add-grid-children-modifiers target-frame-id selected objects cell-data)))))
|
||||
|
||||
(defn modif->js
|
||||
[modif-tree objects]
|
||||
|
@ -454,6 +484,9 @@
|
|||
:layout-gap
|
||||
:layout-item-margin
|
||||
:layout-item-margin-type
|
||||
:layout-grid-cells
|
||||
:layout-grid-columns
|
||||
:layout-grid-rows
|
||||
]})
|
||||
;; We've applied the text-modifier so we can dissoc the temporary data
|
||||
(fn [state]
|
||||
|
|
|
@ -490,10 +490,9 @@
|
|||
target-frame (ctst/top-nested-frame objects position exclude-frames)
|
||||
flex-layout? (ctl/flex-layout? objects target-frame)
|
||||
grid-layout? (ctl/grid-layout? objects target-frame)
|
||||
drop-index (cond
|
||||
flex-layout? (gslf/get-drop-index target-frame objects position)
|
||||
grid-layout? (gslg/get-drop-index target-frame objects position))]
|
||||
[move-vector target-frame drop-index])))
|
||||
drop-index (when flex-layout? (gslf/get-drop-index target-frame objects position))
|
||||
cell-data (when grid-layout? (gslg/get-drop-cell target-frame objects position))]
|
||||
[move-vector target-frame drop-index cell-data])))
|
||||
|
||||
(rx/take-until stopper))]
|
||||
|
||||
|
@ -502,7 +501,7 @@
|
|||
(->> move-stream
|
||||
(rx/with-latest-from ms/mouse-position-shift)
|
||||
(rx/map
|
||||
(fn [[[move-vector target-frame drop-index] shift?]]
|
||||
(fn [[[move-vector target-frame drop-index cell-data] shift?]]
|
||||
(let [x-disp? (> (mth/abs (:x move-vector)) (mth/abs (:y move-vector)))
|
||||
[move-vector snap-ignore-axis]
|
||||
(cond
|
||||
|
@ -516,7 +515,7 @@
|
|||
[move-vector nil])]
|
||||
|
||||
(-> (dwm/create-modif-tree ids (ctm/move-modifiers move-vector))
|
||||
(dwm/build-change-frame-modifiers objects selected target-frame drop-index)
|
||||
(dwm/build-change-frame-modifiers objects selected target-frame drop-index cell-data)
|
||||
(dwm/set-modifiers false false {:snap-ignore-axis snap-ignore-axis}))))))
|
||||
|
||||
(->> move-stream
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
[app.main.data.workspace.shape-layout :as dwsl]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.workspace.viewport.viewport-ref :as uwvv]
|
||||
[app.util.dom :as dom]
|
||||
[cuerdas.core :as str]
|
||||
[rumext.v2 :as mf]))
|
||||
|
@ -153,18 +154,20 @@
|
|||
(mf/use-callback
|
||||
(mf/deps on-drag-start)
|
||||
(fn [event]
|
||||
(let [position (dom/get-client-position event)]
|
||||
(let [raw-pt (dom/get-client-position event)
|
||||
position (uwvv/point->viewport raw-pt)]
|
||||
(dom/capture-pointer event)
|
||||
(mf/set-ref-val! dragging-ref true)
|
||||
(mf/set-ref-val! start-pos-ref position)
|
||||
(mf/set-ref-val! current-pos-ref position)
|
||||
(mf/set-ref-val! start-pos-ref raw-pt)
|
||||
(mf/set-ref-val! current-pos-ref raw-pt)
|
||||
(when on-drag-start (on-drag-start position)))))
|
||||
|
||||
handle-lost-pointer-capture
|
||||
(mf/use-callback
|
||||
(mf/deps on-drag-end)
|
||||
(fn [event]
|
||||
(let [position (mf/ref-val current-pos-ref)]
|
||||
(let [raw-pt (mf/ref-val current-pos-ref)
|
||||
position (uwvv/point->viewport raw-pt)]
|
||||
(dom/release-pointer event)
|
||||
(mf/set-ref-val! dragging-ref false)
|
||||
(mf/set-ref-val! start-pos-ref nil)
|
||||
|
@ -175,10 +178,11 @@
|
|||
(fn [event]
|
||||
(when (mf/ref-val dragging-ref)
|
||||
(let [start (mf/ref-val start-pos-ref)
|
||||
pos (dom/get-client-position event)]
|
||||
pos (dom/get-client-position event)
|
||||
pt (uwvv/point->viewport pos)]
|
||||
(mf/set-ref-val! current-pos-ref pos)
|
||||
(when on-drag-delta (on-drag-delta (gpt/to-vec start pos)))
|
||||
(when on-drag-position (on-drag-position pos))))))]
|
||||
(when on-drag-position (on-drag-position pt))))))]
|
||||
|
||||
{:handle-pointer-down handle-pointer-down
|
||||
:handle-lost-pointer-capture handle-lost-pointer-capture
|
||||
|
@ -192,15 +196,16 @@
|
|||
width (unchecked-get props "width")
|
||||
height (unchecked-get props "height")
|
||||
direction (unchecked-get props "direction")
|
||||
layout-data (unchecked-get props "layout-data")
|
||||
cursor (if (= direction :row) (cur/scale-ns 0) (cur/scale-ew 0))
|
||||
|
||||
handle-drag-delta
|
||||
handle-drag-position
|
||||
(mf/use-callback
|
||||
(fn [delta]
|
||||
(prn ">>>" delta)))
|
||||
(fn [position]
|
||||
(prn ">>>" (gsg/get-position-grid-coord layout-data position))))
|
||||
|
||||
{:keys [handle-pointer-down handle-lost-pointer-capture handle-pointer-move]}
|
||||
(use-drag {:on-drag-delta handle-drag-delta})]
|
||||
(use-drag {:on-drag-position handle-drag-position})]
|
||||
|
||||
[:rect
|
||||
{:x x
|
||||
|
@ -218,7 +223,8 @@
|
|||
[props]
|
||||
(let [shape (unchecked-get props "shape")
|
||||
|
||||
{:keys [origin row-tracks column-tracks layout-bounds]} (unchecked-get props "layout-data")
|
||||
{:keys [origin row-tracks column-tracks layout-bounds] :as layout-data}
|
||||
(unchecked-get props "layout-data")
|
||||
|
||||
zoom (unchecked-get props "zoom")
|
||||
|
||||
|
@ -278,7 +284,8 @@
|
|||
:y y
|
||||
:width width
|
||||
:height height
|
||||
:direction dir}])]))]))
|
||||
:direction dir
|
||||
:layout-data layout-data}])]))]))
|
||||
|
||||
(mf/defc resize-handler
|
||||
{::mf/wrap-props false}
|
||||
|
|
Loading…
Add table
Reference in a new issue