diff --git a/CHANGES.md b/CHANGES.md index 190729d82..42c3513c4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,10 @@ # CHANGELOG +## 1.17.2 + +### :bug: Bugs fixed +- Fix paste board inside itself [Taiga #4775](https://tree.taiga.io/project/penpot/issue/4775) + ## 1.17.1 ### :bug: Bugs fixed diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 826ee7be6..f5ec2bfb7 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -1440,6 +1440,19 @@ (and (= 1 (count selected)) (= :frame (get-in objects [(first selected) :type]))))) +(defn same-frame-from-selected? [state frame-id] + (let [selected (wsh/lookup-selected state)] + (contains? frame-id (first selected)))) + +(defn frame-same-size? + [paste-obj frame-obj] + (and + (= (:heigth (:selrect (first (vals paste-obj)))) + (:heigth (:selrect frame-obj))) + (= (:width (:selrect (first (vals paste-obj)))) + (:width (:selrect frame-obj))))) + + (defn- paste-shape [{selected :selected paste-objects :objects ;; rename this because here comes only the clipboard shapes, @@ -1478,55 +1491,67 @@ item)) (calculate-paste-position [state mouse-pos in-viewport?] - (let [page-objects (wsh/lookup-page-objects state) - selected-objs (map #(get paste-objects %) selected) - page-selected (wsh/lookup-selected state) - wrapper (gsh/selection-rect selected-objs) - orig-pos (gpt/point (:x1 wrapper) (:y1 wrapper))] + (let [page-objects (wsh/lookup-page-objects state) + selected-objs (map #(get paste-objects %) selected) + first-selected-obj (first selected-objs) + page-selected (wsh/lookup-selected state) + wrapper (gsh/selection-rect selected-objs) + orig-pos (gpt/point (:x1 wrapper) (:y1 wrapper)) + frame-id (first page-selected) + frame-object (get page-objects frame-id) + base (cph/get-base-shape page-objects page-selected) + index (cph/get-position-on-parent page-objects (:id base))] (cond - ;; Pasting inside a frame (selected-frame? state) - (let [frame-id (first page-selected) - frame-object (get page-objects frame-id) - origin-frame-id (:frame-id (first selected-objs)) - origin-frame-object (get page-objects origin-frame-id) + (if (or (same-frame-from-selected? state (first (vals paste-objects))) + (frame-same-size? paste-objects frame-object)) + ;; Paste next to selected frame, if selected is itself or of the same size as the copied + (let [selected-frame-obj (get page-objects (first page-selected)) + parent-id (:parent-id base) + paste-x (+ (:width selected-frame-obj) (:x selected-frame-obj) 50) + paste-y (:y selected-frame-obj) + delta (gpt/subtract (gpt/point paste-x paste-y) orig-pos)] - margin-x (-> (- (:width origin-frame-object) (+ (:x wrapper) (:width wrapper))) - (min (- (:width frame-object) (:width wrapper)))) + [(:frame-id base) parent-id delta index]) - margin-y (-> (- (:height origin-frame-object) (+ (:y wrapper) (:height wrapper))) - (min (- (:height frame-object) (:height wrapper)))) + ;; Paste inside selected frame otherwise + (let [origin-frame-id (:frame-id first-selected-obj) + origin-frame-object (get page-objects origin-frame-id) + + margin-x (-> (- (:width origin-frame-object) (+ (:x wrapper) (:width wrapper))) + (min (- (:width frame-object) (:width wrapper)))) + + margin-y (-> (- (:height origin-frame-object) (+ (:y wrapper) (:height wrapper))) + (min (- (:height frame-object) (:height wrapper)))) ;; Pasted objects mustn't exceed the selected frame x limit - paste-x (if (> (+ (:width wrapper) (:x1 wrapper)) (:width frame-object)) - (+ (- (:x frame-object) (:x orig-pos)) (- (:width frame-object) (:width wrapper) margin-x)) - (:x frame-object)) + paste-x (if (> (+ (:width wrapper) (:x1 wrapper)) (:width frame-object)) + (+ (- (:x frame-object) (:x orig-pos)) (- (:width frame-object) (:width wrapper) margin-x)) + (:x frame-object)) ;; Pasted objects mustn't exceed the selected frame y limit - paste-y (if (> (+ (:height wrapper) (:y1 wrapper)) (:height frame-object)) - (+ (- (:y frame-object) (:y orig-pos)) (- (:height frame-object) (:height wrapper) margin-y)) - (:y frame-object)) + paste-y (if (> (+ (:height wrapper) (:y1 wrapper)) (:height frame-object)) + (+ (- (:y frame-object) (:y orig-pos)) (- (:height frame-object) (:height wrapper) margin-y)) + (:y frame-object)) - delta (if (= origin-frame-id uuid/zero) + delta (if (= origin-frame-id uuid/zero) ;; When the origin isn't in a frame the result is pasted in the center. - (gpt/subtract (gsh/center-shape frame-object) (gsh/center-selrect wrapper)) + (gpt/subtract (gsh/center-shape frame-object) (gsh/center-selrect wrapper)) ;; When pasting from one frame to another frame the object position must be limited to container boundaries. If the pasted object doesn't fit we try to: ;; - Align it to the limits on the x and y axis ;; - Respect the distance of the object to the right and bottom in the original frame - (gpt/point paste-x paste-y))] - [frame-id frame-id delta]) - + (gpt/point paste-x paste-y))] + [frame-id frame-id delta])) + (empty? page-selected) (let [frame-id (ctst/top-nested-frame page-objects mouse-pos) delta (gpt/subtract mouse-pos orig-pos)] [frame-id frame-id delta]) :else - (let [base (cph/get-base-shape page-objects page-selected) - index (cph/get-position-on-parent page-objects (:id base)) - frame-id (:frame-id base) + (let [frame-id (:frame-id base) parent-id (:parent-id base) delta (if in-viewport? (gpt/subtract mouse-pos orig-pos)