0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-03-12 15:51:37 -05:00

🐛 Fix problems with thumbnails

This commit is contained in:
alonso.torres 2022-05-11 13:44:47 +02:00
parent f2bb59fd77
commit c14ece9f8d
7 changed files with 271 additions and 250 deletions

View file

@ -153,7 +153,8 @@
(ptk/reify ::duplicate-thumbnail
ptk/UpdateEvent
(update [_ state]
(let [old-shape-thumbnail (get-in state [:workspace-file :thumbnails old-id])]
(-> state (assoc-in [:workspace-file :thumbnails new-id] old-shape-thumbnail))))))
(let [page-id (get state :current-page-id)
old-shape-thumbnail (get-in state [:workspace-file :thumbnails (dm/str page-id old-id)])]
(-> state (assoc-in [:workspace-file :thumbnails (dm/str page-id new-id)] old-shape-thumbnail))))))

View file

@ -55,17 +55,26 @@
:width width
:height height
:className "frame-background"}))
path? (some? (.-d props))]
path? (some? (.-d props))
render-id (mf/use-ctx muc/render-ctx)]
[:*
[:image.frame-thumbnail
{:id (dm/str "thumbnail-" (:id shape))
:xlinkHref (:thumbnail shape)
:x (:x shape)
:y (:y shape)
:width (:width shape)
:height (:height shape)
;; DEBUG
:style {:filter (when (debug? :thumbnails) "sepia(1)")}}]
[:g {:clip-path (frame-clip-url shape render-id)}
[:& frame-clip-def {:shape shape :render-id render-id}]
[:& shape-fills {:shape shape}
(if path?
[:> :path props]
[:> :rect props])]
[:image.frame-thumbnail
{:id (dm/str "thumbnail-" (:id shape))
:xlinkHref (:thumbnail shape)
:x (:x shape)
:y (:y shape)
:width (:width shape)
:height (:height shape)
;; DEBUG
:style {:filter (when (debug? :thumbnails) "sepia(1)")}}]]
[:& shape-strokes {:shape shape}
(if path?
@ -96,16 +105,16 @@
[:*
[:g {:clip-path (frame-clip-url shape render-id)}
[:*
[:& shape-fills {:shape shape}
(if path?
[:> :path props]
[:> :rect props])]
[:& shape-fills {:shape shape}
(if path?
[:> :path props]
[:> :rect props])]
[:g.frame-children
(for [item childs]
[:& shape-wrapper {:shape item
:key (dm/str (:id item))}])
]]
:key (dm/str (:id item))}])]]
[:& shape-strokes {:shape shape}
(if path?
[:> :path props]

View file

@ -8,6 +8,7 @@
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.uuid :as uuid]
[app.main.data.workspace.thumbnails :as dwt]
[app.main.refs :as refs]
[app.main.ui.context :as ctx]
@ -61,6 +62,7 @@
thumbnail? (unchecked-get props "thumbnail?")
objects (unchecked-get props "objects")
render-id (mf/use-memo #(str (uuid/next)))
fonts (mf/use-memo (mf/deps shape objects) #(ff/shape->fonts shape objects))
fonts (-> fonts (hooks/use-equal-memo))
@ -73,7 +75,7 @@
thumbnail-data-ref (mf/use-memo (mf/deps page-id frame-id) #(refs/thumbnail-frame-data page-id frame-id))
thumbnail-data (mf/deref thumbnail-data-ref)
thumbnail? (and thumbnail? (or (some? (:thumbnail shape)) (some? thumbnail-data)))
thumbnail? (and thumbnail? (some? thumbnail-data))
;; References to the current rendered node and the its parentn
node-ref (mf/use-var nil)
@ -117,11 +119,12 @@
@node-ref)
(when (not @rendered?) (reset! rendered? true)))))
[:g.frame-container {:key "frame-container" :ref on-frame-load}
[:g.frame-thumbnail {:id (dm/str "thumbnail-container-" (:id shape))}
[:> frame/frame-thumbnail {:key (dm/str (:id shape))
:shape (cond-> shape
(some? thumbnail-data)
(assoc :thumbnail thumbnail-data))}]
[:& (mf/provider ctx/render-ctx) {:value render-id}
[:g.frame-container {:key "frame-container" :ref on-frame-load}
[:g.frame-thumbnail-wrapper {:id (dm/str "thumbnail-container-" (:id shape))}
[:> frame/frame-thumbnail {:key (dm/str (:id shape))
:shape (cond-> shape
(some? thumbnail-data)
(assoc :thumbnail thumbnail-data))}]
thumb-renderer]]))))
thumb-renderer]]]))))

View file

@ -7,10 +7,223 @@
(ns app.main.ui.workspace.shapes.frame.dynamic-modifiers
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.geom.matrix :as gmt]
[app.common.geom.point :as gpt]
[app.common.geom.shapes :as gsh]
[app.main.ui.workspace.viewport.utils :as utils]
[app.util.dom :as dom]
[rumext.alpha :as mf]))
(defn- transform-no-resize
"If we apply a scale directly to the texts it will show deformed so we need to create this
correction matrix to \"undo\" the resize but keep the other transformations."
[{:keys [x y width height points transform transform-inverse] :as shape} current-transform modifiers]
(let [corner-pt (first points)
corner-pt (cond-> corner-pt (some? transform-inverse) (gpt/transform transform-inverse))
resize-x? (some? (:resize-vector modifiers))
resize-y? (some? (:resize-vector-2 modifiers))
flip-x? (neg? (get-in modifiers [:resize-vector :x]))
flip-y? (or (neg? (get-in modifiers [:resize-vector :y]))
(neg? (get-in modifiers [:resize-vector-2 :y])))
result (cond-> (gmt/matrix)
(and (some? transform) (or resize-x? resize-y?))
(gmt/multiply transform)
resize-x?
(gmt/scale (gpt/inverse (:resize-vector modifiers)) corner-pt)
resize-y?
(gmt/scale (gpt/inverse (:resize-vector-2 modifiers)) corner-pt)
flip-x?
(gmt/scale (gpt/point -1 1) corner-pt)
flip-y?
(gmt/scale (gpt/point 1 -1) corner-pt)
(and (some? transform) (or resize-x? resize-y?))
(gmt/multiply transform-inverse))
[width height]
(if (or resize-x? resize-y?)
(let [pc (cond-> (gpt/point x y)
(some? transform)
(gpt/transform transform)
(some? current-transform)
(gpt/transform current-transform))
pw (cond-> (gpt/point (+ x width) y)
(some? transform)
(gpt/transform transform)
(some? current-transform)
(gpt/transform current-transform))
ph (cond-> (gpt/point x (+ y height))
(some? transform)
(gpt/transform transform)
(some? current-transform)
(gpt/transform current-transform))]
[(gpt/distance pc pw) (gpt/distance pc ph)])
[width height])]
[result width height]))
(defn get-nodes
"Retrieve the DOM nodes to apply the matrix transformation"
[base-node {:keys [id type masked-group?]}]
(let [shape-node (dom/query base-node (str "#shape-" id))
frame? (= :frame type)
group? (= :group type)
text? (= :text type)
mask? (and group? masked-group?)]
(cond
frame?
[shape-node
(dom/query shape-node ".frame-children")
(dom/query (str "#thumbnail-container-" id))
(dom/query (str "#thumbnail-" id))]
;; For groups we don't want to transform the whole group but only
;; its filters/masks
mask?
[(dom/query shape-node ".mask-clip-path")
(dom/query shape-node ".mask-shape")]
group?
(let [shape-defs (dom/query shape-node "defs")]
(d/concat-vec
(dom/query-all shape-defs ".svg-def")
(dom/query-all shape-defs ".svg-mask-wrapper")))
text?
[shape-node
(dom/query shape-node ".text-shape")]
:else
[shape-node])))
(defn transform-region!
[node modifiers]
(let [{:keys [x y width height]}
(-> (gsh/make-selrect
(-> (dom/get-attribute node "data-old-x") d/parse-double)
(-> (dom/get-attribute node "data-old-y") d/parse-double)
(-> (dom/get-attribute node "data-old-width") d/parse-double)
(-> (dom/get-attribute node "data-old-height") d/parse-double))
(gsh/transform-selrect modifiers))]
(dom/set-attribute! node "x" x)
(dom/set-attribute! node "y" y)
(dom/set-attribute! node "width" width)
(dom/set-attribute! node "height" height)))
(defn start-transform!
[base-node shapes]
(doseq [shape shapes]
(when-let [nodes (get-nodes base-node shape)]
(doseq [node nodes]
(let [old-transform (dom/get-attribute node "transform")]
(when (some? old-transform)
(dom/set-attribute! node "data-old-transform" old-transform))
(when (or (= (dom/get-tag-name node) "linearGradient")
(= (dom/get-tag-name node) "radialGradient"))
(let [gradient-transform (dom/get-attribute node "gradientTransform")]
(when (some? gradient-transform)
(dom/set-attribute! node "data-old-gradientTransform" gradient-transform))))
(when (= (dom/get-tag-name node) "pattern")
(let [pattern-transform (dom/get-attribute node "patternTransform")]
(when (some? pattern-transform)
(dom/set-attribute! node "data-old-patternTransform" pattern-transform))))
(when (or (= (dom/get-tag-name node) "mask")
(= (dom/get-tag-name node) "filter"))
(let [old-x (dom/get-attribute node "x")
old-y (dom/get-attribute node "y")
old-width (dom/get-attribute node "width")
old-height (dom/get-attribute node "height")]
(dom/set-attribute! node "data-old-x" old-x)
(dom/set-attribute! node "data-old-y" old-y)
(dom/set-attribute! node "data-old-width" old-width)
(dom/set-attribute! node "data-old-height" old-height))))))))
(defn set-transform-att!
[node att value]
(let [old-att (dom/get-attribute node (dm/str "data-old-" att))
new-value (if (some? old-att)
(dm/str value " " old-att)
(str value))]
(dom/set-attribute! node att (str new-value))))
(defn update-transform!
[base-node shapes transforms modifiers]
(doseq [{:keys [id] :as shape} shapes]
(when-let [nodes (get-nodes base-node shape)]
(let [transform (get transforms id)
modifiers (get-in modifiers [id :modifiers])]
(doseq [node nodes]
(cond
;; Text shapes need special treatment because their resize only change
;; the text area, not the change size/position
(or (dom/class? node "text-shape")
(dom/class? node "frame-thumbnail"))
(let [[transform] (transform-no-resize shape transform modifiers)]
(set-transform-att! node "transform" transform))
(dom/class? node "frame-children")
(set-transform-att! node "transform" (gmt/inverse transform))
(or (= (dom/get-tag-name node) "mask")
(= (dom/get-tag-name node) "filter"))
(transform-region! node modifiers)
(or (= (dom/get-tag-name node) "linearGradient")
(= (dom/get-tag-name node) "radialGradient"))
(set-transform-att! node "gradientTransform" transform)
(= (dom/get-tag-name node) "pattern")
(set-transform-att! node "patternTransform" transform)
(and (some? transform) (some? node))
(set-transform-att! node "transform" transform)))))))
(defn remove-transform!
[base-node shapes]
(doseq [shape shapes]
(when-let [nodes (get-nodes base-node shape)]
(doseq [node nodes]
(when (some? node)
(cond
(= (dom/get-tag-name node) "foreignObject")
;; The shape width/height will be automaticaly setup when the modifiers are applied
nil
(or (= (dom/get-tag-name node) "mask")
(= (dom/get-tag-name node) "filter"))
(do
(dom/remove-attribute! node "data-old-x")
(dom/remove-attribute! node "data-old-y")
(dom/remove-attribute! node "data-old-width")
(dom/remove-attribute! node "data-old-height"))
:else
(let [old-transform (dom/get-attribute node "data-old-transform")]
(if (some? old-transform)
(dom/remove-attribute! node "data-old-transform")
(dom/remove-attribute! node "transform")))))))))
(defn use-dynamic-modifiers
[objects node modifiers]
@ -42,13 +255,13 @@
is-cur-val? (d/not-empty? modifiers)]
(when (and (not is-prev-val?) is-cur-val?)
(utils/start-transform! node shapes))
(start-transform! node shapes))
(when is-cur-val?
(utils/update-transform! node shapes transforms modifiers))
(update-transform! node shapes transforms modifiers))
(when (and is-prev-val? (not is-cur-val?))
(utils/remove-transform! node @prev-shapes))
(remove-transform! node @prev-shapes))
(reset! prev-modifiers modifiers)
(reset! prev-transforms transforms)

View file

@ -183,7 +183,7 @@
(hooks/setup-hover-shapes page-id move-stream base-objects transform selected mod? hover hover-ids @hover-disabled? focus zoom)
(hooks/setup-viewport-modifiers modifiers base-objects)
(hooks/setup-shortcuts node-editing? drawing-path?)
(hooks/setup-active-frames base-objects hover-ids selected active-frames zoom)
(hooks/setup-active-frames base-objects hover-ids selected active-frames zoom transform)
[:div.viewport
[:div.viewport-overlays {:ref overlays-ref}

View file

@ -219,20 +219,29 @@
(gsh/overlaps? frame vbox))))
(defn setup-active-frames
[objects hover-ids selected active-frames zoom]
[objects hover-ids selected active-frames zoom transform]
(mf/use-effect
(mf/deps objects @hover-ids selected zoom)
(mf/deps objects @hover-ids selected zoom transform)
(fn []
(when (some? @hover-ids)
(let [hover-frame (when (> zoom 0.25) (last @hover-ids))
new-active-frames (if (some? hover-frame) #{hover-frame} #{})
frame? #(= :frame (get-in objects [% :type]))
selected-frames (->> selected (filter frame?))
new-active-frames
(cond-> new-active-frames
(and (not= transform :move) (= (count selected-frames) 1))
(conj new-active-frames (first selected-frames)))
new-active-frames
(into new-active-frames
(comp
(filter #(not= :frame (get-in objects [% :type])))
(remove frame?)
(map #(get-in objects [% :frame-id])))
selected) ]
selected)]
(reset! active-frames new-active-frames))))))
;; NOTE: this is executed on each page change, maybe we need to move

View file

@ -8,224 +8,10 @@
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.geom.matrix :as gmt]
[app.common.geom.point :as gpt]
[app.common.geom.shapes :as gsh]
[app.main.ui.cursors :as cur]
[app.util.dom :as dom]))
(defn- text-corrected-transform
"If we apply a scale directly to the texts it will show deformed so we need to create this
correction matrix to \"undo\" the resize but keep the other transformations."
[{:keys [x y width height points transform transform-inverse] :as shape} current-transform modifiers]
(let [corner-pt (first points)
corner-pt (cond-> corner-pt (some? transform-inverse) (gpt/transform transform-inverse))
resize-x? (some? (:resize-vector modifiers))
resize-y? (some? (:resize-vector-2 modifiers))
flip-x? (neg? (get-in modifiers [:resize-vector :x]))
flip-y? (or (neg? (get-in modifiers [:resize-vector :y]))
(neg? (get-in modifiers [:resize-vector-2 :y])))
result (cond-> (gmt/matrix)
(and (some? transform) (or resize-x? resize-y?))
(gmt/multiply transform)
resize-x?
(gmt/scale (gpt/inverse (:resize-vector modifiers)) corner-pt)
resize-y?
(gmt/scale (gpt/inverse (:resize-vector-2 modifiers)) corner-pt)
flip-x?
(gmt/scale (gpt/point -1 1) corner-pt)
flip-y?
(gmt/scale (gpt/point 1 -1) corner-pt)
(and (some? transform) (or resize-x? resize-y?))
(gmt/multiply transform-inverse))
[width height]
(if (or resize-x? resize-y?)
(let [pc (cond-> (gpt/point x y)
(some? transform)
(gpt/transform transform)
(some? current-transform)
(gpt/transform current-transform))
pw (cond-> (gpt/point (+ x width) y)
(some? transform)
(gpt/transform transform)
(some? current-transform)
(gpt/transform current-transform))
ph (cond-> (gpt/point x (+ y height))
(some? transform)
(gpt/transform transform)
(some? current-transform)
(gpt/transform current-transform))]
[(gpt/distance pc pw) (gpt/distance pc ph)])
[width height])]
[result width height]))
(defn get-nodes
"Retrieve the DOM nodes to apply the matrix transformation"
[base-node {:keys [id type masked-group?]}]
(let [shape-node (dom/query base-node (str "#shape-" id))
frame? (= :frame type)
group? (= :group type)
text? (= :text type)
mask? (and group? masked-group?)
;; When the shape is a frame we maybe need to move its thumbnail
thumb-node (when frame? (dom/query (str "#thumbnail-container-" id)))]
(cond
frame?
[thumb-node
(dom/get-parent (dom/query shape-node ".frame-background"))
(dom/query shape-node ".frame-clip")]
;; For groups we don't want to transform the whole group but only
;; its filters/masks
mask?
[(dom/query shape-node ".mask-clip-path")
(dom/query shape-node ".mask-shape")]
group?
(let [shape-defs (dom/query shape-node "defs")]
(d/concat-vec
(dom/query-all shape-defs ".svg-def")
(dom/query-all shape-defs ".svg-mask-wrapper")))
text?
[shape-node
(dom/query shape-node ".text-shape")]
:else
[shape-node])))
(defn transform-region!
[node modifiers]
(let [{:keys [x y width height]}
(-> (gsh/make-selrect
(-> (dom/get-attribute node "data-old-x") d/parse-double)
(-> (dom/get-attribute node "data-old-y") d/parse-double)
(-> (dom/get-attribute node "data-old-width") d/parse-double)
(-> (dom/get-attribute node "data-old-height") d/parse-double))
(gsh/transform-selrect modifiers))]
(dom/set-attribute! node "x" x)
(dom/set-attribute! node "y" y)
(dom/set-attribute! node "width" width)
(dom/set-attribute! node "height" height)))
(defn start-transform!
[base-node shapes]
(doseq [shape shapes]
(when-let [nodes (get-nodes base-node shape)]
(doseq [node nodes]
(let [old-transform (dom/get-attribute node "transform")]
(when (some? old-transform)
(dom/set-attribute! node "data-old-transform" old-transform))
(when (or (= (dom/get-tag-name node) "linearGradient")
(= (dom/get-tag-name node) "radialGradient"))
(let [gradient-transform (dom/get-attribute node "gradientTransform")]
(when (some? gradient-transform)
(dom/set-attribute! node "data-old-gradientTransform" gradient-transform))))
(when (= (dom/get-tag-name node) "pattern")
(let [pattern-transform (dom/get-attribute node "patternTransform")]
(when (some? pattern-transform)
(dom/set-attribute! node "data-old-patternTransform" pattern-transform))))
(when (or (= (dom/get-tag-name node) "mask")
(= (dom/get-tag-name node) "filter"))
(let [old-x (dom/get-attribute node "x")
old-y (dom/get-attribute node "y")
old-width (dom/get-attribute node "width")
old-height (dom/get-attribute node "height")]
(dom/set-attribute! node "data-old-x" old-x)
(dom/set-attribute! node "data-old-y" old-y)
(dom/set-attribute! node "data-old-width" old-width)
(dom/set-attribute! node "data-old-height" old-height))))))))
(defn set-transform-att!
[node att value]
(let [old-att (dom/get-attribute node (dm/str "data-old-" att))
new-value (if (some? old-att)
(dm/str value " " old-att)
(str value))]
(dom/set-attribute! node att (str new-value))))
(defn update-transform!
[base-node shapes transforms modifiers]
(doseq [{:keys [id type] :as shape} shapes]
(when-let [nodes (get-nodes base-node shape)]
(let [transform (get transforms id)
modifiers (get-in modifiers [id :modifiers])
[text-transform _text-width _text-height]
(when (= :text type)
(text-corrected-transform shape transform modifiers))]
(doseq [node nodes]
(cond
;; Text shapes need special treatment because their resize only change
;; the text area, not the change size/position
(dom/class? node "text-shape")
(when (some? text-transform)
(set-transform-att! node "transform" text-transform))
(or (= (dom/get-tag-name node) "mask")
(= (dom/get-tag-name node) "filter"))
(transform-region! node modifiers)
(or (= (dom/get-tag-name node) "linearGradient")
(= (dom/get-tag-name node) "radialGradient"))
(set-transform-att! node "gradientTransform" transform)
(= (dom/get-tag-name node) "pattern")
(set-transform-att! node "patternTransform" transform)
(and (some? transform) (some? node))
(set-transform-att! node "transform" transform)))))))
(defn remove-transform!
[base-node shapes]
(doseq [shape shapes]
(when-let [nodes (get-nodes base-node shape)]
(doseq [node nodes]
(when (some? node)
(cond
(= (dom/get-tag-name node) "foreignObject")
;; The shape width/height will be automaticaly setup when the modifiers are applied
nil
(or (= (dom/get-tag-name node) "mask")
(= (dom/get-tag-name node) "filter"))
(do
(dom/remove-attribute! node "data-old-x")
(dom/remove-attribute! node "data-old-y")
(dom/remove-attribute! node "data-old-width")
(dom/remove-attribute! node "data-old-height"))
:else
(let [old-transform (dom/get-attribute node "data-old-transform")]
(if (some? old-transform)
(dom/remove-attribute! node "data-old-transform")
(dom/remove-attribute! node "transform")))))))))
(defn format-viewbox [vbox]
(dm/str (:x vbox 0) " "
(:y vbox 0) " "