diff --git a/common/src/app/common/geom/shapes/constraints.cljc b/common/src/app/common/geom/shapes/constraints.cljc index 5c295acef..32064b22d 100644 --- a/common/src/app/common/geom/shapes/constraints.cljc +++ b/common/src/app/common/geom/shapes/constraints.cljc @@ -152,6 +152,28 @@ :top :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 [parent child modifiers ignore-constraints transformed-parent-rect] (let [constraints-h @@ -192,5 +214,7 @@ (: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)))) diff --git a/frontend/src/app/main/ui/shapes/text/styles.cljs b/frontend/src/app/main/ui/shapes/text/styles.cljs index cd2a21c5b..f6490e5fb 100644 --- a/frontend/src/app/main/ui/shapes/text/styles.cljs +++ b/frontend/src/app/main/ui/shapes/text/styles.cljs @@ -50,7 +50,7 @@ text-align (:text-align data "start") base #js {:fontSize (str (:font-size data (:font-size txt/default-text-attrs)) "px") :lineHeight (:line-height data (:line-height txt/default-text-attrs)) - :margin "inherit"}] + :margin 0}] (cond-> base (some? line-height) (obj/set! "lineHeight" line-height) (some? text-align) (obj/set! "textAlign" text-align)))) diff --git a/frontend/src/app/main/ui/shapes/text/svg_text.cljs b/frontend/src/app/main/ui/shapes/text/svg_text.cljs index d46025e4d..79d1127f1 100644 --- a/frontend/src/app/main/ui/shapes/text/svg_text.cljs +++ b/frontend/src/app/main/ui/shapes/text/svg_text.cljs @@ -78,10 +78,11 @@ [:defs (for [[index data] (d/enumerate position-data)] (when (some? (:fill-color-gradient data)) - [:& grad/gradient {:id (str "fill-color-gradient_" (get-gradient-id index)) - :key index - :attr :fill-color-gradient - :shape data}]))]) + (let [id (dm/str "fill-color-gradient_" (get-gradient-id index))] + [:& grad/gradient {:id id + :key id + :attr :fill-color-gradient + :shape data}])))]) [:> :g group-props (for [[index data] (d/enumerate position-data)] @@ -91,7 +92,7 @@ alignment-bl (when (cfg/check-browser? :safari) "text-before-edge") 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) :x (if rtl? (+ (:x data) (:width data)) (:x data)) :y y @@ -110,7 +111,7 @@ (obj/set! "fill" (str "url(#fill-" index "-" render-id ")")))}) 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} [:> :text props (:text data)]]]))]])) diff --git a/frontend/src/app/main/ui/workspace/shapes/frame/dynamic_modifiers.cljs b/frontend/src/app/main/ui/workspace/shapes/frame/dynamic_modifiers.cljs index 9ec66d7d6..e465205c7 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame/dynamic_modifiers.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame/dynamic_modifiers.cljs @@ -82,7 +82,6 @@ frame? (= :frame type) group? (= :group type) - text? (= :text type) mask? (and group? masked-group?)] (cond @@ -104,10 +103,6 @@ (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]))) @@ -177,8 +172,7 @@ (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")) + (dom/class? node "frame-thumbnail") (let [[transform] (transform-no-resize shape transform modifiers)] (set-transform-att! node "transform" transform)) @@ -233,7 +227,13 @@ (fn [] (when (some? 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))) modifiers)))) diff --git a/frontend/src/app/main/ui/workspace/shapes/text.cljs b/frontend/src/app/main/ui/workspace/shapes/text.cljs index 821fca536..7a5b662d9 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text.cljs @@ -7,6 +7,7 @@ (ns app.main.ui.workspace.shapes.text (:require [app.common.data :as d] + [app.common.data.macros :as dm] [app.common.math :as mth] [app.main.data.workspace.texts :as dwt] [app.main.refs :as refs] @@ -32,24 +33,23 @@ (dwt/apply-text-modifier text-modifier))] [:> shape-container {:shape shape} - [:* - [:g.text-shape - [:& text/text-shape {:shape shape}]] + [:g.text-shape {:key (dm/str "text-" (:id shape))} + [:& text/text-shape {:shape shape}]] - (when (and (debug? :text-outline) (d/not-empty? (:position-data shape))) - (for [data (:position-data shape)] - (let [{:keys [x y width height]} data] - [:* - ;; Text fragment bounding box - [:rect {:x x - :y (- y height) - :width width - :height height - :style {:fill "none" :stroke "red"}}] + (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"}}] - ;; 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"}}]])))]])) + ;; 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/viewport_texts.cljs b/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs index 2045ec671..fea2ad1d9 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs @@ -9,6 +9,7 @@ [app.common.attrs :as attrs] [app.common.data :as d] [app.common.data.macros :as dm] + [app.common.geom.shapes :as gsh] [app.common.math :as mth] [app.common.pages.helpers :as cph] [app.common.text :as txt] @@ -28,6 +29,15 @@ (defn strip-position-data [shape] (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 "Updates the shape with the current state in the editor" [shape editor-state] @@ -88,7 +98,6 @@ (fn [node] (when (some? node) (on-update shape node))))] - [:& fo/text-shape {:key (str "shape-" (:id shape)) :ref handle-update :shape shape @@ -120,7 +129,7 @@ [:* (for [{:keys [id] :as shape} changed-texts] - [:& text-container {:shape shape + [:& text-container {:shape (gsh/transform-shape shape) :on-update handle-update-shape :key (str (dm/str "text-container-" id))}])])) @@ -151,24 +160,30 @@ (defn check-props [new-props old-props] - (and (identical? (unchecked-get new-props "objects") (unchecked-get old-props "objects")) - (= (unchecked-get new-props "edition") (unchecked-get old-props "edition")))) + (and (identical? (unchecked-get new-props "objects") + (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/wrap-props false ::mf/wrap [#(mf/memo' % check-props)]} [props] - (let [objects (obj/get props "objects") - edition (obj/get props "edition") - - xf-texts (comp (filter (comp cph/text-shape? second)) - (map (fn [[id shape]] - [id (strip-position-data shape)]))) + (let [objects (obj/get props "objects") + edition (obj/get props "edition") + modifiers (obj/get props "modifiers") text-shapes (mf/use-memo (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)] @@ -183,4 +198,6 @@ [:* (when 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}]])) diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index 3abcde449..0224855ac 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -246,6 +246,7 @@ [:& stv/viewport-texts {:key (dm/str "texts-" page-id) :page-id page-id :objects base-objects + :modifiers modifiers :edition edition}]]] [:svg.viewport-controls