mirror of
https://github.com/penpot/penpot.git
synced 2025-02-18 13:04:38 -05:00
🎉 New cursors in viewport
This commit is contained in:
parent
b3237524bd
commit
bedd244f44
10 changed files with 49 additions and 34 deletions
|
@ -145,15 +145,6 @@
|
||||||
|
|
||||||
g.controls {
|
g.controls {
|
||||||
rect.main { pointer-events: none; }
|
rect.main { pointer-events: none; }
|
||||||
circle.rotate { cursor: ns-resize; } // TODO
|
|
||||||
.top-left { cursor: nwse-resize; }
|
|
||||||
.bottom-right { cursor: nwse-resize; }
|
|
||||||
.top-right { cursor: nesw-resize; }
|
|
||||||
.bottom-left { cursor: nesw-resize; }
|
|
||||||
.top { cursor: ns-resize; }
|
|
||||||
.bottom { cursor: ns-resize; }
|
|
||||||
.left { cursor: ew-resize; }
|
|
||||||
.right { cursor: ew-resize; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -315,6 +315,14 @@
|
||||||
|
|
||||||
(reduce reduce-fn state groups-to-adjust)))))
|
(reduce reduce-fn state groups-to-adjust)))))
|
||||||
|
|
||||||
|
(defn start-pan [state]
|
||||||
|
(-> state
|
||||||
|
(assoc-in [:workspace-local :panning] true)))
|
||||||
|
|
||||||
|
(defn finish-pan [state]
|
||||||
|
(-> state
|
||||||
|
(update :workspace-local dissoc :panning)))
|
||||||
|
|
||||||
|
|
||||||
;; --- Toggle layout flag
|
;; --- Toggle layout flag
|
||||||
|
|
||||||
|
|
|
@ -204,7 +204,7 @@
|
||||||
(rx/take-until stopper)
|
(rx/take-until stopper)
|
||||||
(rx/map #(gpt/to-vec initial %))
|
(rx/map #(gpt/to-vec initial %))
|
||||||
(rx/map #(gpt/length %))
|
(rx/map #(gpt/length %))
|
||||||
(rx/filter #(> % 0.5))
|
(rx/filter #(> % 1))
|
||||||
(rx/take 1)
|
(rx/take 1)
|
||||||
(rx/map #(start-move initial selected)))))))
|
(rx/map #(start-move initial selected)))))))
|
||||||
|
|
||||||
|
|
|
@ -64,11 +64,13 @@
|
||||||
data))
|
data))
|
||||||
|
|
||||||
(defmacro cursor-ref
|
(defmacro cursor-ref
|
||||||
|
"Creates a static cursor given its name, rotation and x/y hotspot"
|
||||||
([id] (encode-svg-cursor id default-rotation default-hotspot-x default-hotspot-y))
|
([id] (encode-svg-cursor id default-rotation default-hotspot-x default-hotspot-y))
|
||||||
([id rotation] (encode-svg-cursor id rotation default-hotspot-x default-hotspot-y))
|
([id rotation] (encode-svg-cursor id rotation default-hotspot-x default-hotspot-y))
|
||||||
([id rotation x y] (encode-svg-cursor id rotation x y)))
|
([id rotation x y] (encode-svg-cursor id rotation x y)))
|
||||||
|
|
||||||
(defmacro cursor-fn
|
(defmacro cursor-fn
|
||||||
|
"Creates a dynamic cursor that can be rotated in runtime"
|
||||||
[id initial]
|
[id initial]
|
||||||
(let [cursor (encode-svg-cursor id "{{rotation}}" default-hotspot-x default-hotspot-y)]
|
(let [cursor (encode-svg-cursor id "{{rotation}}" default-hotspot-x default-hotspot-y)]
|
||||||
`(fn [rot#]
|
`(fn [rot#]
|
||||||
|
|
|
@ -26,15 +26,10 @@
|
||||||
(def pen (cursor-ref :pen 0 0 0))
|
(def pen (cursor-ref :pen 0 0 0))
|
||||||
(def pointer-inner (cursor-ref :pointer-inner 0 0 0))
|
(def pointer-inner (cursor-ref :pointer-inner 0 0 0))
|
||||||
(def resize-alt (cursor-ref :resize-alt))
|
(def resize-alt (cursor-ref :resize-alt))
|
||||||
|
|
||||||
#_(def resize-nesw (cursor-fn :resize-diagonal 90))
|
|
||||||
#_(def resize-nwse (cursor-fn :resize-diagonal 0))
|
|
||||||
(def resize-nesw (cursor-fn :resize-h 45))
|
(def resize-nesw (cursor-fn :resize-h 45))
|
||||||
(def resize-nwse (cursor-fn :resize-h 135))
|
(def resize-nwse (cursor-fn :resize-h 135))
|
||||||
|
|
||||||
(def resize-ew (cursor-fn :resize-h 0))
|
(def resize-ew (cursor-fn :resize-h 0))
|
||||||
(def resize-ns (cursor-fn :resize-h 90))
|
(def resize-ns (cursor-fn :resize-h 90))
|
||||||
|
|
||||||
(def rotate (cursor-fn :rotate 90))
|
(def rotate (cursor-fn :rotate 90))
|
||||||
(def text (cursor-ref :text))
|
(def text (cursor-ref :text))
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
[uxbox.main.refs :as refs]
|
[uxbox.main.refs :as refs]
|
||||||
[uxbox.main.store :as st]
|
[uxbox.main.store :as st]
|
||||||
[uxbox.main.streams :as ms]
|
[uxbox.main.streams :as ms]
|
||||||
|
[uxbox.main.ui.cursors :as cur]
|
||||||
[uxbox.util.dom :as dom]
|
[uxbox.util.dom :as dom]
|
||||||
[uxbox.util.object :as obj]
|
[uxbox.util.object :as obj]
|
||||||
[uxbox.common.geom.shapes :as geom]
|
[uxbox.common.geom.shapes :as geom]
|
||||||
|
@ -26,8 +27,6 @@
|
||||||
[uxbox.common.geom.matrix :as gmt]
|
[uxbox.common.geom.matrix :as gmt]
|
||||||
[uxbox.util.debug :refer [debug?]]))
|
[uxbox.util.debug :refer [debug?]]))
|
||||||
|
|
||||||
(defn rotation-cursor [angle]
|
|
||||||
(str "url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20px' height='20px' transform='rotate(" angle ")' viewBox='0 0 132.292 132.006'%3E%3Cpath d='M85.225 3.48c.034 4.989-.093 9.852-.533 14.78-29.218 5.971-54.975 27.9-63.682 56.683-1.51 2.923-1.431 7.632-3.617 9.546-5.825.472-11.544.5-17.393.45 11.047 15.332 20.241 32.328 32.296 46.725 5.632 1.855 7.155-5.529 10.066-8.533 8.12-12.425 17.252-24.318 24.269-37.482-6.25-.86-12.564-.88-18.857-1.057 5.068-17.605 19.763-31.81 37.091-37.122.181 6.402.206 12.825 1.065 19.184 15.838-9.05 30.899-19.617 45.601-30.257 2.985-4.77-3.574-7.681-6.592-9.791C111.753 17.676 98.475 8.889 85.23.046l-.005 3.435z'/%3E%3Cpath fill='%23fff' d='M92.478 23.995s-1.143.906-6.714 1.923c-29.356 5.924-54.352 30.23-59.717 59.973-.605 3.728-1.09 5.49-1.09 5.49l-11.483-.002s7.84 10.845 10.438 15.486c3.333 4.988 6.674 9.971 10.076 14.912a2266.92 2266.92 0 0019.723-29.326c-5.175-.16-10.35-.343-15.522-.572 3.584-27.315 26.742-50.186 53.91-54.096.306 5.297.472 10.628.631 15.91a2206.462 2206.462 0 0029.333-19.726c-9.75-6.7-19.63-13.524-29.483-20.12z'/%3E%3C/svg%3E\") 10 10, auto"))
|
|
||||||
|
|
||||||
(def rotation-handler-size 25)
|
(def rotation-handler-size 25)
|
||||||
(def resize-point-radius 4)
|
(def resize-point-radius 4)
|
||||||
|
@ -115,7 +114,7 @@
|
||||||
:top-right 90
|
:top-right 90
|
||||||
:bottom-right 180
|
:bottom-right 180
|
||||||
:bottom-left 270)]
|
:bottom-left 270)]
|
||||||
[:rect {:style {:cursor (rotation-cursor (+ rotation angle))}
|
[:rect {:style {:cursor (cur/rotate (+ rotation angle))}
|
||||||
:x x
|
:x x
|
||||||
:y y
|
:y y
|
||||||
:width size
|
:width size
|
||||||
|
@ -124,7 +123,7 @@
|
||||||
:transform transform
|
:transform transform
|
||||||
:on-mouse-down on-rotate}]))
|
:on-mouse-down on-rotate}]))
|
||||||
|
|
||||||
(mf/defc resize-point-handler [{:keys [cx cy zoom position on-resize transform]}]
|
(mf/defc resize-point-handler [{:keys [cx cy zoom position on-resize transform rotation]}]
|
||||||
(let [{cx' :x cy' :y} (gpt/transform (gpt/point cx cy) transform)
|
(let [{cx' :x cy' :y} (gpt/transform (gpt/point cx cy) transform)
|
||||||
rot-square (case position
|
rot-square (case position
|
||||||
:top-left 0
|
:top-left 0
|
||||||
|
@ -132,34 +131,36 @@
|
||||||
:bottom-right 180
|
:bottom-right 180
|
||||||
:bottom-left 270)]
|
:bottom-left 270)]
|
||||||
[:g.resize-handler
|
[:g.resize-handler
|
||||||
[:circle {:class (name position)
|
[:circle {:r (/ resize-point-radius zoom)
|
||||||
:r (/ resize-point-radius zoom)
|
|
||||||
:style {:fillOpacity "1"
|
:style {:fillOpacity "1"
|
||||||
:strokeWidth "1px"
|
:strokeWidth "1px"
|
||||||
:vectorEffect "non-scaling-stroke"}
|
:vectorEffect "non-scaling-stroke"
|
||||||
|
}
|
||||||
:fill "#FFFFFF"
|
:fill "#FFFFFF"
|
||||||
:stroke "#1FDEA7"
|
:stroke "#1FDEA7"
|
||||||
:cx cx'
|
:cx cx'
|
||||||
:cy cy'}]
|
:cy cy'}]
|
||||||
|
|
||||||
[:rect {:class (name position)
|
[:rect {:x cx
|
||||||
:x cx
|
|
||||||
:y cy
|
:y cy
|
||||||
:width (/ resize-point-rect-size zoom)
|
:width (/ resize-point-rect-size zoom)
|
||||||
:height (/ resize-point-rect-size zoom)
|
:height (/ resize-point-rect-size zoom)
|
||||||
:fill (if (debug? :resize-handler) "red" "transparent")
|
:fill (if (debug? :resize-handler) "red" "transparent")
|
||||||
:on-mouse-down on-resize
|
:on-mouse-down on-resize
|
||||||
|
:style {:cursor (if (#{:top-left :bottom-right} position)
|
||||||
|
(cur/resize-nesw rotation) (cur/resize-nwse rotation))}
|
||||||
:transform (gmt/multiply transform
|
:transform (gmt/multiply transform
|
||||||
(gmt/rotate-matrix rot-square (gpt/point cx cy)))}]
|
(gmt/rotate-matrix rot-square (gpt/point cx cy)))}]
|
||||||
[:circle {:class (name position)
|
[:circle {:on-mouse-down on-resize
|
||||||
:on-mouse-down on-resize
|
|
||||||
:r (/ resize-point-circle-radius zoom)
|
:r (/ resize-point-circle-radius zoom)
|
||||||
:fill (if (debug? :resize-handler) "red" "transparent")
|
:fill (if (debug? :resize-handler) "red" "transparent")
|
||||||
:cx cx'
|
:cx cx'
|
||||||
:cy cy'}]
|
:cy cy'
|
||||||
|
:style {:cursor (if (#{:top-left :bottom-right} position)
|
||||||
|
(cur/resize-nesw rotation) (cur/resize-nwse rotation))}}]
|
||||||
]))
|
]))
|
||||||
|
|
||||||
(mf/defc resize-side-handler [{:keys [x y length angle zoom position transform on-resize]}]
|
(mf/defc resize-side-handler [{:keys [x y length angle zoom position rotation transform on-resize]}]
|
||||||
[:rect {:x (+ x (/ resize-point-rect-size zoom))
|
[:rect {:x (+ x (/ resize-point-rect-size zoom))
|
||||||
:y (- y (/ resize-side-height 2 zoom))
|
:y (- y (/ resize-side-height 2 zoom))
|
||||||
:width (max 0 (- length (/ (* resize-point-rect-size 2) zoom)))
|
:width (max 0 (- length (/ (* resize-point-rect-size 2) zoom)))
|
||||||
|
@ -169,8 +170,8 @@
|
||||||
:on-mouse-down on-resize
|
:on-mouse-down on-resize
|
||||||
:style {:fill (if (debug? :resize-handler) "yellow" "transparent")
|
:style {:fill (if (debug? :resize-handler) "yellow" "transparent")
|
||||||
:cursor (if (#{:left :right} position)
|
:cursor (if (#{:left :right} position)
|
||||||
"ew-resize"
|
(cur/resize-ew rotation)
|
||||||
"ns-resize") }}])
|
(cur/resize-ns rotation)) }}])
|
||||||
|
|
||||||
(mf/defc controls
|
(mf/defc controls
|
||||||
{::mf/wrap-props false}
|
{::mf/wrap-props false}
|
||||||
|
@ -244,7 +245,7 @@
|
||||||
:on-mouse-down #(on-mouse-down % index)
|
:on-mouse-down #(on-mouse-down % index)
|
||||||
:fill "#ffffff"
|
:fill "#ffffff"
|
||||||
:stroke "#1FDEA7"
|
:stroke "#1FDEA7"
|
||||||
:style {:cursor "pointer"}}]))])))
|
:style {:cursor cur/move-pointer}}]))])))
|
||||||
|
|
||||||
;; TODO: add specs for clarity
|
;; TODO: add specs for clarity
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,7 @@
|
||||||
(when-not (or (empty? selected) (kbd/shift? event))
|
(when-not (or (empty? selected) (kbd/shift? event))
|
||||||
(st/emit! dw/deselect-all))
|
(st/emit! dw/deselect-all))
|
||||||
(st/emit! (dw/select-shape id))))
|
(st/emit! (dw/select-shape id))))
|
||||||
|
|
||||||
(st/emit! (dw/start-move-selected)))))))
|
(st/emit! (dw/start-move-selected)))))))
|
||||||
|
|
||||||
(defn on-context-menu
|
(defn on-context-menu
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
[uxbox.main.data.workspace.texts :as dwt]
|
[uxbox.main.data.workspace.texts :as dwt]
|
||||||
[uxbox.main.refs :as refs]
|
[uxbox.main.refs :as refs]
|
||||||
[uxbox.main.store :as st]
|
[uxbox.main.store :as st]
|
||||||
|
[uxbox.main.ui.cursors :as cur]
|
||||||
[uxbox.main.ui.workspace.shapes.common :as common]
|
[uxbox.main.ui.workspace.shapes.common :as common]
|
||||||
[uxbox.main.ui.shapes.text :as text]
|
[uxbox.main.ui.shapes.text :as text]
|
||||||
[uxbox.main.ui.keyboard :as kbd]
|
[uxbox.main.ui.keyboard :as kbd]
|
||||||
|
@ -301,6 +302,7 @@
|
||||||
:spell-check "false"
|
:spell-check "false"
|
||||||
:on-focus on-focus
|
:on-focus on-focus
|
||||||
:class "rich-text"
|
:class "rich-text"
|
||||||
|
:style {:cursor cur/text}
|
||||||
:render-element render-element
|
:render-element render-element
|
||||||
:render-leaf render-text
|
:render-leaf render-text
|
||||||
:on-mouse-up on-mouse-up
|
:on-mouse-up on-mouse-up
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
[potok.core :as ptk]
|
[potok.core :as ptk]
|
||||||
[rumext.alpha :as mf]
|
[rumext.alpha :as mf]
|
||||||
[uxbox.main.ui.icons :as i]
|
[uxbox.main.ui.icons :as i]
|
||||||
|
[uxbox.main.ui.cursors :as cur]
|
||||||
[uxbox.common.data :as d]
|
[uxbox.common.data :as d]
|
||||||
[uxbox.main.constants :as c]
|
[uxbox.main.constants :as c]
|
||||||
[uxbox.main.data.workspace :as dw]
|
[uxbox.main.data.workspace :as dw]
|
||||||
|
@ -96,6 +97,7 @@
|
||||||
|
|
||||||
stream (->> ms/mouse-position-delta
|
stream (->> ms/mouse-position-delta
|
||||||
(rx/take-until stoper))]
|
(rx/take-until stoper))]
|
||||||
|
(st/emit! dw/start-pan)
|
||||||
(rx/subscribe stream
|
(rx/subscribe stream
|
||||||
(fn [delta]
|
(fn [delta]
|
||||||
(let [vbox (.. ^js node -viewBox -baseVal)
|
(let [vbox (.. ^js node -viewBox -baseVal)
|
||||||
|
@ -136,7 +138,8 @@
|
||||||
vbox
|
vbox
|
||||||
edition
|
edition
|
||||||
tooltip
|
tooltip
|
||||||
selected]} local
|
selected
|
||||||
|
panning]} local
|
||||||
|
|
||||||
viewport-ref (mf/use-ref nil)
|
viewport-ref (mf/use-ref nil)
|
||||||
last-position (mf/use-var nil)
|
last-position (mf/use-var nil)
|
||||||
|
@ -185,7 +188,8 @@
|
||||||
(st/emit! (ms/->MouseEvent :up ctrl? shift?))
|
(st/emit! (ms/->MouseEvent :up ctrl? shift?))
|
||||||
|
|
||||||
(when (= 2 (.-which event))
|
(when (= 2 (.-which event))
|
||||||
(st/emit! ::finish-positioning)))))
|
(st/emit! dw/finish-pan
|
||||||
|
::finish-positioning)))))
|
||||||
|
|
||||||
on-pointer-down
|
on-pointer-down
|
||||||
(mf/use-callback
|
(mf/use-callback
|
||||||
|
@ -251,7 +255,8 @@
|
||||||
:shift? shift?
|
:shift? shift?
|
||||||
:ctrl? ctrl?}]
|
:ctrl? ctrl?}]
|
||||||
(when (kbd/space? event)
|
(when (kbd/space? event)
|
||||||
(st/emit! ::finish-positioning))
|
(st/emit! dw/finish-pan
|
||||||
|
::finish-positioning))
|
||||||
(st/emit! (ms/->KeyboardEvent :up key ctrl? shift?)))))
|
(st/emit! (ms/->KeyboardEvent :up key ctrl? shift?)))))
|
||||||
|
|
||||||
translate-point-to-viewport
|
translate-point-to-viewport
|
||||||
|
@ -365,6 +370,15 @@
|
||||||
(:height vbox 0)])
|
(:height vbox 0)])
|
||||||
:ref viewport-ref
|
:ref viewport-ref
|
||||||
:class (when drawing-tool "drawing")
|
:class (when drawing-tool "drawing")
|
||||||
|
:style {:cursor (cond
|
||||||
|
panning cur/hand
|
||||||
|
(= drawing-tool :frame) cur/create-artboard
|
||||||
|
(= drawing-tool :rect) cur/create-rectangle
|
||||||
|
(= drawing-tool :circle) cur/create-ellipse
|
||||||
|
(= drawing-tool :path) cur/pen
|
||||||
|
(= drawing-tool :curve)cur/pencil
|
||||||
|
drawing-tool cur/create-shape
|
||||||
|
:else cur/pointer-inner)}
|
||||||
:on-context-menu on-context-menu
|
:on-context-menu on-context-menu
|
||||||
:on-click on-click
|
:on-click on-click
|
||||||
:on-double-click on-double-click
|
:on-double-click on-double-click
|
||||||
|
|
|
@ -71,3 +71,4 @@
|
||||||
[index {:keys [id x y width height] :as obj}]
|
[index {:keys [id x y width height] :as obj}]
|
||||||
(let [rect #js {:x x :y y :width width :height height}]
|
(let [rect #js {:x x :y y :width width :height height}]
|
||||||
(qdt/insert index rect obj)))
|
(qdt/insert index rect obj)))
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue