0
Fork 0
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:
alonso.torres 2023-04-25 15:41:41 +02:00
parent 2df40ad767
commit 43d1f676ef
7 changed files with 227 additions and 37 deletions

View file

@ -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)

View file

@ -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

View file

@ -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)))

View file

@ -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))))

View file

@ -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]

View file

@ -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

View file

@ -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}