0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-01-25 07:58:49 -05:00

Fix editor and bounds for new texts

This commit is contained in:
alonso.torres 2022-02-15 18:22:13 +01:00
parent 1c2785f34e
commit 18dded1a00
5 changed files with 165 additions and 61 deletions

View file

@ -190,3 +190,20 @@
(multiply mtx)
(translate (gpt/negate pt)))
mtx))
(defn determinant
"Determinant for the affinity transform"
[{:keys [a b c d _ _]}]
(- (* a d) (* c b)))
(defn inverse
"Gets the inverse of the affinity transform `mtx`"
[{:keys [a b c d e f] :as mtx}]
(let [det (determinant mtx)
a' (/ d det)
b' (/ (- b) det)
c' (/ (- c) det)
d' (/ a det)
e' (/ (- (* c f) (* d e)) det)
f' (/ (- (* b e) (* a f)) det)]
(Matrix. a' b' c' d' e' f')))

View file

@ -84,16 +84,18 @@
base #js {:textDecoration text-decoration
:textTransform text-transform
:lineHeight (or line-height "inherit")
:color text-color}]
:color text-color
:caretColor "black"}]
(when-let [gradient (:fill-color-gradient data)]
(let [text-color (-> (update gradient :type keyword)
(uc/gradient->css))]
(-> base
(obj/set! "--text-color" text-color)
(obj/set! "backgroundImage" "var(--text-color)")
(obj/set! "WebkitTextFillColor" "transparent")
(obj/set! "WebkitBackgroundClip" "text"))))
(obj/set! "color" text-color)
#_(obj/set! "--text-color" text-color)
#_(obj/set! "backgroundImage" "var(--text-color)")
#_(obj/set! "WebkitTextFillColor" "transparent")
#_(obj/set! "WebkitBackgroundClip" "text"))))
(when (and (string? letter-spacing)
(pos? (alength letter-spacing)))

View file

@ -23,9 +23,11 @@
[props]
(let [render-id (mf/use-ctx muc/render-ctx)
{:keys [position-data] :as shape} (obj/get props "shape")
group-props (-> #js {:transform (gsh/transform-matrix shape)}
(attrs/add-style-attrs shape render-id))
{:keys [id x y width height position-data] :as shape} (obj/get props "shape")
clip-id (str "clip-text" id "_" render-id)
group-props (-> #js {:transform (gsh/transform-matrix shape)
:clipPath (str "url(#" clip-id ")")}
(attrs/add-style-attrs shape render-id))
get-gradient-id
(fn [index]
(str render-id "_" (:id shape) "_" index))]
@ -41,10 +43,16 @@
[:& shape-custom-stroke {:shape shape}
[:> :g group-props
[:defs
[:clipPath {:id clip-id}
[:rect.text-clip
{:x x :y y
:width width :height height
:transform (gsh/transform-matrix shape)}]]]
(for [[index data] (d/enumerate position-data)]
(let [props (-> #js {:x (:x data)
:y (:y data)
:dominant-baseline "ideographic"
:dominantBaseline "ideographic"
:style (-> #js {:fontFamily (:font-family data)
:fontSize (:font-size data)
:fontWeight (:font-weight data)

View file

@ -6,7 +6,10 @@
(ns app.main.ui.workspace.shapes.text
(:require
[app.common.attrs :as attrs]
[app.common.data :as d]
[app.common.geom.matrix :as gmt]
[app.common.geom.shapes :as gsh]
[app.common.logging :as log]
[app.common.math :as mth]
[app.common.transit :as transit]
@ -19,6 +22,7 @@
[app.main.ui.shapes.text.svg-text :as svg]
[app.util.dom :as dom]
[app.util.object :as obj]
[app.util.svg :as usvg]
[app.util.text-editor :as ted]
[app.util.text-svg-position :as utp]
[app.util.timers :as timers]
@ -40,12 +44,18 @@
(defn- update-with-current-editor-state
[{:keys [id] :as shape}]
(let [editor-state-ref (mf/use-memo (mf/deps id) #(l/derived (l/key id) refs/workspace-editor-state))
editor-state (mf/deref editor-state-ref)]
editor-state (mf/deref editor-state-ref)
content (:content shape)
editor-content
(when editor-state
(-> editor-state
(ted/get-editor-current-content)
(ted/export-content)))]
(cond-> shape
(some? editor-state)
(assoc :content (-> editor-state
(ted/get-editor-current-content)
(ted/export-content))))))
(some? editor-content)
(assoc :content (attrs/merge content editor-content)))))
(mf/defc text-resize-content
{::mf/wrap-props false}
@ -106,69 +116,126 @@
[:& fo/text-shape {:ref text-ref-cb :shape shape :grow-type (:grow-type shape)}]))
(defn calc-position-data
[base-node]
(let [viewport (dom/get-element "render")
zoom (get-in @st/state [:workspace-local :zoom])
text-data (utp/calc-text-node-positions base-node viewport zoom)]
(->> text-data
(map (fn [{:keys [node position text]}]
(let [{:keys [x y width height]} position
rtl? (= "rtl" (.-dir (.-parentElement ^js node)))
styles (.computedStyleMap ^js node)]
(d/without-nils
{:rtl? rtl?
:x (if rtl? (+ x width) x)
:y (+ y height)
:width width
:height height
:font-family (str (.get styles "font-family"))
:font-size (str (.get styles "font-size"))
:font-weight (str (.get styles "font-weight"))
:text-transform (str (.get styles "text-transform"))
:text-decoration (str (.get styles "text-decoration"))
:font-style (str (.get styles "font-style"))
:fill-color (or (dom/get-attribute node "data-fill-color") "#000000")
:fill-color-gradient (transit/decode-str (dom/get-attribute node "data-fill-color-gradient"))
:fill-opacity (d/parse-double (or (:fill-opacity node) "1"))
:text text})))))))
(mf/defc text-wrapper
{::mf/wrap-props false}
[props]
(let [{:keys [id dirty?] :as shape} (unchecked-get props "shape")
edition-ref (mf/use-memo (mf/deps id) #(l/derived (fn [o] (= id (:edition o))) refs/workspace-local))
edition? (mf/deref edition-ref)
shape-ref (mf/use-ref nil)]
shape-ref (mf/use-ref nil)
prev-obs-ref (mf/use-ref nil)
local-position-data (mf/use-state nil)
handle-change-foreign-object
(fn []
(when-let [node (mf/ref-val shape-ref)]
(let [position-data (calc-position-data node)
parent (dom/get-parent node)
parent-transform (dom/get-attribute parent "transform")
node-transform (dom/get-attribute node "transform")
parent-mtx (usvg/parse-transform parent-transform)
node-mtx (usvg/parse-transform node-transform)
;; We need to see what transformation is applied in the DOM to reverse it
;; before calculating the position data
mtx (-> (gmt/multiply parent-mtx node-mtx)
(gmt/inverse))
position-data'
(->> position-data
(mapv #(merge % (-> (select-keys % [:x :y :width :height])
(gsh/transform-rect mtx)))))]
(reset! local-position-data position-data'))))
on-change-node
(fn [^js node]
(mf/set-ref-val! shape-ref node)
(when-let [^js prev-obs (mf/ref-val prev-obs-ref)]
(.disconnect prev-obs)
(mf/set-ref-val! prev-obs-ref nil))
(when (some? node)
(let [fo-node (dom/query node "foreignObject")
options #js {:attributes true
:childList true
:subtree true}
mutation-obs (js/MutationObserver. handle-change-foreign-object)]
(mf/set-ref-val! prev-obs-ref mutation-obs)
(.observe mutation-obs fo-node options))))]
(mf/use-effect
(fn []
(fn []
(when-let [^js prev-obs (mf/ref-val prev-obs-ref)]
(.disconnect prev-obs)
(mf/set-ref-val! prev-obs-ref nil)))))
(mf/use-layout-effect
(mf/deps dirty?)
(mf/deps id dirty?)
(fn []
(when (and (or dirty? (not (:position-data shape))) (some? id))
(let [base-node (mf/ref-val shape-ref)
viewport (dom/get-element "render")
zoom (get-in @st/state [:workspace-local :zoom])
text-data (utp/calc-text-node-positions base-node viewport zoom)
position-data
(->> text-data
(map (fn [{:keys [node position text]}]
(let [{:keys [x y width height]} position
rtl? (= "rtl" (.-dir (.-parentElement ^js node)))
styles (.computedStyleMap ^js node)]
(d/without-nils
{:rtl? rtl?
:x (if rtl? (+ x width) x)
:y (+ y height)
:width width
:height height
:font-family (str (.get styles "font-family"))
:font-size (str (.get styles "font-size"))
:font-weight (str (.get styles "font-weight"))
:text-transform (str (.get styles "text-transform"))
:text-decoration (str (.get styles "text-decoration"))
:font-style (str (.get styles "font-style"))
:fill-color (or (dom/get-attribute node "data-fill-color") "#000000")
:fill-color-gradient (transit/decode-str (dom/get-attribute node "data-fill-color-gradient"))
:fill-opacity (d/parse-double (or (:fill-opacity node) "1"))
:text text})))))]
(st/emit! (dch/update-shapes
[id]
(fn [shape]
(-> shape
(dissoc :dirty?)
(assoc :position-data position-data)))))))))
(let [node (mf/ref-val shape-ref)
position-data (calc-position-data node)]
(reset! local-position-data nil)
(st/emit! (dch/update-shapes
[id]
(fn [shape]
(-> shape
(dissoc :dirty?)
(assoc :position-data position-data))))))))
[:> shape-container {:shape shape}
;; We keep hidden the shape when we're editing so it keeps track of the size
;; and updates the selrect accordingly
[:*
[:g.text-shape {:ref shape-ref
[:g.text-shape {:ref on-change-node
:opacity (when (or edition? (some? (:position-data shape))) 0)
:pointer-events "none"}
;; The `:key` prop here is mandatory because the
;; text-resize-content breaks a hooks rule and we can't reuse
;; the component if the edition flag changes.
[:& text-resize-content {:shape (cond-> shape
(:position-data shape)
(dissoc :transform :transform-inverse))
[:& text-resize-content {:shape
(cond-> shape
(:position-data shape)
(dissoc :transform :transform-inverse))
:edition? edition?
:key (str id edition?)}]]
[:g.text-svg {:opacity (when edition? 0)
:pointer-events "none"}
(when (some? (:position-data shape))
[:& svg/text-shape {:shape shape}])]]]))
[:& svg/text-shape {:shape (cond-> shape
(some? @local-position-data)
(assoc :position-data @local-position-data))}])]]]))

View file

@ -105,7 +105,9 @@
text?
[shape-node
(dom/query shape-node "foreignObject")
(dom/query shape-node ".text-shape")]
(dom/query shape-node ".text-shape")
(dom/query shape-node ".text-svg")
(dom/query shape-node ".text-clip")]
:else
[shape-node])))
@ -118,18 +120,26 @@
[text-transform text-width text-height]
(when (= :text type)
(text-corrected-transform shape transform modifiers))]
(text-corrected-transform shape transform modifiers))
text-width (str text-width)
text-height (str text-height)]
(doseq [node nodes]
(cond
(dom/class? node "text-shape")
(or (dom/class? node "text-shape") (dom/class? node "text-svg"))
(when (some? text-transform)
(dom/set-attribute node "transform" (str text-transform)))
(= (dom/get-tag-name node) "foreignObject")
(when (and (some? text-width) (some? text-height))
(dom/set-attribute node "width" text-width)
(dom/set-attribute node "height" text-height))
(or (= (dom/get-tag-name node) "foreignObject")
(dom/class? node "text-clip"))
(let [cur-width (dom/get-attribute node "width")
cur-height (dom/get-attribute node "height")]
(when (and (some? text-width) (not= cur-width text-width))
(dom/set-attribute node "width" text-width))
(when (and (some? text-height) (not= cur-height text-height))
(dom/set-attribute node "height" text-height)))
(and (some? transform) (some? node))
(dom/set-attribute node "transform" (str transform))))))))