Merge pull request #1892 from penpot/alotor-bugfixing-2

Change text disposition on resize
This commit is contained in:
Eva Marco 2022-05-12 16:52:27 +02:00 committed by GitHub
commit af519b3f89
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 172 additions and 85 deletions

View file

@ -219,3 +219,13 @@
e' (/ (- (* c f) (* d e)) det) e' (/ (- (* c f) (* d e)) det)
f' (/ (- (* b e) (* a f)) det)] f' (/ (- (* b e) (* a f)) det)]
(Matrix. a' b' c' d' e' f'))) (Matrix. a' b' c' d' e' f')))
(defn round
[mtx]
(-> mtx
(update :a mth/precision 4)
(update :b mth/precision 4)
(update :c mth/precision 4)
(update :d mth/precision 4)
(update :e mth/precision 4)
(update :f mth/precision 4)))

View file

@ -152,6 +152,28 @@
:top :top
:scale))) :scale)))
(defn clean-modifiers
"Remove redundant modifiers"
[{:keys [displacement resize-vector resize-vector-2] :as modifiers}]
(cond-> modifiers
;; Displacement with value 0. We don't move in any direction
(and (some? displacement)
(mth/almost-zero? (:e displacement))
(mth/almost-zero? (:f displacement)))
(dissoc :displacement)
;; Resize with value very close to 1 means no resize
(and (some? resize-vector)
(mth/almost-zero? (- 1.0 (:x resize-vector)))
(mth/almost-zero? (- 1.0 (:y resize-vector))))
(dissoc :resize-origin :resize-vector)
(and (some? resize-vector)
(mth/almost-zero? (- 1.0 (:x resize-vector-2)))
(mth/almost-zero? (- 1.0 (:y resize-vector-2))))
(dissoc :resize-origin-2 :resize-vector-2)))
(defn calc-child-modifiers (defn calc-child-modifiers
[parent child modifiers ignore-constraints transformed-parent-rect] [parent child modifiers ignore-constraints transformed-parent-rect]
(let [constraints-h (let [constraints-h
@ -192,5 +214,7 @@
(:resize-transform modifiers) (:resize-transform modifiers)
(assoc :resize-transform (:resize-transform modifiers) (assoc :resize-transform (:resize-transform modifiers)
:resize-transform-inverse (:resize-transform-inverse modifiers))))) :resize-transform-inverse (:resize-transform-inverse modifiers))
:always
(clean-modifiers))))

View file

@ -8,7 +8,9 @@
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.pages.helpers :as cph])) [app.common.geom.shapes :as gsh]
[app.common.pages.helpers :as cph]
[app.common.path.commands :as upc]))
(defn lookup-page (defn lookup-page
([state] ([state]
@ -50,6 +52,10 @@
(filter selectable?) (filter selectable?)
selected))))) selected)))))
(defn lookup-selected-raw
[state]
(dm/get-in state [:workspace-local :selected]))
(defn lookup-selected (defn lookup-selected
([state] ([state]
(lookup-selected state nil)) (lookup-selected state nil))
@ -94,3 +100,26 @@
(-> (:workspace-libraries state) (-> (:workspace-libraries state)
(assoc id {:id id (assoc id {:id id
:data local})))) :data local}))))
(defn- set-content-modifiers [state]
(fn [id shape]
(let [content-modifiers (dm/get-in state [:workspace-local :edit-path id :content-modifiers])]
(if (some? content-modifiers)
(update shape :content upc/apply-content-modifiers content-modifiers)
shape))))
(defn select-bool-children
[parent-id state]
(let [objects (lookup-page-objects state)
selected (lookup-selected-raw state)
modifiers (:workspace-modifiers state)
children-ids (cph/get-children-ids objects parent-id)
selected-children (into [] (filter selected) children-ids)
modifiers (select-keys modifiers selected-children)
children (select-keys objects children-ids)]
(as-> children $
(gsh/merge-modifiers $ modifiers)
(d/mapm (set-content-modifiers state) $))))

View file

@ -9,9 +9,7 @@
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.geom.shapes :as gsh]
[app.common.pages.helpers :as cph] [app.common.pages.helpers :as cph]
[app.common.path.commands :as upc]
[app.main.data.workspace.state-helpers :as wsh] [app.main.data.workspace.state-helpers :as wsh]
[app.main.store :as st] [app.main.store :as st]
[okulary.core :as l])) [okulary.core :as l]))
@ -193,28 +191,29 @@
(assoc :pages (:pages data))))) (assoc :pages (:pages data)))))
st/state =)) st/state =))
(def workspace-data
(l/derived :workspace-data st/state))
(def workspace-file-colors (def workspace-file-colors
(l/derived (fn [state] (l/derived (fn [data]
(when-let [file (:workspace-data state)] (when data
(->> (:colors file) (->> (:colors data)
(d/mapm #(assoc %2 :file-id (:id file)))))) (d/mapm #(assoc %2 :file-id (:id data))))))
st/state)) workspace-data
=))
(def workspace-recent-colors (def workspace-recent-colors
(l/derived (fn [state] (l/derived (fn [data]
(dm/get-in state [:workspace-data :recent-colors] [])) (get data :recent-colors []))
st/state)) workspace-data))
(def workspace-recent-fonts (def workspace-recent-fonts
(l/derived (fn [state] (l/derived (fn [data]
(dm/get-in state [:workspace-data :recent-fonts] [])) (get data :workspace-data []))
st/state)) workspace-data))
(def workspace-file-typography (def workspace-file-typography
(l/derived (fn [state] (l/derived :typographies workspace-data))
(when-let [file (:workspace-data state)]
(:typographies file)))
st/state))
(def workspace-project (def workspace-project
(l/derived :workspace-project st/state)) (l/derived :workspace-project st/state))
@ -313,24 +312,8 @@
workspace-modifiers-with-objects workspace-modifiers-with-objects
=)) =))
(defn- set-content-modifiers [state]
(fn [id shape]
(let [content-modifiers (dm/get-in state [:workspace-local :edit-path id :content-modifiers])]
(if (some? content-modifiers)
(update shape :content upc/apply-content-modifiers content-modifiers)
shape))))
(defn select-bool-children [id] (defn select-bool-children [id]
(let [selector (l/derived (partial wsh/select-bool-children id) st/state =))
(fn [state]
(let [objects (wsh/lookup-page-objects state)
modifiers (:workspace-modifiers state)
children (->> (cph/get-children-ids objects id)
(select-keys objects))]
(as-> children $
(gsh/merge-modifiers $ modifiers)
(d/mapm (set-content-modifiers state) $))))]
(l/derived selector st/state =)))
(def selected-data (def selected-data
(l/derived #(let [selected (wsh/lookup-selected %) (l/derived #(let [selected (wsh/lookup-selected %)

View file

@ -7,6 +7,7 @@
(ns app.main.ui.shapes.filters (ns app.main.ui.shapes.filters
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.geom.shapes :as gsh] [app.common.geom.shapes :as gsh]
[app.common.math :as mth] [app.common.math :as mth]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
@ -249,6 +250,7 @@
:height filter-height :height filter-height
:filterUnits "objectBoundingBox" :filterUnits "objectBoundingBox"
:color-interpolation-filters "sRGB"} :color-interpolation-filters "sRGB"}
(for [entry filters] (for [[index entry] (d/enumerate filters)]
[:& filter-entry {:entry entry}])]))) [:& filter-entry {:key (dm/str filter-id "-" index)
:entry entry}])])))

View file

@ -41,7 +41,7 @@
(defn propagate-wrapper-styles (defn propagate-wrapper-styles
([children wrapper-props] ([children wrapper-props]
(if (.isArray js/Array children) (if (.isArray js/Array children)
(->> children (map propagate-wrapper-styles-child)) (->> children (map #(propagate-wrapper-styles-child % wrapper-props)))
(-> children (propagate-wrapper-styles-child wrapper-props))))) (-> children (propagate-wrapper-styles-child wrapper-props)))))
(mf/defc shape-container (mf/defc shape-container

View file

@ -50,7 +50,7 @@
text-align (:text-align data "start") text-align (:text-align data "start")
base #js {:fontSize (str (:font-size data (:font-size txt/default-text-attrs)) "px") base #js {:fontSize (str (:font-size data (:font-size txt/default-text-attrs)) "px")
:lineHeight (:line-height data (:line-height txt/default-text-attrs)) :lineHeight (:line-height data (:line-height txt/default-text-attrs))
:margin "inherit"}] :margin 0}]
(cond-> base (cond-> base
(some? line-height) (obj/set! "lineHeight" line-height) (some? line-height) (obj/set! "lineHeight" line-height)
(some? text-align) (obj/set! "textAlign" text-align)))) (some? text-align) (obj/set! "textAlign" text-align))))

View file

@ -64,6 +64,7 @@
;; These position attributes are not really necesary but they are convenient for for the export ;; These position attributes are not really necesary but they are convenient for for the export
group-props (-> #js {:transform transform group-props (-> #js {:transform transform
:className "text-container"
:x x :x x
:y y :y y
:width width :width width
@ -78,10 +79,11 @@
[:defs [:defs
(for [[index data] (d/enumerate position-data)] (for [[index data] (d/enumerate position-data)]
(when (some? (:fill-color-gradient data)) (when (some? (:fill-color-gradient data))
[:& grad/gradient {:id (str "fill-color-gradient_" (get-gradient-id index)) (let [id (dm/str "fill-color-gradient_" (get-gradient-id index))]
:key index [:& grad/gradient {:id id
:attr :fill-color-gradient :key id
:shape data}]))]) :attr :fill-color-gradient
:shape data}])))])
[:> :g group-props [:> :g group-props
(for [[index data] (d/enumerate position-data)] (for [[index data] (d/enumerate position-data)]
@ -91,7 +93,7 @@
alignment-bl (when (cfg/check-browser? :safari) "text-before-edge") alignment-bl (when (cfg/check-browser? :safari) "text-before-edge")
dominant-bl (when-not (cfg/check-browser? :safari) "ideographic") dominant-bl (when-not (cfg/check-browser? :safari) "ideographic")
rtl? (= "rtl"(:direction data)) rtl? (= "rtl" (:direction data))
props (-> #js {:key (dm/str "text-" (:id shape) "-" index) props (-> #js {:key (dm/str "text-" (:id shape) "-" index)
:x (if rtl? (+ (:x data) (:width data)) (:x data)) :x (if rtl? (+ (:x data) (:width data)) (:x data))
:y y :y y
@ -110,7 +112,7 @@
(obj/set! "fill" (str "url(#fill-" index "-" render-id ")")))}) (obj/set! "fill" (str "url(#fill-" index "-" render-id ")")))})
shape (assoc shape :fills (:fills data))] shape (assoc shape :fills (:fills data))]
[:& (mf/provider muc/render-ctx) {:value (str render-id "_" (:id shape) "_" index)} [:& (mf/provider muc/render-ctx) {:key index :value (str render-id "_" (:id shape) "_" index)}
[:& shape-custom-strokes {:shape shape :position index :render-id render-id} [:& shape-custom-strokes {:shape shape :position index :render-id render-id}
[:> :text props (:text data)]]]))]])) [:> :text props (:text data)]]]))]]))

View file

@ -81,8 +81,8 @@
(let [shape-node (dom/query base-node (str "#shape-" id)) (let [shape-node (dom/query base-node (str "#shape-" id))
frame? (= :frame type) frame? (= :frame type)
group? (= :group type)
text? (= :text type) text? (= :text type)
group? (= :group type)
mask? (and group? masked-group?)] mask? (and group? masked-group?)]
(cond (cond
@ -106,7 +106,7 @@
text? text?
[shape-node [shape-node
(dom/query shape-node ".text-shape")] (dom/query shape-node ".text-container")]
:else :else
[shape-node]))) [shape-node])))
@ -166,6 +166,10 @@
(str value))] (str value))]
(dom/set-attribute! node att (str new-value)))) (dom/set-attribute! node att (str new-value))))
(defn override-transform-att!
[node att value]
(dom/set-attribute! node att (str value)))
(defn update-transform! (defn update-transform!
[base-node shapes transforms modifiers] [base-node shapes transforms modifiers]
(doseq [{:keys [id] :as shape} shapes] (doseq [{:keys [id] :as shape} shapes]
@ -177,14 +181,24 @@
(cond (cond
;; Text shapes need special treatment because their resize only change ;; Text shapes need special treatment because their resize only change
;; the text area, not the change size/position ;; the text area, not the change size/position
(or (dom/class? node "text-shape") (dom/class? node "frame-thumbnail")
(dom/class? node "frame-thumbnail"))
(let [[transform] (transform-no-resize shape transform modifiers)] (let [[transform] (transform-no-resize shape transform modifiers)]
(set-transform-att! node "transform" transform)) (set-transform-att! node "transform" transform))
(dom/class? node "frame-children") (dom/class? node "frame-children")
(set-transform-att! node "transform" (gmt/inverse transform)) (set-transform-att! node "transform" (gmt/inverse transform))
;; We need to update the shape transform matrix when there is a resize
;; we do it dinamicaly here
(dom/class? node "text-container")
(let [modifiers (dissoc modifiers :displacement :rotation)]
(when (not (gsh/empty-modifiers? modifiers))
(let [mtx (-> shape
(assoc :modifiers modifiers)
(gsh/transform-shape)
(gsh/transform-matrix {:no-flip true}))]
(override-transform-att! node "transform" mtx))))
(or (= (dom/get-tag-name node) "mask") (or (= (dom/get-tag-name node) "mask")
(= (dom/get-tag-name node) "filter")) (= (dom/get-tag-name node) "filter"))
(transform-region! node modifiers) (transform-region! node modifiers)
@ -233,7 +247,13 @@
(fn [] (fn []
(when (some? modifiers) (when (some? modifiers)
(d/mapm (fn [id {modifiers :modifiers}] (d/mapm (fn [id {modifiers :modifiers}]
(let [center (gsh/center-shape (get objects id))] (let [shape (get objects id)
center (gsh/center-shape shape)
modifiers (cond-> modifiers
;; For texts we only use the displacement because
;; resize needs to recalculate the text layout
(= :text (:type shape))
(select-keys [:displacement :rotation]))]
(gsh/modifiers->transform center modifiers))) (gsh/modifiers->transform center modifiers)))
modifiers)))) modifiers))))

View file

@ -7,6 +7,7 @@
(ns app.main.ui.workspace.shapes.text (ns app.main.ui.workspace.shapes.text
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.math :as mth] [app.common.math :as mth]
[app.main.data.workspace.texts :as dwt] [app.main.data.workspace.texts :as dwt]
[app.main.refs :as refs] [app.main.refs :as refs]
@ -32,24 +33,23 @@
(dwt/apply-text-modifier text-modifier))] (dwt/apply-text-modifier text-modifier))]
[:> shape-container {:shape shape} [:> shape-container {:shape shape}
[:* [:g.text-shape {:key (dm/str "text-" (:id shape))}
[:g.text-shape [:& text/text-shape {:shape shape}]]
[:& text/text-shape {:shape shape}]]
(when (and (debug? :text-outline) (d/not-empty? (:position-data shape))) (when (and (debug? :text-outline) (d/not-empty? (:position-data shape)))
(for [data (:position-data shape)] (for [[index data] (d/enumerate (:position-data shape))]
(let [{:keys [x y width height]} data] (let [{:keys [x y width height]} data]
[:* [:g {:key (dm/str index)}
;; Text fragment bounding box ;; Text fragment bounding box
[:rect {:x x [:rect {:x x
:y (- y height) :y (- y height)
:width width :width width
:height height :height height
:style {:fill "none" :stroke "red"}}] :style {:fill "none" :stroke "red"}}]
;; Text baselineazo ;; Text baselineazo
[:line {:x1 (mth/round x) [:line {:x1 (mth/round x)
:y1 (mth/round (- (:y data) (:height data))) :y1 (mth/round (- (:y data) (:height data)))
:x2 (mth/round (+ x width)) :x2 (mth/round (+ x width))
:y2 (mth/round (- (:y data) (:height data))) :y2 (mth/round (- (:y data) (:height data)))
:style {:stroke "blue"}}]])))]])) :style {:stroke "blue"}}]])))]))

View file

@ -9,6 +9,7 @@
[app.common.attrs :as attrs] [app.common.attrs :as attrs]
[app.common.data :as d] [app.common.data :as d]
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.geom.shapes :as gsh]
[app.common.math :as mth] [app.common.math :as mth]
[app.common.pages.helpers :as cph] [app.common.pages.helpers :as cph]
[app.common.text :as txt] [app.common.text :as txt]
@ -28,6 +29,15 @@
(defn strip-position-data [shape] (defn strip-position-data [shape]
(dissoc shape :position-data :transform :transform-inverse)) (dissoc shape :position-data :transform :transform-inverse))
(defn process-shape [modifiers {:keys [id] :as shape}]
(let [modifier (get modifiers id)
modifier (d/update-when modifier :modifiers dissoc :displacement :rotation)
shape (cond-> shape
(not (gsh/empty-modifiers? modifier))
(-> (assoc :grow-type :fixed)
(merge modifier) gsh/transform-shape))]
(strip-position-data shape)))
(defn- update-with-editor-state (defn- update-with-editor-state
"Updates the shape with the current state in the editor" "Updates the shape with the current state in the editor"
[shape editor-state] [shape editor-state]
@ -88,7 +98,6 @@
(fn [node] (fn [node]
(when (some? node) (when (some? node)
(on-update shape node))))] (on-update shape node))))]
[:& fo/text-shape {:key (str "shape-" (:id shape)) [:& fo/text-shape {:key (str "shape-" (:id shape))
:ref handle-update :ref handle-update
:shape shape :shape shape
@ -120,7 +129,7 @@
[:* [:*
(for [{:keys [id] :as shape} changed-texts] (for [{:keys [id] :as shape} changed-texts]
[:& text-container {:shape shape [:& text-container {:shape (gsh/transform-shape shape)
:on-update handle-update-shape :on-update handle-update-shape
:key (str (dm/str "text-container-" id))}])])) :key (str (dm/str "text-container-" id))}])]))
@ -151,24 +160,30 @@
(defn check-props (defn check-props
[new-props old-props] [new-props old-props]
(and (identical? (unchecked-get new-props "objects") (unchecked-get old-props "objects")) (and (identical? (unchecked-get new-props "objects")
(= (unchecked-get new-props "edition") (unchecked-get old-props "edition")))) (unchecked-get old-props "objects"))
(identical? (unchecked-get new-props "modifiers")
(unchecked-get old-props "modifiers"))
(= (unchecked-get new-props "edition")
(unchecked-get old-props "edition"))))
(mf/defc viewport-texts (mf/defc viewport-texts
{::mf/wrap-props false {::mf/wrap-props false
::mf/wrap [#(mf/memo' % check-props)]} ::mf/wrap [#(mf/memo' % check-props)]}
[props] [props]
(let [objects (obj/get props "objects") (let [objects (obj/get props "objects")
edition (obj/get props "edition") edition (obj/get props "edition")
modifiers (obj/get props "modifiers")
xf-texts (comp (filter (comp cph/text-shape? second))
(map (fn [[id shape]]
[id (strip-position-data shape)])))
text-shapes text-shapes
(mf/use-memo (mf/use-memo
(mf/deps objects) (mf/deps objects)
#(into {} xf-texts objects)) #(into {} (filter (comp cph/text-shape? second)) objects))
text-shapes
(mf/use-memo
(mf/deps text-shapes modifiers)
#(d/update-vals text-shapes (partial process-shape modifiers)))
editing-shape (get text-shapes edition)] editing-shape (get text-shapes edition)]
@ -183,4 +198,6 @@
[:* [:*
(when editing-shape (when editing-shape
[:& viewport-text-editing {:shape editing-shape}]) [:& viewport-text-editing {:shape editing-shape}])
[:& viewport-texts-wrapper {:text-shapes (dissoc text-shapes edition)}]]))
[:& viewport-texts-wrapper {:text-shapes (dissoc text-shapes edition)
:modifiers modifiers}]]))

View file

@ -246,6 +246,7 @@
[:& stv/viewport-texts {:key (dm/str "texts-" page-id) [:& stv/viewport-texts {:key (dm/str "texts-" page-id)
:page-id page-id :page-id page-id
:objects base-objects :objects base-objects
:modifiers modifiers
:edition edition}]]] :edition edition}]]]
[:svg.viewport-controls [:svg.viewport-controls

View file

@ -166,7 +166,8 @@
(:name frame)]])) (:name frame)]]))
(mf/defc frame-titles (mf/defc frame-titles
{::mf/wrap-props false} {::mf/wrap-props false
::mf/wrap [mf/memo]}
[props] [props]
(let [objects (unchecked-get props "objects") (let [objects (unchecked-get props "objects")
zoom (unchecked-get props "zoom") zoom (unchecked-get props "zoom")

View file

@ -6,8 +6,6 @@
* LICENSE file in the root directory of this source tree. An additional grant * LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory. * of patent rights can be found in the PATENTS file in the same directory.
* *
* @providesModule normalizeWheel
* @typechecks
*/ */
/** /**