From 5820f73b6ea6991ffa163ada52b5a069924aedb7 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Wed, 31 Aug 2022 16:29:38 +0200 Subject: [PATCH 1/3] :bug: Fix problem when hovering over nested frames --- CHANGES.md | 5 +++++ common/src/app/common/pages/helpers.cljc | 6 +++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index fe57da669..630895b8d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,10 @@ # CHANGELOG +## 1.15.3-beta + +### :bug: Bugs fixed + +- Fix problem when hovering over nested frames [Taiga #4018] https://tree.taiga.io/project/penpot/issue/4018 ## 1.15.2-beta ### :bug: Bugs fixed diff --git a/common/src/app/common/pages/helpers.cljc b/common/src/app/common/pages/helpers.cljc index c7214af10..d31fe916f 100644 --- a/common/src/app/common/pages/helpers.cljc +++ b/common/src/app/common/pages/helpers.cljc @@ -44,6 +44,10 @@ [{:keys [type]}] (= type :group)) +(defn bool-shape? + [{:keys [type]}] + (= type :bool)) + (defn text-shape? [{:keys [type]}] (= type :text)) @@ -268,7 +272,7 @@ (= id-a id-b) 0 - (is-shape-over-shape? objects id-a id-b options) + (is-shape-over-shape? objects id-a id-b {:top-frames? true}) 1 :else From d6317297d7493787a55cb85ce47971161161c98e Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Wed, 31 Aug 2022 16:30:30 +0200 Subject: [PATCH 2/3] :bug: Fix error when moving nested frames outside --- CHANGES.md | 2 + .../src/app/main/ui/workspace/shapes.cljs | 34 +++-- .../app/main/ui/workspace/shapes/frame.cljs | 141 ++++++++++-------- .../ui/workspace/viewport/pixel_overlay.cljs | 26 +++- 4 files changed, 120 insertions(+), 83 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 630895b8d..ea75c9dca 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,7 +4,9 @@ ### :bug: Bugs fixed +- Fix error when moving nested frames outside [Taiga #4017] https://tree.taiga.io/project/penpot/issue/4017 - Fix problem when hovering over nested frames [Taiga #4018] https://tree.taiga.io/project/penpot/issue/4018 + ## 1.15.2-beta ### :bug: Bugs fixed diff --git a/frontend/src/app/main/ui/workspace/shapes.cljs b/frontend/src/app/main/ui/workspace/shapes.cljs index 0c93d33ed..298a8b24d 100644 --- a/frontend/src/app/main/ui/workspace/shapes.cljs +++ b/frontend/src/app/main/ui/workspace/shapes.cljs @@ -32,7 +32,8 @@ (declare group-wrapper) (declare svg-raw-wrapper) (declare bool-wrapper) -(declare frame-wrapper) +(declare root-frame-wrapper) +(declare nested-frame-wrapper) (def circle-wrapper (common/generic-wrapper-factory circle/circle-shape)) (def image-wrapper (common/generic-wrapper-factory image/image-shape)) @@ -62,15 +63,25 @@ (mapcat #(cph/get-children-with-self objects (:id %))))] [:& ff/fontfaces-style {:shapes (into [] xform shapes)}]) - (for [item shapes] - (if (cph/frame-shape? item) - [:& frame-wrapper {:shape item - :key (:id item) - :objects (get frame-objects (:id item)) - :thumbnail? (not (contains? active-frames (:id item)))}] + (for [shape shapes] + (cond + (not (cph/frame-shape? shape)) + [:& shape-wrapper + {:shape shape + :key (:id shape)}] - [:& shape-wrapper {:shape item - :key (:id item)}]))])) + (cph/root-frame? shape) + [:& root-frame-wrapper + {:shape shape + :key (:id shape) + :objects (get frame-objects (:id shape)) + :thumbnail? (not (contains? active-frames (:id shape)))}] + + :else + [:& nested-frame-wrapper + {:shape shape + :key (:id shape) + :objects (get frame-objects (:id shape))}]))])) (mf/defc shape-wrapper {::mf/wrap [#(mf/memo' % (mf/check-props ["shape"]))] @@ -98,12 +109,13 @@ :bool [:> bool-wrapper opts] ;; Only used when drawing a new frame. - :frame [:> frame-wrapper opts] + :frame [:> nested-frame-wrapper opts] nil)))) (def group-wrapper (group/group-wrapper-factory shape-wrapper)) (def svg-raw-wrapper (svg-raw/svg-raw-wrapper-factory shape-wrapper)) (def bool-wrapper (bool/bool-wrapper-factory shape-wrapper)) -(def frame-wrapper (frame/frame-wrapper-factory shape-wrapper)) +(def root-frame-wrapper (frame/root-frame-wrapper-factory shape-wrapper)) +(def nested-frame-wrapper (frame/nested-frame-wrapper-factory shape-wrapper)) diff --git a/frontend/src/app/main/ui/workspace/shapes/frame.cljs b/frontend/src/app/main/ui/workspace/shapes/frame.cljs index 2cacfc13d..ad769ade6 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame.cljs @@ -35,9 +35,9 @@ ::mf/forward-ref true} [props ref] - (let [shape (unchecked-get props "shape") - childs-ref (mf/use-memo (mf/deps (:id shape)) #(refs/children-objects (:id shape))) - childs (mf/deref childs-ref)] + (let [shape (unchecked-get props "shape") + 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)} @@ -49,7 +49,7 @@ (= (unchecked-get new-props "thumbnail?") (unchecked-get old-props "thumbnail?")) (= (unchecked-get new-props "shape") (unchecked-get old-props "shape")))) -(defn frame-wrapper-factory +(defn nested-frame-wrapper-factory [shape-wrapper] (let [frame-shape (frame-shape-factory shape-wrapper)] @@ -58,81 +58,92 @@ ::mf/wrap-props false} [props] - (let [shape (unchecked-get props "shape") - frame-id (:id shape) + (let [shape (unchecked-get props "shape") + frame-id (:id shape) + objects (wsh/lookup-page-objects @st/state) + node-ref (mf/use-var nil) + modifiers-ref (mf/use-memo (mf/deps frame-id) #(refs/workspace-modifiers-by-frame-id frame-id)) + modifiers (mf/deref modifiers-ref)] - ;; References to the current rendered node and the its parentn - node-ref (mf/use-var nil) + (fdm/use-dynamic-modifiers objects @node-ref modifiers) + (let [shape (unchecked-get props "shape")] + [:& frame-shape {:shape shape :ref node-ref}]))))) - objects (wsh/lookup-page-objects @st/state) +(defn root-frame-wrapper-factory + [shape-wrapper] - ;; Modifiers - modifiers-ref (mf/use-memo (mf/deps frame-id) #(refs/workspace-modifiers-by-frame-id frame-id)) - modifiers (mf/deref modifiers-ref)] + (let [frame-shape (frame-shape-factory shape-wrapper)] + (mf/fnc frame-wrapper + {::mf/wrap [#(mf/memo' % check-props)] + ::mf/wrap-props false} + [props] + + (let [shape (unchecked-get props "shape") + frame-id (:id shape) + objects (wsh/lookup-page-objects @st/state) + node-ref (mf/use-var nil) + modifiers-ref (mf/use-memo (mf/deps frame-id) #(refs/workspace-modifiers-by-frame-id frame-id)) + modifiers (mf/deref modifiers-ref)] (fdm/use-dynamic-modifiers objects @node-ref modifiers) - (if-not (cph/root-frame? shape) - [:& frame-shape {:shape shape :ref node-ref}] + (let [thumbnail? (unchecked-get props "thumbnail?") + fonts (mf/use-memo (mf/deps shape objects) #(ff/shape->fonts shape objects)) + fonts (-> fonts (hooks/use-equal-memo)) - ;; If the current shape is root we handle its thumbnail and the dynamic modifiers - (let [thumbnail? (unchecked-get props "thumbnail?") - fonts (mf/use-memo (mf/deps shape objects) #(ff/shape->fonts shape objects)) - fonts (-> fonts (hooks/use-equal-memo)) + force-render (mf/use-state false) - force-render (mf/use-state false) + ;; Thumbnail data + page-id (mf/use-ctx ctx/current-page-id) - ;; Thumbnail data - page-id (mf/use-ctx ctx/current-page-id) + ;; when `true` we've called the mount for the frame + rendered? (mf/use-var false) - ;; when `true` we've called the mount for the frame - rendered? (mf/use-var false) + disable-thumbnail? (d/not-empty? (dm/get-in modifiers [(:id shape) :modifiers])) - disable-thumbnail? (d/not-empty? (dm/get-in modifiers [(:id shape) :modifiers])) + [on-load-frame-dom render-frame? thumbnail-renderer] + (ftr/use-render-thumbnail page-id shape node-ref rendered? disable-thumbnail? @force-render) - [on-load-frame-dom render-frame? thumbnail-renderer] - (ftr/use-render-thumbnail page-id shape node-ref rendered? disable-thumbnail? @force-render) + on-frame-load + (fns/use-node-store thumbnail? node-ref rendered? render-frame?)] - on-frame-load - (fns/use-node-store thumbnail? node-ref rendered? render-frame?)] + (mf/use-effect + (mf/deps fonts) + (fn [] + (->> (rx/from fonts) + (rx/merge-map fonts/fetch-font-css) + (rx/ignore)))) - (mf/use-effect - (mf/deps fonts) - (fn [] - (->> (rx/from fonts) - (rx/merge-map fonts/fetch-font-css) - (rx/ignore)))) + (mf/use-effect + (fn [] + ;; When a change in the data is received a "force-render" event is emited + ;; that will force the component to be mounted in memory + (let [sub + (->> (dwt/force-render-stream (:id shape)) + (rx/take-while #(not @rendered?)) + (rx/subs #(reset! force-render true)))] + #(when sub + (rx/dispose! sub))))) - (mf/use-effect - (fn [] - ;; When a change in the data is received a "force-render" event is emited - ;; that will force the component to be mounted in memory - (let [sub - (->> (dwt/force-render-stream (:id shape)) - (rx/take-while #(not @rendered?)) - (rx/subs #(reset! force-render true)))] - #(when sub - (rx/dispose! sub))))) + (mf/use-effect + (mf/deps shape fonts thumbnail? on-load-frame-dom @force-render render-frame?) + (fn [] + (when (and (some? @node-ref) (or @rendered? (not thumbnail?) @force-render render-frame?)) + (mf/mount + (mf/element frame-shape + #js {:ref on-load-frame-dom :shape shape :fonts fonts}) - (mf/use-effect - (mf/deps shape fonts thumbnail? on-load-frame-dom @force-render render-frame?) - (fn [] - (when (and (some? @node-ref) (or @rendered? (not thumbnail?) @force-render render-frame?)) - (mf/mount - (mf/element frame-shape - #js {:ref on-load-frame-dom :shape shape :fonts fonts}) + @node-ref) + (when (not @rendered?) (reset! rendered? true))))) - @node-ref) - (when (not @rendered?) (reset! rendered? true))))) - - [:& shape-container {:shape shape} - [:g.frame-container {:id (dm/str "frame-container-" (:id shape)) - :key "frame-container" - :ref on-frame-load - :opacity (when (:hidden shape) 0)} - [:& ff/fontfaces-style {:fonts fonts}] - [:g.frame-thumbnail-wrapper - {:id (dm/str "thumbnail-container-" (:id shape)) - ;; Hide the thumbnail when not displaying - :opacity (when (and @rendered? (not thumbnail?) (not render-frame?)) 0)} - thumbnail-renderer]]])))))) + [:& shape-container {:shape shape} + [:g.frame-container {:id (dm/str "frame-container-" (:id shape)) + :key "frame-container" + :ref on-frame-load + :opacity (when (:hidden shape) 0)} + [:& ff/fontfaces-style {:fonts fonts}] + [:g.frame-thumbnail-wrapper + {:id (dm/str "thumbnail-container-" (:id shape)) + ;; Hide the thumbnail when not displaying + :opacity (when (and @rendered? (not thumbnail?) (not render-frame?)) 0)} + thumbnail-renderer]]]))))) diff --git a/frontend/src/app/main/ui/workspace/viewport/pixel_overlay.cljs b/frontend/src/app/main/ui/workspace/viewport/pixel_overlay.cljs index 9fda55c82..902053304 100644 --- a/frontend/src/app/main/ui/workspace/viewport/pixel_overlay.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/pixel_overlay.cljs @@ -7,6 +7,7 @@ (ns app.main.ui.workspace.viewport.pixel-overlay (:require [app.common.data :as d] + [app.common.pages.helpers :as cph] [app.common.uuid :as uuid] [app.main.data.modal :as modal] [app.main.data.workspace.colors :as dwc] @@ -41,13 +42,24 @@ shapes (->> (:shapes root) (map (d/getf objects)))] [:g.shapes - (for [item shapes] - (if (= (:type item) :frame) - [:& shapes/frame-wrapper {:shape item - :key (:id item) - :objects objects}] - [:& shapes/shape-wrapper {:shape item - :key (:id item)}]))])) + (for [shape shapes] + (cond + (not (cph/frame-shape? shape)) + [:& shapes/shape-wrapper + {:shape shape + :key (:id shape)}] + + (cph/root-frame? shape) + [:& shapes/root-frame-wrapper + {:shape shape + :key (:id shape) + :objects objects}] + + :else + [:& shapes/nested-frame-wrapper + {:shape shape + :key (:id shape) + :objects objects}]))])) (mf/defc pixel-overlay {::mf/wrap-props false} From 04d6e76c6c96f6f188ccab89e87f568e97d0c264 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 1 Sep 2022 12:44:33 +0200 Subject: [PATCH 3/3] :bug: Fix problem editing rotated texts --- CHANGES.md | 5 ++- common/src/app/common/geom/shapes/text.cljc | 20 +++++---- .../app/main/ui/workspace/shapes/text.cljs | 41 ++++++++++++------- .../main/ui/workspace/shapes/text/editor.cljs | 2 +- 4 files changed, 43 insertions(+), 25 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index ea75c9dca..c8d5b9cb4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,8 +4,9 @@ ### :bug: Bugs fixed -- Fix error when moving nested frames outside [Taiga #4017] https://tree.taiga.io/project/penpot/issue/4017 -- Fix problem when hovering over nested frames [Taiga #4018] https://tree.taiga.io/project/penpot/issue/4018 +- Fix error when moving nested frames outside [Taiga #4017](https://tree.taiga.io/project/penpot/issue/4017) +- Fix problem when hovering over nested frames [Taiga #4018](https://tree.taiga.io/project/penpot/issue/4018) +- Fix problem editing rotated texts [Taiga #4026](https://tree.taiga.io/project/penpot/issue/4026) ## 1.15.2-beta diff --git a/common/src/app/common/geom/shapes/text.cljc b/common/src/app/common/geom/shapes/text.cljc index af23e0c15..992f3c092 100644 --- a/common/src/app/common/geom/shapes/text.cljc +++ b/common/src/app/common/geom/shapes/text.cljc @@ -17,16 +17,22 @@ :width width :height height}) -(defn position-data-points - [{:keys [position-data] :as shape}] - (let [points (->> position-data - (mapcat (comp gpr/rect->points position-data->rect))) - transform (gtr/transform-matrix shape)] - (gco/transform-points points transform))) +(defn position-data-selrect + [shape] + (let [points (->> shape + :position-data + (mapcat (comp gpr/rect->points position-data->rect)))] + (-> points (gpr/points->selrect)))) (defn position-data-bounding-box [shape] - (gpr/points->selrect (position-data-points shape))) + (let [points (->> shape + :position-data + (mapcat (comp gpr/rect->points position-data->rect))) + transform (gtr/transform-matrix shape)] + (-> points + (gco/transform-points transform) + (gpr/points->selrect )))) (defn overlaps-position-data? "Checks if the given position data is inside the shape" diff --git a/frontend/src/app/main/ui/workspace/shapes/text.cljs b/frontend/src/app/main/ui/workspace/shapes/text.cljs index 7a5b662d9..28dee8f66 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text.cljs @@ -8,6 +8,8 @@ (:require [app.common.data :as d] [app.common.data.macros :as dm] + [app.common.geom.shapes :as gsh] + [app.common.geom.shapes.text :as gsht] [app.common.math :as mth] [app.main.data.workspace.texts :as dwt] [app.main.refs :as refs] @@ -37,19 +39,28 @@ [:& text/text-shape {:shape shape}]] (when (and (debug? :text-outline) (d/not-empty? (:position-data shape))) - (for [[index data] (d/enumerate (:position-data shape))] - (let [{:keys [x y width height]} data] - [:g {:key (dm/str index)} - ;; Text fragment bounding box - [:rect {:x x - :y (- y height) - :width width - :height height - :style {:fill "none" :stroke "red"}}] + [:g {:transform (gsh/transform-str shape {:no-flip true})} + (let [bounding-box (gsht/position-data-selrect shape)] + [:rect { + :x (:x bounding-box) + :y (:y bounding-box) + :width (:width bounding-box) + :height (:height bounding-box) + :style { :fill "none" :stroke "orange"}}]) - ;; Text baselineazo - [:line {:x1 (mth/round x) - :y1 (mth/round (- (:y data) (:height data))) - :x2 (mth/round (+ x width)) - :y2 (mth/round (- (:y data) (:height data))) - :style {:stroke "blue"}}]])))])) + (for [[index data] (d/enumerate (:position-data shape))] + (let [{:keys [x y width height]} data] + [:g {:key (dm/str index)} + ;; Text fragment bounding box + [:rect {:x x + :y (- y height) + :width width + :height height + :style {:fill "none" :stroke "red"}}] + + ;; Text baselineazo + [:line {:x1 (mth/round x) + :y1 (mth/round (- (:y data) (:height data))) + :x2 (mth/round (+ x width)) + :y2 (mth/round (- (:y data) (:height data))) + :style {:stroke "blue"}}]]))])])) diff --git a/frontend/src/app/main/ui/workspace/shapes/text/editor.cljs b/frontend/src/app/main/ui/workspace/shapes/text/editor.cljs index 0e9791dfd..484141d69 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text/editor.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text/editor.cljs @@ -272,7 +272,7 @@ (some? text-modifier) (dwt/apply-text-modifier text-modifier)) - bounding-box (gsht/position-data-bounding-box shape) + bounding-box (gsht/position-data-selrect shape) x (min (:x bounding-box) (:x shape)) y (min (:y bounding-box) (:y shape))