mirror of
https://github.com/penpot/penpot.git
synced 2025-01-08 16:00:19 -05:00
🐛 Fixes problem with inside-frame calculations
This commit is contained in:
parent
4adb79e869
commit
d1437997d2
11 changed files with 113 additions and 79 deletions
|
@ -11,7 +11,6 @@
|
|||
(:require
|
||||
[clojure.spec.alpha :as s]
|
||||
[app.common.spec :as us]
|
||||
[app.common.pages-helpers :as cph]
|
||||
[app.common.geom.matrix :as gmt]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.math :as mth]
|
||||
|
@ -58,10 +57,17 @@
|
|||
(update :points #(mapv inc-point %))
|
||||
(update :segments #(mapv inc-point %)))))
|
||||
|
||||
;; Duplicated from pages-helpers to remove cyclic dependencies
|
||||
(defn get-children [id objects]
|
||||
(let [shapes (vec (get-in objects [id :shapes]))]
|
||||
(if shapes
|
||||
(d/concat shapes (mapcat #(get-children % objects) shapes))
|
||||
[])))
|
||||
|
||||
(defn recursive-move
|
||||
"Move the shape and all its recursive children."
|
||||
[shape dpoint objects]
|
||||
(let [children-ids (cph/get-children (:id shape) objects)
|
||||
(let [children-ids (get-children (:id shape) objects)
|
||||
children (map #(get objects %) children-ids)]
|
||||
(map #(move % dpoint) (cons shape children))))
|
||||
|
||||
|
|
|
@ -10,7 +10,8 @@
|
|||
(ns app.common.pages-helpers
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.uuid :as uuid]))
|
||||
[app.common.uuid :as uuid]
|
||||
[app.common.geom.shapes :as gsh]))
|
||||
|
||||
(defn walk-pages
|
||||
"Go through all pages of a file and apply a function to each one"
|
||||
|
@ -247,3 +248,11 @@
|
|||
(filter (fn [[idx _]] (and (>= idx from) (<= idx to))))
|
||||
(map second)
|
||||
(into #{}))))
|
||||
|
||||
(defn frame-id-by-position [objects position]
|
||||
(let [frames (select-frames objects)]
|
||||
(or
|
||||
(->> frames
|
||||
(d/seek #(gsh/has-point? % position))
|
||||
:id)
|
||||
uuid/zero)))
|
||||
|
|
|
@ -549,11 +549,8 @@
|
|||
unames (dwc/retrieve-used-names objects)
|
||||
name (dwc/generate-unique-name unames (:name shape))
|
||||
|
||||
frames (cph/select-frames objects)
|
||||
|
||||
frame-id (if (= :frame (:type shape))
|
||||
uuid/zero
|
||||
(dwc/calculate-frame-overlap frames shape))
|
||||
frame-id (or (:frame-id attrs)
|
||||
(cph/frame-id-by-position objects attrs))
|
||||
|
||||
shape (merge
|
||||
(if (= :frame (:type shape))
|
||||
|
@ -561,8 +558,7 @@
|
|||
cp/default-shape-attrs)
|
||||
(assoc shape
|
||||
:id id
|
||||
:name name
|
||||
:frame-id frame-id))
|
||||
:name name))
|
||||
|
||||
rchange {:type :add-obj
|
||||
:id id
|
||||
|
@ -586,19 +582,25 @@
|
|||
[(+ x (/ width 2)) (+ y (/ height 2))]))
|
||||
|
||||
(defn create-and-add-shape
|
||||
[type data]
|
||||
[type frame-x frame-y data]
|
||||
(ptk/reify ::create-and-add-shape
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [{:keys [width height]} data
|
||||
|
||||
[vbc-x vbc-y] (viewport-center state)
|
||||
|
||||
x (:x data (- vbc-x (/ width 2)))
|
||||
y (:y data (- vbc-y (/ height 2)))
|
||||
|
||||
page-id (:current-page-id state)
|
||||
frame-id (-> (dwc/lookup-page-objects state page-id)
|
||||
(cph/frame-id-by-position {:x frame-x :y frame-y}))
|
||||
|
||||
shape (-> (cp/make-minimal-shape type)
|
||||
(merge data)
|
||||
(merge {:x x :y y})
|
||||
(assoc :frame-id frame-id)
|
||||
(geom/setup-selrect))]
|
||||
(rx/of (add-shape shape))))))
|
||||
|
||||
|
@ -1243,16 +1245,26 @@
|
|||
|
||||
(def copy-selected
|
||||
(letfn [(prepare-selected [objects selected]
|
||||
(let [data (reduce #(prepare %1 objects %2) {} selected)]
|
||||
(let [data (reduce #(prepare %1 objects selected %2) {} selected)]
|
||||
{:type :copied-shapes
|
||||
:selected selected
|
||||
:objects data}))
|
||||
|
||||
(prepare [result objects id]
|
||||
(let [obj (get objects id)]
|
||||
(maybe-translate [shape objects selected]
|
||||
(if (and (not= (:type shape) :frame)
|
||||
(not (contains? selected (:frame-id shape))))
|
||||
;; When the parent frame is not selected we change to relative
|
||||
;; coordinates
|
||||
(let [frame (get objects (:frame-id shape))]
|
||||
(geom/translate-to-frame shape frame))
|
||||
shape))
|
||||
|
||||
(prepare [result objects selected id]
|
||||
(let [obj (-> (get objects id)
|
||||
(maybe-translate objects selected))]
|
||||
(as-> result $$
|
||||
(assoc $$ id obj)
|
||||
(reduce #(prepare %1 objects %2) $$ (:shapes obj)))))
|
||||
(reduce #(prepare %1 objects selected %2) $$ (:shapes obj)))))
|
||||
|
||||
(on-copy-error [error]
|
||||
(js/console.error "Clipboard blocked:" error)
|
||||
|
@ -1279,6 +1291,15 @@
|
|||
wrapper (geom/selection-rect selected-objs)
|
||||
orig-pos (gpt/point (:x1 wrapper) (:y1 wrapper))
|
||||
mouse-pos @ms/mouse-position
|
||||
|
||||
page-id (:current-page-id state)
|
||||
frame-id (-> (dwc/lookup-page-objects state page-id)
|
||||
(cph/frame-id-by-position mouse-pos))
|
||||
|
||||
objects (d/mapm (fn [_ v] (assoc v
|
||||
:frame-id frame-id
|
||||
:parent-id frame-id)) objects)
|
||||
|
||||
delta (gpt/subtract mouse-pos orig-pos)
|
||||
|
||||
page-id (:current-page-id state)
|
||||
|
@ -1309,7 +1330,7 @@
|
|||
:height height
|
||||
:id (:id image)
|
||||
:path (:path image)}}]
|
||||
(st/emit! (create-and-add-shape :image shape))))
|
||||
(st/emit! (create-and-add-shape :image x y shape))))
|
||||
|
||||
(defn- paste-image-impl
|
||||
[image]
|
||||
|
@ -1378,12 +1399,16 @@
|
|||
{:keys [x y]} @ms/mouse-position
|
||||
width (min (* 7 (count text)) 700)
|
||||
height 16
|
||||
page-id (:current-page-id state)
|
||||
frame-id (-> (dwc/lookup-page-objects state page-id)
|
||||
(cph/frame-id-by-position @ms/mouse-position))
|
||||
shape (geom/setup-selrect
|
||||
{:id id
|
||||
:type :text
|
||||
:name "Text"
|
||||
:x x
|
||||
:y y
|
||||
:frame-id frame-id
|
||||
:width width
|
||||
:height height
|
||||
:grow-type (if (> (count text) 100) :auto-height :auto-width)
|
||||
|
@ -1391,7 +1416,6 @@
|
|||
(rx/of dwc/start-undo-transaction
|
||||
(dws/deselect-all)
|
||||
(add-shape shape)
|
||||
(dwc/rehash-shape-frame-relationship [id])
|
||||
dwc/commit-undo-transaction)))))
|
||||
|
||||
(defn update-shape-flags
|
||||
|
|
|
@ -155,55 +155,6 @@
|
|||
|
||||
;; --- Common Helpers & Events
|
||||
|
||||
(defn- calculate-frame-overlap
|
||||
[frames shape]
|
||||
(let [xf (comp
|
||||
(filter #(geom/overlaps? % (:selrect shape)))
|
||||
(take 1))
|
||||
frame (first (into [] xf frames))]
|
||||
(or (:id frame) uuid/zero)))
|
||||
|
||||
(defn- calculate-shape-to-frame-relationship-changes
|
||||
[page-id frames shapes]
|
||||
(loop [shape (first shapes)
|
||||
shapes (rest shapes)
|
||||
rch []
|
||||
uch []]
|
||||
(if (nil? shape)
|
||||
[rch uch]
|
||||
(let [fid (calculate-frame-overlap frames shape)]
|
||||
(if (not= fid (:frame-id shape))
|
||||
(recur (first shapes)
|
||||
(rest shapes)
|
||||
(conj rch {:type :mov-objects
|
||||
:page-id page-id
|
||||
:parent-id fid
|
||||
:shapes [(:id shape)]})
|
||||
(conj uch {:type :mov-objects
|
||||
:page-id page-id
|
||||
:parent-id (:frame-id shape)
|
||||
:shapes [(:id shape)]}))
|
||||
(recur (first shapes)
|
||||
(rest shapes)
|
||||
rch
|
||||
uch))))))
|
||||
|
||||
(defn rehash-shape-frame-relationship
|
||||
[ids]
|
||||
(ptk/reify ::rehash-shape-frame-relationship
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [page-id (:current-page-id state)
|
||||
objects (lookup-page-objects state page-id)
|
||||
|
||||
shapes (cph/select-toplevel-shapes objects)
|
||||
frames (cph/select-frames objects)
|
||||
|
||||
[rch uch] (calculate-shape-to-frame-relationship-changes page-id frames shapes)]
|
||||
(when-not (empty? rch)
|
||||
(rx/of (commit-changes rch uch {:commit-local? true})))))))
|
||||
|
||||
|
||||
(defn get-frame-at-point
|
||||
[objects point]
|
||||
(let [frames (cph/select-frames objects)]
|
||||
|
|
|
@ -241,7 +241,7 @@
|
|||
objects (dwc/lookup-page-objects state page-id)
|
||||
unames (atom (dwc/retrieve-used-names objects))
|
||||
|
||||
all-frames (cph/select-frames objects)
|
||||
frame-id (cph/frame-id-by-position objects (gpt/add orig-pos delta))
|
||||
|
||||
update-new-shape
|
||||
(fn [new-shape original-shape]
|
||||
|
@ -255,8 +255,7 @@
|
|||
(as-> $
|
||||
(assoc $ :name new-name)
|
||||
(geom/move $ delta)
|
||||
(assoc $ :frame-id
|
||||
(dwc/calculate-frame-overlap all-frames $))
|
||||
(assoc $ :frame-id frame-id)
|
||||
(assoc $ :parent-id
|
||||
(or (:parent-id $) (:frame-id $)))
|
||||
(assoc $ :shape-ref (:id original-shape))
|
||||
|
|
|
@ -304,10 +304,6 @@
|
|||
renamed-obj (assoc obj :id id :name name)
|
||||
moved-obj (geom/move renamed-obj delta)
|
||||
frames (cph/select-frames objects)
|
||||
frame-id (if frame-id
|
||||
frame-id
|
||||
(dwc/calculate-frame-overlap frames moved-obj))
|
||||
|
||||
parent-id (or parent-id frame-id)
|
||||
|
||||
children-changes
|
||||
|
|
|
@ -233,6 +233,38 @@
|
|||
(rx/first)
|
||||
(rx/map #(start-move from-position))))))
|
||||
|
||||
(defn calculate-frame-for-move [ids]
|
||||
(ptk/reify ::calculate-frame-for-move
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [position @ms/mouse-position
|
||||
page-id (:current-page-id state)
|
||||
objects (dwc/lookup-page-objects state page-id)
|
||||
frame-id (cph/frame-id-by-position objects position)
|
||||
|
||||
moving-shapes (->> ids
|
||||
(map #(get objects %))
|
||||
(remove #(= (:frame-id %) frame-id)))
|
||||
|
||||
rch [{:type :mov-objects
|
||||
:page-id page-id
|
||||
:parent-id frame-id
|
||||
:shapes (mapv :id moving-shapes)}]
|
||||
|
||||
moving-shapes-by-frame-id (group-by :frame-id moving-shapes)
|
||||
|
||||
uch (->> moving-shapes-by-frame-id
|
||||
(mapv (fn [[frame-id shapes]]
|
||||
{:type :mov-objects
|
||||
:page-id page-id
|
||||
:parent-id frame-id
|
||||
:shapes (mapv :id shapes)})))]
|
||||
|
||||
(when-not (empty? rch)
|
||||
(rx/of dwc/pop-undo-into-transaction
|
||||
(dwc/commit-changes rch uch {:commit-local? true})
|
||||
dwc/commit-undo-transaction))))))
|
||||
|
||||
(defn start-move
|
||||
([from-position] (start-move from-position nil))
|
||||
([from-position ids]
|
||||
|
@ -261,6 +293,7 @@
|
|||
|
||||
(rx/of (set-modifiers ids)
|
||||
(apply-modifiers ids)
|
||||
(calculate-frame-for-move ids)
|
||||
(fn [state] (update state :workspace-local dissoc :modifiers))
|
||||
finish-transform)))))))
|
||||
|
||||
|
@ -433,10 +466,8 @@
|
|||
;; we have 3 different objects references).
|
||||
|
||||
rchanges (conj (dwc/generate-changes page-id objects1 objects2) regchg)
|
||||
uchanges (conj (dwc/generate-changes page-id objects2 objects0) regchg)
|
||||
]
|
||||
uchanges (conj (dwc/generate-changes page-id objects2 objects0) regchg)]
|
||||
|
||||
(rx/of dwc/start-undo-transaction
|
||||
(dwc/commit-changes rchanges uchanges {:commit-local? true})
|
||||
(dwc/rehash-shape-frame-relationship ids)
|
||||
dwc/commit-undo-transaction)))))
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
:id (:id image)
|
||||
:path (:path image)}}
|
||||
aspect-ratio (/ (:width image) (:height image))]
|
||||
(st/emit! (dw/create-and-add-shape :image shape))))
|
||||
(st/emit! (dw/create-and-add-shape :image 0 0 shape))))
|
||||
|
||||
on-files-selected
|
||||
(fn [js-files]
|
||||
|
|
|
@ -70,6 +70,13 @@
|
|||
:on-mouse-out on-mouse-out}
|
||||
(:name frame)]))
|
||||
|
||||
(defn make-is-moving-ref
|
||||
[id]
|
||||
(let [check-moving (fn [local]
|
||||
(and (= :move (:transform local))
|
||||
(contains? (:selected local) id)))]
|
||||
(l/derived check-moving refs/workspace-local)))
|
||||
|
||||
(defn frame-wrapper-factory
|
||||
[shape-wrapper]
|
||||
(let [frame-shape (frame/frame-shape shape-wrapper)]
|
||||
|
@ -80,6 +87,11 @@
|
|||
[props]
|
||||
(let [shape (unchecked-get props "shape")
|
||||
objects (unchecked-get props "objects")
|
||||
ghost? (unchecked-get props "ghost?")
|
||||
|
||||
moving-iref (mf/use-memo (mf/deps (:id shape))
|
||||
#(make-is-moving-ref (:id shape)))
|
||||
moving? (mf/deref moving-iref)
|
||||
|
||||
selected-iref (mf/use-memo (mf/deps (:id shape))
|
||||
#(refs/make-selected-ref (:id shape)))
|
||||
|
@ -114,7 +126,9 @@
|
|||
(fn []
|
||||
(st/emit! (dws/change-hover-state (:id shape) false))))]
|
||||
|
||||
(when-not (:hidden shape)
|
||||
(when (and shape
|
||||
(or ghost? (not moving?))
|
||||
(not (:hidden shape)))
|
||||
[:g {:class (when selected? "selected")
|
||||
:on-context-menu on-context-menu
|
||||
:on-double-click on-double-click
|
||||
|
|
|
@ -187,6 +187,10 @@
|
|||
(single? (filter #(= :group (first %)) (:delete operations)))
|
||||
(-> entries (get (first (filter #(= :group (first %)) (:delete operations)))) (last))
|
||||
|
||||
;; If there is a move of shapes will have priority
|
||||
(single? (:move operations))
|
||||
(-> entries (get (first (:move operations))) (last))
|
||||
|
||||
;; Otherwise we could have the same operation between several
|
||||
;; types (i.e: delete various shapes). If that happens we return
|
||||
;; the operation with `:multiple` id
|
||||
|
|
|
@ -408,7 +408,7 @@
|
|||
:id (:id image)
|
||||
:path (:path image)}}
|
||||
aspect-ratio (/ (:width image) (:height image))]
|
||||
(st/emit! (dw/create-and-add-shape :image shape))))
|
||||
(st/emit! (dw/create-and-add-shape :image x y shape))))
|
||||
|
||||
on-drop
|
||||
(fn [event]
|
||||
|
|
Loading…
Reference in a new issue