From 95a2da5ebc706b44cbcf451a68da8e884e823056 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Moya?= Date: Thu, 12 May 2022 14:10:31 +0200 Subject: [PATCH] :sparkles: Rework multi edit of measures values --- common/src/app/common/attrs.cljc | 77 +++++++++++++------ common/src/app/common/data.cljc | 10 +++ common/src/app/common/geom/shapes/text.cljc | 1 - .../app/common/geom/shapes/transforms.cljc | 3 +- common/src/app/common/pages/common.cljc | 9 +++ .../src/app/main/data/workspace/texts.cljs | 6 +- .../workspace/shapes/text/viewport_texts.cljs | 3 +- .../sidebar/options/menus/measures.cljs | 27 +++++-- 8 files changed, 100 insertions(+), 36 deletions(-) diff --git a/common/src/app/common/attrs.cljc b/common/src/app/common/attrs.cljc index febb78446..b63f036d8 100644 --- a/common/src/app/common/attrs.cljc +++ b/common/src/app/common/attrs.cljc @@ -5,7 +5,49 @@ ;; Copyright (c) UXBOX Labs SL (ns app.common.attrs - (:refer-clojure :exclude [merge])) + (:require + [app.common.geom.shapes.transforms :as gst] + [app.common.math :as mth])) + +(defn- get-attr + [obj attr] + (if (= (get obj attr) :multiple) + :multiple + (cond + ;; For rotated or stretched shapes, the origin point we show in the menu + ;; is not the (:x :y) shape attribute, but the top left coordinate of the + ;; wrapping recangle (see measures.cljs). As the :points attribute cannot + ;; be merged for several objects, we calculate the origin point in two fake + ;; attributes to be used in the measures menu. + (#{:ox :oy} attr) + (if-let [value (get obj attr)] + value + (if-let [points (:points obj)] + (if (not= points :multiple) + (let [rect (gst/selection-rect [obj])] + (if (= attr :ox) (:x rect) (:y rect))) + :multiple) + (get obj attr ::unset))) + + ;; Not all shapes have width and height (e.g. paths), so we extract + ;; them from the :selrect attribute. + (#{:width :height} attr) + (if-let [value (get obj attr)] + value + (if-let [selrect (:selrect obj)] + (if (not= selrect :multiple) + (get (:selrect obj) attr) + :multiple) + (get obj attr ::unset))) + + :else + (get obj attr ::unset)))) + +(defn- default-equal + [val1 val2] + (if (and (number? val1) (number? val2)) + (mth/close? val1 val2) + (= val1 val2))) ;; Extract some attributes of a list of shapes. ;; For each attribute, if the value is the same in all shapes, @@ -36,13 +78,11 @@ ;; :rx nil ;; :ry nil} ;; - (defn get-attrs-multi ([objs attrs] - (get-attrs-multi objs attrs = identity)) + (get-attrs-multi objs attrs default-equal identity)) ([objs attrs eqfn sel] - (loop [attr (first attrs) attrs (rest attrs) result (transient {})] @@ -50,34 +90,25 @@ (let [value (loop [curr (first objs) objs (rest objs) - value ::undefined] + value ::unset] (if (and curr (not= value :multiple)) - ;; - (let [new-val (get curr attr ::undefined) + (let [new-val (get-attr curr attr) value (cond - (= new-val ::undefined) value - (= new-val :multiple) :multiple - (= value ::undefined) (sel new-val) - (eqfn new-val value) value - :else :multiple)] + (= new-val ::unset) value + (= new-val :multiple) :multiple + (= value ::unset) (sel new-val) + (eqfn new-val value) value + :else :multiple)] (recur (first objs) (rest objs) value)) - ;; + value))] + (recur (first attrs) (rest attrs) (cond-> result - (not= value ::undefined) + (not= value ::unset) (assoc! attr value)))) (persistent! result))))) -(defn merge - "Attrs specific merge function." - [obj attrs] - (reduce-kv (fn [obj k v] - (if (nil? v) - (dissoc obj k) - (assoc obj k v))) - obj - attrs)) diff --git a/common/src/app/common/data.cljc b/common/src/app/common/data.cljc index b7603c8ed..ee7bd560e 100644 --- a/common/src/app/common/data.cljc +++ b/common/src/app/common/data.cljc @@ -336,6 +336,16 @@ [& maps] (reduce conj (or (first maps) {}) (rest maps))) +(defn txt-merge + "Text attrs specific merge function." + [obj attrs] + (reduce-kv (fn [obj k v] + (if (nil? v) + (dissoc obj k) + (assoc obj k v))) + obj + attrs)) + (defn distinct-xf [f] (fn [rf] diff --git a/common/src/app/common/geom/shapes/text.cljc b/common/src/app/common/geom/shapes/text.cljc index a23cca61a..9fdb0b34b 100644 --- a/common/src/app/common/geom/shapes/text.cljc +++ b/common/src/app/common/geom/shapes/text.cljc @@ -28,4 +28,3 @@ [shape] (gpr/points->selrect (position-data-points shape))) - diff --git a/common/src/app/common/geom/shapes/transforms.cljc b/common/src/app/common/geom/shapes/transforms.cljc index 3f561b602..cd0a707b6 100644 --- a/common/src/app/common/geom/shapes/transforms.cljc +++ b/common/src/app/common/geom/shapes/transforms.cljc @@ -6,7 +6,6 @@ (ns app.common.geom.shapes.transforms (:require - [app.common.attrs :as attrs] [app.common.data :as d] [app.common.geom.matrix :as gmt] [app.common.geom.point :as gpt] @@ -533,7 +532,7 @@ (* (get-in modifiers [:resize-vector :x] 1)) (* (get-in modifiers [:resize-vector-2 :x] 1)) (str))] - (attrs/merge attrs {:font-size font-size})))] + (d/txt-merge attrs {:font-size font-size})))] (update shape :content #(txt/transform-nodes txt/is-text-node? merge-attrs diff --git a/common/src/app/common/pages/common.cljc b/common/src/app/common/pages/common.cljc index b7f6f2280..2ac202d73 100644 --- a/common/src/app/common/pages/common.cljc +++ b/common/src/app/common/pages/common.cljc @@ -79,6 +79,7 @@ :rx :ry :r1 :r2 :r3 :r4 :selrect + :points :opacity :blend-mode @@ -112,6 +113,7 @@ :x :y :rotation :selrect + :points :constraints-h :constraints-v @@ -137,6 +139,7 @@ :rx :ry :r1 :r2 :r3 :r4 :selrect + :points :constraints-h :constraints-v @@ -179,6 +182,7 @@ :x :y :rotation :selrect + :points :constraints-h :constraints-v @@ -221,6 +225,7 @@ :x :y :rotation :selrect + :points :constraints-h :constraints-v @@ -263,6 +268,7 @@ :x :y :rotation :selrect + :points :constraints-h :constraints-v @@ -330,6 +336,7 @@ :rx :ry :r1 :r2 :r3 :r4 :selrect + :points :constraints-h :constraints-v @@ -355,6 +362,7 @@ :rx :ry :r1 :r2 :r3 :r4 :selrect + :points :constraints-h :constraints-v @@ -399,6 +407,7 @@ :rx :ry :r1 :r2 :r3 :r4 :selrect + :points :constraints-h :constraints-v diff --git a/frontend/src/app/main/data/workspace/texts.cljs b/frontend/src/app/main/data/workspace/texts.cljs index b9545818f..ec0045125 100644 --- a/frontend/src/app/main/data/workspace/texts.cljs +++ b/frontend/src/app/main/data/workspace/texts.cljs @@ -187,8 +187,8 @@ update-fn (fn [shape] (if (some? (:content shape)) - (update-text-content shape txt/is-root-node? attrs/merge attrs) - (assoc shape :content (attrs/merge {:type "root"} attrs)))) + (update-text-content shape txt/is-root-node? d/txt-merge attrs) + (assoc shape :content (d/txt-merge {:type "root"} attrs)))) shape-ids (cond (cph/text-shape? shape) [id] (cph/group-shape? shape) (cph/get-children-ids objects id))] @@ -240,7 +240,7 @@ shape-ids (cond (cph/text-shape? shape) [id] (cph/group-shape? shape) (cph/get-children-ids objects id))] - (rx/of (dch/update-shapes shape-ids #(update-text-content % update-node? attrs/merge attrs)))))))) + (rx/of (dch/update-shapes shape-ids #(update-text-content % update-node? d/txt-merge attrs)))))))) (defn migrate-node [node] 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 2e65fd79c..b78c3967a 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 @@ -6,7 +6,6 @@ (ns app.main.ui.workspace.shapes.text.viewport-texts (:require - [app.common.attrs :as attrs] [app.common.data :as d] [app.common.data.macros :as dm] [app.common.geom.shapes :as gsh] @@ -50,7 +49,7 @@ (cond-> shape (and (some? shape) (some? editor-content)) - (assoc :content (attrs/merge content editor-content))))) + (assoc :content (d/txt-merge content editor-content))))) (defn- update-text-shape [{:keys [grow-type id]} node] diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs index dfc8f1640..58d4bc2d4 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs @@ -25,10 +25,12 @@ [:proportion-lock :width :height :x :y + :ox :oy :rotation :rx :ry :r1 :r2 :r3 :r4 - :selrect]) + :selrect + :points]) (def ^:private type->options {:bool #{:size :position :rotation} @@ -46,7 +48,7 @@ ;; -- User/drawing coords (mf/defc measures-menu [{:keys [ids ids-with-children values type all-types shape] :as props}] - + (let [options (if (= type :multiple) (reduce #(union %1 %2) (map #(get type->options %) all-types)) (get type->options type)) @@ -58,20 +60,36 @@ [shape]) frames (map #(deref (refs/object-by-id (:frame-id %))) old-shapes) + ;; To show interactively the measures while the user is manipulating + ;; the shape with the mouse, generate a copy of the shapes applying + ;; the transient tranformations. shapes (as-> old-shapes $ (map gsh/transform-shape $) (map gsh/translate-to-frame $ frames)) - values (let [{:keys [x y]} (-> shapes first :points gsh/points->selrect)] + ;; For rotated or stretched shapes, the origin point we show in the menu + ;; is not the (:x :y) shape attribute, but the top left coordinate of the + ;; wrapping rectangle. + values (let [{:keys [x y]} (gsh/selection-rect [(first shapes)])] (cond-> values (not= (:x values) :multiple) (assoc :x x) - (not= (:y values) :multiple) (assoc :y y))) + (not= (:y values) :multiple) (assoc :y y) + ;; In case of multiple selection, the origin point has been already + ;; calculated and given in the fake :ox and :oy attributes. See + ;; common/src/app/common/attrs.cljc + (some? (:ox values)) (assoc :x (:ox values)) + (some? (:oy values)) (assoc :y (:oy values)))) + ;; For :height and :width we take those in the :selrect attribute, because + ;; not all shapes have an own :width and :height (e. g. paths). Here the + ;; rotation is ignored (selrect always has the original size excluding + ;; transforms). values (let [{:keys [width height]} (-> shapes first :selrect)] (cond-> values (not= (:width values) :multiple) (assoc :width width) (not= (:height values) :multiple) (assoc :height height))) + ;; The :rotation, however, does use the transforms. values (let [{:keys [rotation] :or {rotation 0}} (-> shapes first)] (cond-> values (not= (:rotation values) :multiple) (assoc :rotation rotation))) @@ -85,7 +103,6 @@ radius-multi? (mf/use-state nil) radius-input-ref (mf/use-ref nil) - on-preset-selected (fn [width height] (st/emit! (udw/update-dimensions ids :width width)