From 3a6072bc8f7b99777a441145dfa1ef63d24f5ba8 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Fri, 6 May 2022 13:58:55 +0200 Subject: [PATCH 1/9] :bug: Fix problem with RTL --- frontend/src/app/util/text_position_data.js | 66 ++++++++++++++++++++ frontend/src/app/util/text_svg_position.cljs | 55 +++++----------- 2 files changed, 81 insertions(+), 40 deletions(-) create mode 100644 frontend/src/app/util/text_position_data.js diff --git a/frontend/src/app/util/text_position_data.js b/frontend/src/app/util/text_position_data.js new file mode 100644 index 000000000..cb77fe8eb --- /dev/null +++ b/frontend/src/app/util/text_position_data.js @@ -0,0 +1,66 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Copyright (c) UXBOX Labs SL + */ + +"use strict"; + +goog.provide("app.util.text_position_data"); + +goog.scope(function () { + const self = app.util.text_position_data; + const document = goog.global.document; + + function getRangeRects(node, start, end) { + const range = document.createRange(); + range.setStart(node, start); + range.setEnd(node, end); + return range.getClientRects(); + } + + self.parse_text_nodes = function(parent, direction, textNode) { + const content = textNode.textContent; + const textSize = content.length; + const rtl = direction === "rtl"; + + let from = 0; + let to = 0; + let current = ""; + let result = []; + + while (to < textSize) { + const rects = getRangeRects(textNode, from, to + 1); + + if (rects.length > 1) { + let position; + + if (rtl) { + position = rects[1]; + } else { + position = rects[0]; + } + + result.push({ + node: parent, + position: position, + text: current + }); + + from = to; + current = ""; + + } else { + current += content[to]; + to = to + 1; + } + } + + // to == textSize + const rects = getRangeRects(textNode, from, to); + result.push({node: parent, position: rects[0], text: current}); + return result; + }; +}); diff --git a/frontend/src/app/util/text_svg_position.cljs b/frontend/src/app/util/text_svg_position.cljs index d88039196..eff905a64 100644 --- a/frontend/src/app/util/text_svg_position.cljs +++ b/frontend/src/app/util/text_svg_position.cljs @@ -11,45 +11,20 @@ [app.common.transit :as transit] [app.main.store :as st] [app.util.dom :as dom] - [app.util.globals :as global])) + [app.util.text-position-data :as tpd])) -(defn get-range-rects - "Retrieve the rectangles that cover the selection given by a `node` adn - the start and end index `start-i`, `end-i`" - [^js node start-i end-i] - (let [^js range (.createRange global/document)] - (.setStart range node start-i) - (.setEnd range node end-i) - (.getClientRects range))) - -;; TODO: Evaluate to change this function to Javascript (defn parse-text-nodes "Given a text node retrieves the rectangles for everyone of its paragraphs and its text." - [parent-node rtl text-node] + [parent-node direction text-node] - (let [content (.-textContent text-node) - text-size (.-length content)] - - (loop [from-i 0 - to-i 0 - current "" - result []] - (if (>= to-i text-size) - (let [rects (get-range-rects text-node from-i to-i) - entry {:node parent-node - :position (dom/bounding-rect->rect (first rects)) - :text current}] - ;; We need to add the last element not closed yet - (conj result entry)) - - (let [rects (get-range-rects text-node from-i (inc to-i))] - ;; If the rects increase means we're in a new paragraph - (if (> (.-length rects) 1) - (let [entry {:node parent-node - :position (dom/bounding-rect->rect (if rtl (second rects) (first rects))) - :text current}] - (recur to-i to-i "" (conj result entry))) - (recur from-i (inc to-i) (str current (nth content to-i)) result))))))) + (letfn [(parse-entry [^js entry] + {:node (.-node entry) + :position (dom/bounding-rect->rect (.-position entry)) + :text (.-text entry)})] + (into + [] + (map parse-entry) + (tpd/parse-text-nodes parent-node direction text-node)))) (defn calc-text-node-positions @@ -87,9 +62,9 @@ (->> text-nodes (mapcat (fn [parent-node] - (let [rtl (= "rtl" (.-dir (.-parentElement parent-node)))] + (let [direction (.-direction (js/getComputedStyle parent-node))] (->> (.-childNodes parent-node) - (mapcat #(parse-text-nodes parent-node rtl %)))))) + (mapcat #(parse-text-nodes parent-node direction %)))))) (mapv #(update % :position translate-rect)))))) (defn calc-position-data @@ -102,15 +77,13 @@ (->> text-data (mapv (fn [{:keys [node position text]}] (let [{:keys [x y width height]} position - rtl (= "rtl" (.-dir (.-parentElement ^js node))) styles (js/getComputedStyle ^js node) get (fn [prop] (let [value (.getPropertyValue styles prop)] (when (and value (not= value "")) value)))] (d/without-nils - {:rtl rtl - :x (if rtl (+ x width) x) + {:x x :y (+ y height) :width width :height height @@ -123,3 +96,5 @@ :font-style (str (get "font-style")) :fills (transit/decode-str (get "--fills")) :text text})))))))))) + + From 6bbd76f350505df338fb7f5b7f4a1f6d58388d91 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Mon, 9 May 2022 15:16:40 +0200 Subject: [PATCH 2/9] :bug: Fix problem with text shapes in components --- common/src/app/common/pages/common.cljc | 1 + 1 file changed, 1 insertion(+) diff --git a/common/src/app/common/pages/common.cljc b/common/src/app/common/pages/common.cljc index 5c82be7fc..b7f6f2280 100644 --- a/common/src/app/common/pages/common.cljc +++ b/common/src/app/common/pages/common.cljc @@ -25,6 +25,7 @@ :content :content-group :hidden :visibility-group :blocked :modifiable-group + :grow-type :text-font-group :font-family :text-font-group :font-size :text-font-group :font-style :text-font-group From 81dac233a78a1933e6d60e4a2660c846ba622a0b Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Mon, 9 May 2022 16:08:59 +0200 Subject: [PATCH 3/9] :bug: Fix problem with text edition selection area --- frontend/src/app/main/ui/workspace/viewport.cljs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index 715543c84..b17eb4e65 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -297,7 +297,8 @@ (when show-text-editor? [:& text-edition-outline - {:shape (get base-objects edition)}]) + {:shape (get base-objects edition) + :zoom zoom}]) (when show-measures? [:& msr/measurement From 644c796772a329bc09653000ad366d0b3019929c Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Mon, 9 May 2022 16:44:36 +0200 Subject: [PATCH 4/9] :bug: Fix problem with path edition --- .../src/app/main/data/workspace/path/changes.cljs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/frontend/src/app/main/data/workspace/path/changes.cljs b/frontend/src/app/main/data/workspace/path/changes.cljs index 697965eb4..343b33ec2 100644 --- a/frontend/src/app/main/data/workspace/path/changes.cljs +++ b/frontend/src/app/main/data/workspace/path/changes.cljs @@ -23,9 +23,22 @@ (us/verify ::spec/content new-content) (let [shape-id (:id shape) + [old-points old-selrect] + (helpers/content->points+selrect shape old-content) + [new-points new-selrect] (helpers/content->points+selrect shape new-content) + ;; We set the old values so the update-shapes works + objects + (-> objects + (update + shape-id + assoc + :content old-content + :selrect old-selrect + :points old-points)) + changes (-> (pcb/empty-changes it page-id) (pcb/with-objects objects))] From 40a38cbd386a8151aa1e31b3ff1d188ebdfa8248 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Mon, 9 May 2022 17:01:42 +0200 Subject: [PATCH 5/9] :bug: Fix problem when pasting frame and selected shape --- frontend/src/app/main/data/workspace.cljs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 5a491eaf2..76db0ea07 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -1428,7 +1428,12 @@ wrapper (gsh/selection-rect selected-objs) orig-pos (gpt/point (:x1 wrapper) (:y1 wrapper))] (cond - (and (selected-frame? state) (not has-frame?)) + has-frame? + (let [index (cph/get-position-on-parent page-objects uuid/zero) + delta (gpt/subtract mouse-pos orig-pos)] + [uuid/zero uuid/zero delta index]) + + (selected-frame? state) (let [frame-id (first page-selected) frame-object (get page-objects frame-id) From 94d3f66ef1013a6b7384e01640a7f90ff49031d2 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Mon, 9 May 2022 17:30:56 +0200 Subject: [PATCH 6/9] :bug: Fix problem with rotated shapes and auto-width/auto-height --- .../workspace/shapes/text/viewport_texts.cljs | 29 +++++-------------- 1 file changed, 7 insertions(+), 22 deletions(-) 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 7e593d5a6..2045ec671 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 @@ -25,6 +25,9 @@ [app.util.timers :as ts] [rumext.alpha :as mf])) +(defn strip-position-data [shape] + (dissoc shape :position-data :transform :transform-inverse)) + (defn- update-with-editor-state "Updates the shape with the current state in the editor" [shape editor-state] @@ -78,26 +81,13 @@ [props] (let [shape (obj/get props "shape") on-update (obj/get props "on-update") - watch-edits (obj/get props "watch-edits") handle-update (mf/use-callback (mf/deps shape on-update) (fn [node] (when (some? node) - (on-update shape node)))) - - text-modifier-ref - (mf/use-memo - (mf/deps (:id shape)) - #(refs/workspace-text-modifier-by-id (:id shape))) - - text-modifier - (when watch-edits (mf/deref text-modifier-ref)) - - shape (cond-> shape - (some? text-modifier) - (dwt/apply-text-modifier text-modifier))] + (on-update shape node))))] [:& fo/text-shape {:key (str "shape-" (:id shape)) :ref handle-update @@ -134,11 +124,6 @@ :on-update handle-update-shape :key (str (dm/str "text-container-" id))}])])) -(defn strip-position-data [[id shape]] - (let [shape (dissoc shape :position-data :transform :transform-inverse)] - [id shape])) - - (mf/defc viewport-text-editing {::mf/wrap-props false} [props] @@ -162,7 +147,6 @@ #(st/emit! (dwt/remove-text-modifier (:id shape))))) [:& text-container {:shape shape - :watch-edits true :on-update handle-update-shape}])) (defn check-props @@ -178,7 +162,8 @@ edition (obj/get props "edition") xf-texts (comp (filter (comp cph/text-shape? second)) - (map strip-position-data)) + (map (fn [[id shape]] + [id (strip-position-data shape)]))) text-shapes (mf/use-memo @@ -198,4 +183,4 @@ [:* (when editing-shape [:& viewport-text-editing {:shape editing-shape}]) - [:& viewport-texts-wrapper {:text-shapes text-shapes}]])) + [:& viewport-texts-wrapper {:text-shapes (dissoc text-shapes edition)}]])) From 82d744b94a1e9fa5c2afba011cdbbaed0d402382 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Mon, 9 May 2022 17:50:34 +0200 Subject: [PATCH 7/9] :bug: Fix problem with scrolling on already visible layers --- frontend/src/app/main/ui/workspace/sidebar/layers.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs index 62afe4a1f..11c31809c 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs @@ -189,7 +189,7 @@ (when (and single? selected?) (ts/schedule 100 - #(dom/scroll-into-view! node #js {:block "center", :behavior "smooth"})))] + #(dom/scroll-into-view-if-needed! node #js {:block "center", :behavior "smooth"})))] #(when (some? subid) (rx/dispose! subid))))) From 8df93c2707b25649ea48894cf8bb97aa8e8ddc98 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Tue, 10 May 2022 11:50:10 +0200 Subject: [PATCH 8/9] :bug: Fix problem when exporting single text --- frontend/src/app/main/render.cljs | 2 +- .../src/app/main/ui/shapes/text/fontfaces.cljs | 15 +++++++++------ .../src/app/main/ui/workspace/shapes/frame.cljs | 2 +- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/frontend/src/app/main/render.cljs b/frontend/src/app/main/render.cljs index 8e3e77da5..ec193c6af 100644 --- a/frontend/src/app/main/render.cljs +++ b/frontend/src/app/main/render.cljs @@ -402,7 +402,7 @@ :style {:-webkit-print-color-adjust :exact} :fill "none"} - (let [fonts (ff/frame->fonts object objects)] + (let [fonts (ff/shape->fonts object objects)] [:& ff/fontfaces-style {:fonts fonts}]) (case (:type object) diff --git a/frontend/src/app/main/ui/shapes/text/fontfaces.cljs b/frontend/src/app/main/ui/shapes/text/fontfaces.cljs index f2a903948..d08ebbb87 100644 --- a/frontend/src/app/main/ui/shapes/text/fontfaces.cljs +++ b/frontend/src/app/main/ui/shapes/text/fontfaces.cljs @@ -73,12 +73,15 @@ (when (d/not-empty? style) [:style style]))) -(defn frame->fonts - [frame objects] - (->> (cph/get-children objects (:id frame)) - (filter cph/text-shape?) - (map (comp fonts/get-content-fonts :content)) - (reduce set/union #{}))) +(defn shape->fonts + [shape objects] + (let [initial (cond-> #{} + (cph/text-shape? shape) + (into (fonts/get-content-fonts (:content shape))))] + (->> (cph/get-children objects (:id shape)) + (filter cph/text-shape?) + (map (comp fonts/get-content-fonts :content)) + (reduce set/union initial)))) (defn shapes->fonts [shapes] diff --git a/frontend/src/app/main/ui/workspace/shapes/frame.cljs b/frontend/src/app/main/ui/workspace/shapes/frame.cljs index 4102bea86..254d6a87e 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame.cljs @@ -61,7 +61,7 @@ thumbnail? (unchecked-get props "thumbnail?") objects (unchecked-get props "objects") - fonts (mf/use-memo (mf/deps shape objects) #(ff/frame->fonts shape objects)) + 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) From 2976c5c57290b1dce25f309f74c4305a10c74548 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Tue, 10 May 2022 11:58:44 +0200 Subject: [PATCH 9/9] :bug: Fix problem with flipped texts --- .../src/app/main/ui/shapes/text/svg_text.cljs | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) 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 c13f048e6..34e9cf5f3 100644 --- a/frontend/src/app/main/ui/shapes/text/svg_text.cljs +++ b/frontend/src/app/main/ui/shapes/text/svg_text.cljs @@ -8,6 +8,8 @@ (: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.config :as cfg] [app.main.ui.context :as muc] @@ -30,6 +32,23 @@ (d/update-when :position-data #(mapv update-color %)) (assoc :stroke-color "#FFFFFF" :stroke-opacity 1)))) +(defn position-data-transform + [shape {:keys [x y width height]}] + (let [rect (gsh/make-rect x (- y height) width height) + center (gsh/center-rect rect)] + (when (or (:flip-x shape) (:flip-y shape)) + (-> (gmt/matrix) + (gmt/translate center) + + (cond-> (:flip-x shape) + (gmt/scale (gpt/point -1 1)) + + (:flip-y shape) + (gmt/scale (gpt/point 1 -1))) + + (gmt/translate (gpt/negate center)) + (dm/str))))) + (mf/defc text-shape {::mf/wrap-props false ::mf/wrap [mf/memo]} @@ -41,7 +60,7 @@ {:keys [x y width height position-data]} shape - transform (str (gsh/transform-matrix shape)) + transform (str (gsh/transform-matrix shape {:no-flip true})) ;; These position attributes are not really necesary but they are convenient for for the export group-props (-> #js {:transform transform @@ -75,6 +94,7 @@ props (-> #js {:key (dm/str "text-" (:id shape) "-" index) :x (:x data) :y y + :transform (position-data-transform shape data) :alignmentBaseline alignment-bl :dominantBaseline dominant-bl :style (-> #js {:fontFamily (:font-family data)