🐛 Fixed issues with text selection and edition

This commit is contained in:
alonso.torres 2021-01-21 16:09:13 +01:00 committed by Andrey Antukh
parent 40e54dbbd4
commit 86936a66e0
9 changed files with 164 additions and 109 deletions

View file

@ -50,7 +50,7 @@
(when (and wrapper (when (and wrapper
(not allow-click-outside) (not allow-click-outside)
(not (.contains wrapper current)) (not (.contains wrapper current))
(not (= type (keyword (.getAttribute current "data-allow-click-modal"))))) (not (= type (keyword (dom/get-data current "allow-click-modal")))))
(dom/stop-propagation event) (dom/stop-propagation event)
(dom/prevent-default event) (dom/prevent-default event)
(st/emit! (dm/hide))))) (st/emit! (dm/hide)))))

View file

@ -52,21 +52,24 @@
drawing? @refs/selected-drawing-tool drawing? @refs/selected-drawing-tool
button (.-which (.-nativeEvent event)) button (.-which (.-nativeEvent event))
shift? (kbd/shift? event) shift? (kbd/shift? event)
ctrl? (kbd/ctrl? event)] ctrl? (kbd/ctrl? event)
(dom/prevent-default event)
(when-not blocked allow-click? (and (not blocked)
(not drawing?)
(not ctrl?)
(not edition))]
(when (and (= button 1) allow-click?)
(cond (cond
(or (not= 1 button) drawing? ctrl? edition) (and (= type :frame) selected?)
nil
(= type :frame)
(when selected?
(do
(dom/stop-propagation event)
(st/emit! (dw/start-move-selected))))
:else
(do (do
(dom/prevent-default event)
(dom/stop-propagation event)
(st/emit! (dw/start-move-selected)))
(not= type :frame)
(do
(dom/prevent-default event)
(dom/stop-propagation event) (dom/stop-propagation event)
(let [toggle-selected? (and selected? shift?) (let [toggle-selected? (and selected? shift?)

View file

@ -34,7 +34,7 @@
[app.main.ui.measurements :as msr] [app.main.ui.measurements :as msr]
[app.main.ui.workspace.shapes.path.editor :refer [path-editor]])) [app.main.ui.workspace.shapes.path.editor :refer [path-editor]]))
(def rotation-handler-size 25) (def rotation-handler-size 20)
(def resize-point-radius 4) (def resize-point-radius 4)
(def resize-point-circle-radius 10) (def resize-point-circle-radius 10)
(def resize-point-rect-size 8) (def resize-point-rect-size 8)
@ -43,6 +43,7 @@
(def selection-rect-color-component "#00E0FF") (def selection-rect-color-component "#00E0FF")
(def selection-rect-width 1) (def selection-rect-width 1)
(def min-selrect-side 10) (def min-selrect-side 10)
(def small-selrect-side 30)
(mf/defc selection-rect [{:keys [transform rect zoom color]}] (mf/defc selection-rect [{:keys [transform rect zoom color]}]
(when rect (when rect
@ -57,66 +58,78 @@
:stroke-width (/ selection-rect-width zoom) :stroke-width (/ selection-rect-width zoom)
:fill "transparent"}}]))) :fill "transparent"}}])))
(defn- handlers-for-selection [{:keys [x y width height]}] (defn- handlers-for-selection [{:keys [x y width height]} {:keys [type]} zoom]
(->> (let [zoom-width (* width zoom)
[ ;; TOP-LEFT zoom-height (* height zoom)
{:type :rotation
:position :top-left
:props {:cx x :cy y}}
(when (and (> width min-selrect-side) (> height min-selrect-side)) align (when (or (<= zoom-width small-selrect-side)
{:type :resize-point (<= zoom-height small-selrect-side))
:outside)
show-resize-point? (or (not= type :path)
(and
(> zoom-width min-selrect-side)
(> zoom-height min-selrect-side)))
min-side-top? (or (not= type :path) (> zoom-height min-selrect-side))
min-side-side? (or (not= type :path) (> zoom-width min-selrect-side))]
(->>
[ ;; TOP-LEFT
{:type :rotation
:position :top-left :position :top-left
:props {:cx x :cy y}}) :props {:cx x :cy y}}
{:type :rotation (when show-resize-point?
:position :top-right {:type :resize-point
:props {:cx (+ x width) :cy y}} :position :top-left
:props {:cx x :cy y :align align}})
(when (and (> width min-selrect-side) (> height min-selrect-side)) {:type :rotation
{:type :resize-point
:position :top-right :position :top-right
:props {:cx (+ x width) :cy y}}) :props {:cx (+ x width) :cy y}}
{:type :rotation (when show-resize-point?
:position :bottom-right {:type :resize-point
:props {:cx (+ x width) :cy (+ y height)}} :position :top-right
:props {:cx (+ x width) :cy y :align align}})
(when (and (> width min-selrect-side) (> height min-selrect-side)) {:type :rotation
{:type :resize-point
:position :bottom-right :position :bottom-right
:props {:cx (+ x width) :cy (+ y height)}}) :props {:cx (+ x width) :cy (+ y height)}}
{:type :rotation (when show-resize-point?
:position :bottom-left {:type :resize-point
:props {:cx x :cy (+ y height)}} :position :bottom-right
:props {:cx (+ x width) :cy (+ y height) :align align}})
(when (and (> width min-selrect-side) (> height min-selrect-side)) {:type :rotation
{:type :resize-point
:position :bottom-left :position :bottom-left
:props {:cx x :cy (+ y height)}}) :props {:cx x :cy (+ y height)}}
(when (> height min-selrect-side) (when show-resize-point?
{:type :resize-side {:type :resize-point
:position :top :position :bottom-left
:props {:x x :y y :length width :angle 0 }}) :props {:cx x :cy (+ y height) :align align}})
(when (> width min-selrect-side) (when min-side-top?
{:type :resize-side {:type :resize-side
:position :right :position :top
:props {:x (+ x width) :y y :length height :angle 90 }}) :props {:x x :y y :length width :angle 0 :align align}})
(when (> height min-selrect-side) (when min-side-side?
{:type :resize-side {:type :resize-side
:position :bottom :position :right
:props {:x (+ x width) :y (+ y height) :length width :angle 180 }}) :props {:x (+ x width) :y y :length height :angle 90 :align align}})
(when (> width min-selrect-side) (when min-side-top?
{:type :resize-side {:type :resize-side
:position :left :position :bottom
:props {:x x :y (+ y height) :length height :angle 270 }})] :props {:x (+ x width) :y (+ y height) :length width :angle 180 :align align}})
(filterv (comp not nil?)))) (when min-side-side?
{:type :resize-side
:position :left
:props {:x x :y (+ y height) :length height :angle 270 :align align}})]
(filterv (comp not nil?)))))
(mf/defc rotation-handler [{:keys [cx cy transform position rotation zoom on-rotate]}] (mf/defc rotation-handler [{:keys [cx cy transform position rotation zoom on-rotate]}]
(let [size (/ rotation-handler-size zoom) (let [size (/ rotation-handler-size zoom)
@ -137,13 +150,11 @@
:on-mouse-down on-rotate}])) :on-mouse-down on-rotate}]))
(mf/defc resize-point-handler (mf/defc resize-point-handler
[{:keys [cx cy zoom position on-resize transform rotation color overflow-text]}] [{:keys [cx cy zoom position on-resize transform rotation color overflow-text align]}]
(let [{cx' :x cy' :y} (gpt/transform (gpt/point cx cy) transform) (let [cursor (if (#{:top-left :bottom-right} position)
rot-square (case position (cur/resize-nesw rotation) (cur/resize-nwse rotation))
:top-left 0 {cx' :x cy' :y} (gpt/transform (gpt/point cx cy) transform)]
:top-right 90
:bottom-right 180
:bottom-left 270)]
[:g.resize-handler [:g.resize-handler
[:circle {:r (/ resize-point-radius zoom) [:circle {:r (/ resize-point-radius zoom)
:style {:fillOpacity "1" :style {:fillOpacity "1"
@ -154,24 +165,53 @@
:cx cx' :cx cx'
:cy cy'}] :cy cy'}]
[:circle {:on-mouse-down #(on-resize {:x cx' :y cy'} %) (if (= align :outside)
:r (/ resize-point-circle-radius zoom) (let [resize-point-circle-radius (/ resize-point-circle-radius zoom)
:fill (if (debug? :resize-handler) "red" "transparent") offset-x (if (#{:top-right :bottom-right} position) 0 (- resize-point-circle-radius))
:cx cx' offset-y (if (#{:bottom-left :bottom-right} position) 0 (- resize-point-circle-radius))
:cy cy' cx (+ cx offset-x)
:style {:cursor (if (#{:top-left :bottom-right} position) cy (+ cy offset-y)
(cur/resize-nesw rotation) (cur/resize-nwse rotation))}}] {cx' :x cy' :y} (gpt/transform (gpt/point cx cy) transform)]
])) [:rect {:x cx'
:y cy'
:width resize-point-circle-radius
:height resize-point-circle-radius
:transform (str/fmt "rotate(%s, %s, %s)" rotation cx' cy')
:style {:fill (if (debug? :resize-handler) "red" "transparent")
:cursor cursor}
:on-mouse-down #(on-resize {:x cx' :y cy'} %)}])
(mf/defc resize-side-handler [{:keys [x y length angle zoom position rotation transform on-resize]}] (let [rot-square (case position
:top-left 0
:top-right 90
:bottom-right 180
:bottom-left 270)]
[:circle {:on-mouse-down #(on-resize {:x cx' :y cy'} %)
:r (/ resize-point-circle-radius zoom)
:cx cx'
:cy cy'
:style {:fill (if (debug? :resize-handler) "red" "transparent")
:cursor cursor}}])
)]))
(mf/defc resize-side-handler
"The side handler is always rendered horizontaly and then rotated"
[{:keys [x y length align angle zoom position rotation transform on-resize]}]
(let [res-point (if (#{:top :bottom} position) (let [res-point (if (#{:top :bottom} position)
{:y y} {:y y}
{:x x}) {:x x})
target-length (max 0 (- length (/ (* resize-point-rect-size 2) zoom))) target-length (max 0 (- length (/ (* resize-point-rect-size 2) zoom)))
width (if (< target-length 6) length target-length) width (if (< target-length 6) length target-length)
height (/ resize-side-height zoom)] height (/ resize-side-height zoom)
[:rect {:x (+ x (/ (- length width) 2))
:y (- y (/ height 2)) offset-x (/ (- length width) 2)
offset-y (if (= align :outside) (- height) (- (/ height 2)))
target-x (+ x offset-x)
target-y (+ y offset-y)]
[:rect {:x target-x
:y target-y
:width width :width width
:height height :height height
:transform (gmt/multiply transform :transform (gmt/multiply transform
@ -217,7 +257,7 @@
[:& outline {:shape shape :color color}] [:& outline {:shape shape :color color}]
;; Handlers ;; Handlers
(for [{:keys [type position props]} (handlers-for-selection selrect)] (for [{:keys [type position props]} (handlers-for-selection selrect shape zoom)]
(let [common-props {:key (str (name type) "-" (name position)) (let [common-props {:key (str (name type) "-" (name position))
:zoom zoom :zoom zoom
:position position :position position

View file

@ -43,20 +43,23 @@
(recur (first ids) (rest ids)) (recur (first ids) (rest ids))
false)))))) false))))))
(defn use-select-shape [{:keys [id]}] (defn use-select-shape [{:keys [id]} edition]
(mf/use-callback (mf/use-callback
(mf/deps id) (mf/deps id edition)
(fn [event] (fn [event]
(let [selected @refs/selected-shapes (when (not edition)
selected? (contains? selected id)] (let [selected @refs/selected-shapes
(dom/prevent-default event) selected? (contains? selected id)
(if selected? shift? (kbd/shift? event)]
(when (kbd/shift? event) (cond
(st/emit! (dw/select-shape id true))) (and selected? shift?)
(do (st/emit! (dw/select-shape id true))
(when-not (or (empty? selected) (kbd/shift? event))
(st/emit! (dw/deselect-all))) (and (not (empty? selected)) (not shift?))
(st/emit! (dw/select-shape id)))))))) (st/emit! (dw/deselect-all) (dw/select-shape id))
(not selected?)
(st/emit! (dw/select-shape id))))))))
;; Ensure that the label has always the same font ;; Ensure that the label has always the same font
;; size, regardless of zoom ;; size, regardless of zoom
@ -72,8 +75,9 @@
[{:keys [frame]}] [{:keys [frame]}]
(let [{:keys [width x y]} frame (let [{:keys [width x y]} frame
zoom (mf/deref refs/selected-zoom) zoom (mf/deref refs/selected-zoom)
edition (mf/deref refs/selected-edition)
label-pos (gpt/point x (- y (/ 10 zoom))) label-pos (gpt/point x (- y (/ 10 zoom)))
handle-click (use-select-shape frame) handle-click (use-select-shape frame edition)
handle-pointer-enter (we/use-pointer-enter frame) handle-pointer-enter (we/use-pointer-enter frame)
handle-pointer-leave (we/use-pointer-leave frame)] handle-pointer-leave (we/use-pointer-leave frame)]
[:text {:x 0 [:text {:x 0
@ -123,6 +127,7 @@
(let [shape (unchecked-get props "shape") (let [shape (unchecked-get props "shape")
objects (unchecked-get props "objects") objects (unchecked-get props "objects")
ghost? (mf/use-ctx muc/ghost-ctx) ghost? (mf/use-ctx muc/ghost-ctx)
edition (mf/deref refs/selected-edition)
moving-iref (mf/use-memo (mf/deps (:id shape)) moving-iref (mf/use-memo (mf/deps (:id shape))
#(make-is-moving-ref (:id shape))) #(make-is-moving-ref (:id shape)))
@ -137,7 +142,7 @@
ds-modifier (get-in shape [:modifiers :displacement]) ds-modifier (get-in shape [:modifiers :displacement])
handle-context-menu (we/use-context-menu shape) handle-context-menu (we/use-context-menu shape)
handle-double-click (use-select-shape shape) handle-double-click (use-select-shape shape edition)
handle-mouse-down (we/use-mouse-down shape) handle-mouse-down (we/use-mouse-down shape)
hide-moving? (and (not ghost?) moving?)] hide-moving? (and (not ghost?) moving?)]

View file

@ -45,6 +45,7 @@
(and (= edit-mode :move) selected?) (and (= edit-mode :move) selected?)
(st/emit! (drp/deselect-node position)))))) (st/emit! (drp/deselect-node position))))))
on-mouse-down on-mouse-down
(fn [event] (fn [event]
(when-not last-p? (when-not last-p?
@ -60,6 +61,7 @@
(and (= edit-mode :draw) (not start-path?)) (and (= edit-mode :draw) (not start-path?))
(st/emit! (drp/close-path-drag-start position))))))] (st/emit! (drp/close-path-drag-start position))))))]
[:g.path-point [:g.path-point
[:circle.path-point [:circle.path-point
{:cx x {:cx x
@ -182,11 +184,18 @@
editor-dom (mf/ref-val editor-ref)] editor-dom (mf/ref-val editor-ref)]
(when-not (or (.contains editor-dom current) (when-not (or (.contains editor-dom current)
(dom/class? current "viewport-actions-entry")) (dom/class? current "viewport-actions-entry"))
(st/emit! (drp/deselect-all)))))] (st/emit! (drp/deselect-all)))))
handle-double-click-outside
(fn [event]
(when (= edit-mode :move)
(st/emit! :interrupt)))]
(mf/use-layout-effect (mf/use-layout-effect
(mf/deps edit-mode)
(fn [] (fn []
(let [keys [(events/listen js/document EventType.CLICK handle-click-outside)]] (let [keys [(events/listen (dom/get-root) EventType.CLICK handle-click-outside)
(events/listen (dom/get-root) EventType.DBLCLICK handle-double-click-outside)]]
#(doseq [key keys] #(doseq [key keys]
(events/unlistenByKey key))))) (events/unlistenByKey key)))))

View file

@ -59,7 +59,6 @@
render-editor (mf/use-state false) render-editor (mf/use-state false)
edition? (= edition id) edition? (= edition id)
embed-resources? (mf/use-ctx muc/embed-ctx) embed-resources? (mf/use-ctx muc/embed-ctx)
handle-mouse-down (we/use-mouse-down shape) handle-mouse-down (we/use-mouse-down shape)
@ -116,7 +115,7 @@
:shape shape :shape shape
:selected? selected? :selected? selected?
:grow-type (:grow-type shape)}]] :grow-type (:grow-type shape)}]]
(when edition? (when (and (not ghost?) edition?)
[:& editor/text-shape-edit {:key (str "editor" (:id shape)) [:& editor/text-shape-edit {:key (str "editor" (:id shape))
:shape shape}]) :shape shape}])

View file

@ -167,19 +167,18 @@
on-click-outside on-click-outside
(fn [event] (fn [event]
(let [options (dom/get-element-by-class "element-options") (let [target (dom/get-target event)
options (dom/get-element-by-class "element-options")
assets (dom/get-element-by-class "assets-bar") assets (dom/get-element-by-class "assets-bar")
cpicker (dom/get-element-by-class "colorpicker-tooltip") cpicker (dom/get-element-by-class "colorpicker-tooltip")
self (mf/ref-val self-ref) self (mf/ref-val self-ref)
target (dom/get-target event)
selecting? (mf/ref-val selecting-ref)] selecting? (mf/ref-val selecting-ref)]
(when-not (or (and options (.contains options target)) (when-not (or (and options (.contains options target))
(and assets (.contains assets target)) (and assets (.contains assets target))
(and self (.contains self target)) (and self (.contains self target))
(and cpicker (.contains cpicker target))) (and cpicker (.contains cpicker target)))
(do (do
(dom/prevent-default event)
(dom/stop-propagation event)
(if selecting? (if selecting?
(mf/set-ref-val! selecting-ref false) (mf/set-ref-val! selecting-ref false)
@ -203,16 +202,16 @@
on-mount on-mount
(fn [] (fn []
(let [lkey1 (events/listen js/document EventType.CLICK on-click-outside) (let [keys [(events/listen js/document EventType.CLICK on-click-outside)
lkey2 (events/listen js/document EventType.KEYUP on-key-up)] (events/listen js/document EventType.KEYUP on-key-up)]]
(st/emit! (dwt/assign-editor id editor) (st/emit! (dwt/assign-editor id editor)
(dwc/start-undo-transaction)) (dwc/start-undo-transaction))
#(do #(do
(st/emit! (dwt/assign-editor id nil) (st/emit! (dwt/assign-editor id nil)
(dwc/commit-undo-transaction)) (dwc/commit-undo-transaction))
(events/unlistenByKey lkey1) (doseq [key keys]
(events/unlistenByKey lkey2)))) (events/unlistenByKey key)))))
on-focus on-focus
(fn [event] (fn [event]

View file

@ -345,10 +345,7 @@
(let [ctrl? (kbd/ctrl? event) (let [ctrl? (kbd/ctrl? event)
shift? (kbd/shift? event) shift? (kbd/shift? event)
alt? (kbd/alt? event)] alt? (kbd/alt? event)]
(st/emit! (ms/->MouseEvent :double-click ctrl? shift? alt?)) (st/emit! (ms/->MouseEvent :double-click ctrl? shift? alt?)))))
(if (not drawing-path?)
(st/emit! dw/clear-edition-mode)))))
on-key-down on-key-down
(mf/use-callback (mf/use-callback

View file

@ -253,3 +253,6 @@
(defn active? [node] (defn active? [node]
(= (.-activeElement js/document) node)) (= (.-activeElement js/document) node))
(defn get-data [^js node ^string attr]
(.getAttribute node (str "data-" attr)))