diff --git a/CHANGES.md b/CHANGES.md
index 108078dca6..bcfe100841 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -22,6 +22,7 @@
- Add update components in bulk option in context menu [Taiga #1975](https://tree.taiga.io/project/penpot/us/1975)
- Create first E2E tests [Taiga #2608](https://tree.taiga.io/project/penpot/task/2608), [Taiga #2608](https://tree.taiga.io/project/penpot/task/2608)
- Redesign of workspace toolbars [Taiga #2319](https://tree.taiga.io/project/penpot/us/2319)
+- Graphic Tablet usability improvements [Taiga #1913](https://tree.taiga.io/project/penpot/us/1913)
### :bug: Bugs fixed
diff --git a/frontend/resources/images/cursors/zoom-in.svg b/frontend/resources/images/cursors/zoom-in.svg
new file mode 100644
index 0000000000..ecd153448a
--- /dev/null
+++ b/frontend/resources/images/cursors/zoom-in.svg
@@ -0,0 +1 @@
+
diff --git a/frontend/resources/images/cursors/zoom-out.svg b/frontend/resources/images/cursors/zoom-out.svg
new file mode 100644
index 0000000000..65a5a300f2
--- /dev/null
+++ b/frontend/resources/images/cursors/zoom-out.svg
@@ -0,0 +1 @@
+
diff --git a/frontend/resources/images/cursors/zoom.svg b/frontend/resources/images/cursors/zoom.svg
new file mode 100644
index 0000000000..9650153fef
--- /dev/null
+++ b/frontend/resources/images/cursors/zoom.svg
@@ -0,0 +1 @@
+
diff --git a/frontend/resources/styles/main/partials/debug-icons-preview.scss b/frontend/resources/styles/main/partials/debug-icons-preview.scss
index 8ddba1688c..afca851233 100644
--- a/frontend/resources/styles/main/partials/debug-icons-preview.scss
+++ b/frontend/resources/styles/main/partials/debug-icons-preview.scss
@@ -2,7 +2,9 @@
display: flex;
flex-direction: column;
overflow: scroll;
+ height: 100%;
}
+
.debug-icons-preview {
display: flex;
flex-wrap: wrap;
diff --git a/frontend/src/app/main/data/dashboard.cljs b/frontend/src/app/main/data/dashboard.cljs
index c986116f83..6e34b0f5d9 100644
--- a/frontend/src/app/main/data/dashboard.cljs
+++ b/frontend/src/app/main/data/dashboard.cljs
@@ -834,4 +834,15 @@
action (if in-project? file-created project-created)]
(->> (rp/mutation! action-name params)
- (rx/map action))))))
\ No newline at end of file
+ (rx/map action))))))
+
+(defn open-selected-file
+ []
+ (ptk/reify ::open-selected-file
+ ptk/WatchEvent
+ (watch [_ state _]
+ (let [files (get-in state [:dashboard-local :selected-files])]
+ (if (= 1 (count files))
+ (let [file (get-in state [:dashboard-files (first files)])]
+ (rx/of (go-to-workspace file)))
+ (rx/empty))))))
diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs
index d36dde0638..21fb41f395 100644
--- a/frontend/src/app/main/data/workspace.cljs
+++ b/frontend/src/app/main/data/workspace.cljs
@@ -196,7 +196,7 @@
(->> stream
(rx/filter #(= ::dwc/index-initialized %))
- (rx/first)
+ (rx/take 1)
(rx/map #(file-initialized bundle)))))))))
ptk/EffectEvent
@@ -1636,7 +1636,7 @@
(->> (rx/concat paste-transit-str
paste-plain-text-str
paste-image-str)
- (rx/first)
+ (rx/take 1)
(rx/catch
(fn [err]
(js/console.error "Clipboard error:" err)
diff --git a/frontend/src/app/main/data/workspace/colors.cljs b/frontend/src/app/main/data/workspace/colors.cljs
index 017cc967d6..0cb4eca12d 100644
--- a/frontend/src/app/main/data/workspace/colors.cljs
+++ b/frontend/src/app/main/data/workspace/colors.cljs
@@ -206,11 +206,9 @@
stop? (rx/filter (ptk/type? ::stop-picker) stream)
update-events
- (fn [[color shift?]]
- (rx/of (if shift?
- (change-stroke ids color)
- (change-fill ids color))
- (stop-picker)))]
+ (fn [color]
+ (rx/of (change-fill ids color)))]
+
(rx/merge
;; Stream that updates the stroke/width and stops if `esc` pressed
(->> sub
@@ -219,12 +217,12 @@
;; Hide the modal if the stop event is emitted
(->> stop?
- (rx/first)
+ (rx/take 1)
(rx/map #(md/hide))))))
ptk/UpdateEvent
(update [_ state]
- (let [handle-change-color (fn [color shift?] (rx/push! sub [color shift?]))]
+ (let [handle-change-color (fn [color] (rx/push! sub color))]
(-> state
(assoc-in [:workspace-local :picking-color?] true)
(assoc ::md/modal {:id (random-uuid)
diff --git a/frontend/src/app/main/data/workspace/common.cljs b/frontend/src/app/main/data/workspace/common.cljs
index 21406795d8..a58bf8aa5f 100644
--- a/frontend/src/app/main/data/workspace/common.cljs
+++ b/frontend/src/app/main/data/workspace/common.cljs
@@ -314,37 +314,41 @@
[redo-changes undo-changes])))
(defn add-shape
- [attrs]
- (us/verify ::shape-attrs attrs)
- (ptk/reify ::add-shape
- ptk/WatchEvent
- (watch [it state _]
- (let [page-id (:current-page-id state)
- objects (wsh/lookup-page-objects state page-id)
+ ([attrs]
+ (add-shape attrs {}))
- id (or (:id attrs) (uuid/next))
- name (-> objects
- (retrieve-used-names)
- (generate-unique-name (:name attrs)))
+ ([attrs {:keys [no-select?]}]
+ (us/verify ::shape-attrs attrs)
+ (ptk/reify ::add-shape
+ ptk/WatchEvent
+ (watch [it state _]
+ (let [page-id (:current-page-id state)
+ objects (wsh/lookup-page-objects state page-id)
- selected (wsh/lookup-selected state)
+ id (or (:id attrs) (uuid/next))
+ name (-> objects
+ (retrieve-used-names)
+ (generate-unique-name (:name attrs)))
- [rchanges uchanges] (add-shape-changes
- page-id
- objects
- selected
- (-> attrs
- (assoc :id id )
- (assoc :name name)))]
+ selected (wsh/lookup-selected state)
- (rx/concat
- (rx/of (dch/commit-changes {:redo-changes rchanges
- :undo-changes uchanges
- :origin it})
- (select-shapes (d/ordered-set id)))
- (when (= :text (:type attrs))
- (->> (rx/of (start-edition-mode id))
- (rx/observe-on :async))))))))
+ [rchanges uchanges] (add-shape-changes
+ page-id
+ objects
+ selected
+ (-> attrs
+ (assoc :id id )
+ (assoc :name name)))]
+
+ (rx/concat
+ (rx/of (dch/commit-changes {:redo-changes rchanges
+ :undo-changes uchanges
+ :origin it})
+ (when-not no-select?
+ (select-shapes (d/ordered-set id))))
+ (when (= :text (:type attrs))
+ (->> (rx/of (start-edition-mode id))
+ (rx/observe-on :async)))))))))
(defn move-shapes-into-frame [frame-id shapes]
(ptk/reify ::move-shapes-into-frame
diff --git a/frontend/src/app/main/data/workspace/drawing.cljs b/frontend/src/app/main/data/workspace/drawing.cljs
index 14ec47a2ac..c333a7a293 100644
--- a/frontend/src/app/main/data/workspace/drawing.cljs
+++ b/frontend/src/app/main/data/workspace/drawing.cljs
@@ -42,6 +42,15 @@
(when (= tool :path)
(rx/of (start-drawing :path)))
+ (when (= tool :curve)
+ (let [stopper (->> stream (rx/filter dwc/interrupt?))]
+ (->> stream
+ (rx/filter (ptk/type? ::common/handle-finish-drawing))
+ (rx/take 1)
+ (rx/observe-on :async)
+ (rx/map #(select-for-drawing tool data))
+ (rx/take-until stopper))))
+
;; NOTE: comments are a special case and they manage they
;; own interrupt cycle.q
(when (and (not= tool :comments)
@@ -74,7 +83,7 @@
(rx/of (handle-drawing type))
(->> stream
(rx/filter (ptk/type? ::common/handle-finish-drawing) )
- (rx/first)
+ (rx/take 1)
(rx/map #(fn [state] (update state :workspace-drawing dissoc :lock)))))))))))
(defn handle-drawing
diff --git a/frontend/src/app/main/data/workspace/drawing/common.cljs b/frontend/src/app/main/data/workspace/drawing/common.cljs
index fb93c6f5cc..624bbf3084 100644
--- a/frontend/src/app/main/data/workspace/drawing/common.cljs
+++ b/frontend/src/app/main/data/workspace/drawing/common.cljs
@@ -24,7 +24,8 @@
(ptk/reify ::handle-finish-drawing
ptk/WatchEvent
(watch [_ state _]
- (let [shape (get-in state [:workspace-drawing :object])]
+ (let [tool (get-in state [:workspace-drawing :tool])
+ shape (get-in state [:workspace-drawing :object])]
(rx/concat
(when (:initialized? shape)
(let [page-id (:current-page-id state)
@@ -55,7 +56,7 @@
(rx/of (dwu/start-undo-transaction))
(rx/empty))
- (rx/of (dwc/add-shape shape))
+ (rx/of (dwc/add-shape shape {:no-select? (= tool :curve)}))
(if (= :frame (:type shape))
(->> (uw/ask! {:cmd :selection/query
diff --git a/frontend/src/app/main/data/workspace/layers.cljs b/frontend/src/app/main/data/workspace/layers.cljs
index 4344e13e89..0f06eac314 100644
--- a/frontend/src/app/main/data/workspace/layers.cljs
+++ b/frontend/src/app/main/data/workspace/layers.cljs
@@ -70,7 +70,7 @@
(let [opacity-events (->> stream ;; Stop buffering after time without opacities
(rx/filter (ptk/type? ::pressed-opacity))
(rx/buffer-time 600)
- (rx/first)
+ (rx/take 1)
(rx/map #(set-opacity (calculate-opacity (map deref %)))))]
(rx/concat
(rx/of (set-opacity (calculate-opacity [opacity]))) ;; First opacity is always fired
diff --git a/frontend/src/app/main/data/workspace/selection.cljs b/frontend/src/app/main/data/workspace/selection.cljs
index f6bd9a518f..916103b03a 100644
--- a/frontend/src/app/main/data/workspace/selection.cljs
+++ b/frontend/src/app/main/data/workspace/selection.cljs
@@ -9,7 +9,6 @@
[app.common.data :as d]
[app.common.geom.point :as gpt]
[app.common.geom.shapes :as geom]
- [app.common.math :as mth]
[app.common.pages :as cp]
[app.common.spec :as us]
[app.common.spec.interactions :as cti]
@@ -47,51 +46,58 @@
(assoc-in state [:workspace-local :selrect] selrect))))
(defn handle-area-selection
- [preserve?]
- (letfn [(data->selrect [data]
- (let [start (:start data)
- stop (:stop data)
- start-x (min (:x start) (:x stop))
- start-y (min (:y start) (:y stop))
- end-x (max (:x start) (:x stop))
- end-y (max (:y start) (:y stop))]
- {:type :rect
- :x start-x
- :y start-y
- :width (mth/abs (- end-x start-x))
- :height (mth/abs (- end-y start-y))}))]
- (ptk/reify ::handle-area-selection
- ptk/WatchEvent
- (watch [_ state stream]
- (let [zoom (get-in state [:workspace-local :zoom] 1)
- stop? (fn [event] (or (dwc/interrupt? event) (ms/mouse-up? event)))
- stoper (->> stream (rx/filter stop?))
+ [preserve? ignore-groups?]
+ (ptk/reify ::handle-area-selection
+ ptk/WatchEvent
+ (watch [_ state stream]
+ (let [zoom (get-in state [:workspace-local :zoom] 1)
+ stop? (fn [event] (or (dwc/interrupt? event) (ms/mouse-up? event)))
+ stoper (->> stream (rx/filter stop?))
- calculate-selrect
- (fn [data pos]
- (if data
- (assoc data :stop pos)
- {:start pos :stop pos}))
+ init-selrect
+ {:type :rect
+ :x (:x @ms/mouse-position)
+ :y (:y @ms/mouse-position)
+ :width 0
+ :height 0}
- selrect-stream
- (->> ms/mouse-position
- (rx/scan calculate-selrect nil)
- (rx/map data->selrect)
- (rx/filter #(or (> (:width %) (/ 10 zoom))
- (> (:height %) (/ 10 zoom))))
- (rx/take-until stoper))]
- (rx/concat
- (if preserve?
- (rx/empty)
- (rx/of (deselect-all)))
+ calculate-selrect
+ (fn [selrect [delta space?]]
+ (if space?
+ (-> selrect
+ (update :x + (:x delta))
+ (update :y + (:y delta)))
- (rx/merge
- (->> selrect-stream (rx/map update-selrect))
- (->> selrect-stream
- (rx/debounce 50)
- (rx/map #(select-shapes-by-current-selrect preserve?))))
+ (-> selrect
+ (update :width + (:x delta))
+ (update :height + (:y delta)))))
- (rx/of (update-selrect nil))))))))
+ selrect-stream
+ (->> ms/mouse-position
+ (rx/buffer 2 1)
+ (rx/map (fn [[from to]] (when (and from to) (gpt/to-vec from to))))
+ (rx/filter some?)
+ (rx/with-latest-from ms/keyboard-space)
+ (rx/scan calculate-selrect init-selrect)
+ (rx/filter #(or (> (:width %) (/ 10 zoom))
+ (> (:height %) (/ 10 zoom))))
+ (rx/take-until stoper))]
+ (rx/concat
+ (if preserve?
+ (rx/empty)
+ (rx/of (deselect-all)))
+
+ (rx/merge
+ (->> selrect-stream
+ (rx/map update-selrect))
+
+ (->> selrect-stream
+ (rx/buffer-time 100)
+ (rx/map #(last %))
+ (rx/dedupe)
+ (rx/map #(select-shapes-by-current-selrect preserve? ignore-groups?))))
+
+ (rx/of (update-selrect nil)))))))
;; --- Toggle shape's selection status (selected or deselected)
@@ -216,7 +222,7 @@
;; --- Select Shapes (By selrect)
(defn select-shapes-by-current-selrect
- [preserve?]
+ [preserve? ignore-groups?]
(ptk/reify ::select-shapes-by-current-selrect
ptk/WatchEvent
(watch [_ state _]
@@ -235,6 +241,7 @@
:page-id page-id
:rect selrect
:include-frames? true
+ :ignore-groups? ignore-groups?
:full-frame? true})
(rx/map #(cp/clean-loops objects %))
(rx/map #(into initial-set (filter (comp not blocked?)) %))
diff --git a/frontend/src/app/main/data/workspace/shortcuts.cljs b/frontend/src/app/main/data/workspace/shortcuts.cljs
index 44667db499..ec42b727ae 100644
--- a/frontend/src/app/main/data/workspace/shortcuts.cljs
+++ b/frontend/src/app/main/data/workspace/shortcuts.cljs
@@ -80,7 +80,7 @@
:fn #(st/emit! (dw/toggle-layout-flags :scale-text))}
:increase-zoom {:tooltip "+"
- :command "+"
+ :command ["+" "="]
:fn #(st/emit! (dw/increase-zoom nil))}
:decrease-zoom {:tooltip "-"
diff --git a/frontend/src/app/main/data/workspace/transforms.cljs b/frontend/src/app/main/data/workspace/transforms.cljs
index 559895ddd2..1d343f035d 100644
--- a/frontend/src/app/main/data/workspace/transforms.cljs
+++ b/frontend/src/app/main/data/workspace/transforms.cljs
@@ -523,7 +523,7 @@
(watch [_ _ stream]
(->> stream
(rx/filter (ptk/type? ::dws/duplicate-selected))
- (rx/first)
+ (rx/take 1)
(rx/map #(start-move from-position))))))
(defn- start-move
@@ -612,7 +612,7 @@
(rx/filter #(= direction (deref %))))
stopper (->> move-events
(rx/debounce 100)
- (rx/first))
+ (rx/take 1))
scale (if shift? (gpt/point (:big nudge)) (gpt/point (:small nudge)))
mov-vec (gpt/multiply (get-displacement direction) scale)]
diff --git a/frontend/src/app/main/snap.cljs b/frontend/src/app/main/snap.cljs
index 1de5b1aabc..3da469e5df 100644
--- a/frontend/src/app/main/snap.cljs
+++ b/frontend/src/app/main/snap.cljs
@@ -82,7 +82,7 @@
:frame-id frame-id
:axis coord
:ranges [[(- value 0.5) (+ value 0.5)]]})
- (rx/first)
+ (rx/take 1)
(rx/map (remove-from-snap-points remove-snap?))
(rx/map flatten-to-points))))
@@ -98,7 +98,7 @@
:frame-id frame-id
:axis coord
:ranges ranges})
- (rx/first)
+ (rx/take 1)
(rx/map (remove-from-snap-points remove-snap?))
(rx/map (get-min-distance-snap points coord)))))
diff --git a/frontend/src/app/main/ui/cursors.cljs b/frontend/src/app/main/ui/cursors.cljs
index a575750997..2d571ed06e 100644
--- a/frontend/src/app/main/ui/cursors.cljs
+++ b/frontend/src/app/main/ui/cursors.cljs
@@ -30,6 +30,9 @@
(def pointer-node (cursor-ref :pointer-node 0 0 10 32))
(def resize-alt (cursor-ref :resize-alt))
(def text (cursor-ref :text))
+(def zoom (cursor-ref :zoom))
+(def zoom-in (cursor-ref :zoom-in))
+(def zoom-out (cursor-ref :zoom-out))
;; Dynamic cursors
(def resize-ew (cursor-fn :resize-h 0))
diff --git a/frontend/src/app/main/ui/dashboard.cljs b/frontend/src/app/main/ui/dashboard.cljs
index d27bca6ba8..a377a70df8 100644
--- a/frontend/src/app/main/ui/dashboard.cljs
+++ b/frontend/src/app/main/ui/dashboard.cljs
@@ -22,7 +22,10 @@
[app.main.ui.dashboard.sidebar :refer [sidebar]]
[app.main.ui.dashboard.team :refer [team-settings-page team-members-page]]
[app.main.ui.hooks :as hooks]
- [rumext.alpha :as mf]))
+ [app.util.keyboard :as kbd]
+ [goog.events :as events]
+ [rumext.alpha :as mf])
+ (:import goog.events.EventType))
(defn ^boolean uuid-str?
[s]
@@ -95,6 +98,16 @@
(mf/with-effect [team-id]
(st/emit! (dd/initialize {:id team-id})))
+ (mf/use-effect
+ (fn []
+ (let [events [(events/listen goog/global EventType.KEYDOWN
+ (fn [event]
+ (when (kbd/enter? event)
+ (st/emit! (dd/open-selected-file)))))]]
+ (fn []
+ (doseq [key events]
+ (events/unlistenByKey key))))))
+
[:& (mf/provider ctx/current-team-id) {:value team-id}
[:& (mf/provider ctx/current-project-id) {:value project-id}
;; NOTE: dashboard events and other related functions assumes
diff --git a/frontend/src/app/main/ui/workspace/colorpicker.cljs b/frontend/src/app/main/ui/workspace/colorpicker.cljs
index 2b34513072..1b5046eeb7 100644
--- a/frontend/src/app/main/ui/workspace/colorpicker.cljs
+++ b/frontend/src/app/main/ui/workspace/colorpicker.cljs
@@ -226,9 +226,9 @@
;; Updates color when used el pixel picker
(mf/use-effect
- (mf/deps picking-color? picked-color-select)
+ (mf/deps picking-color? picked-color picked-color-select)
(fn []
- (when (and picking-color? picked-color-select)
+ (when (and picking-color? picked-color picked-color-select)
(let [[r g b alpha] picked-color
hex (uc/rgb->hex [r g b])
[h s v] (uc/hex->hsv hex)]
diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs
index f7e126c705..6ad85d7fe0 100644
--- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs
+++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs
@@ -77,13 +77,11 @@
update-color
(fn [index]
- (fn [color opacity]
+ (fn [color]
(let [color (dissoc color :id :file-id :gradient)]
(st/emit! (dch/update-shapes
ids
- #(-> %
- (assoc-in [:shadow index :color] color)
- (assoc-in [:shadow index :opacity] opacity)))))))
+ #(assoc-in % [:shadow index :color] color))))))
detach-color
(fn [index]
diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs
index 35e8f44b12..16aff53f2c 100644
--- a/frontend/src/app/main/ui/workspace/viewport.cljs
+++ b/frontend/src/app/main/ui/workspace/viewport.cljs
@@ -115,21 +115,21 @@
node-editing? (and edition (not= :text (get-in base-objects [edition :type])))
text-editing? (and edition (= :text (get-in base-objects [edition :type])))
- on-click (actions/on-click hover selected edition drawing-path? drawing-tool)
+ on-click (actions/on-click hover selected edition drawing-path? drawing-tool space?)
on-context-menu (actions/on-context-menu hover hover-ids)
on-double-click (actions/on-double-click hover hover-ids drawing-path? base-objects edition)
on-drag-enter (actions/on-drag-enter)
on-drag-over (actions/on-drag-over)
on-drop (actions/on-drop file viewport-ref zoom)
on-mouse-down (actions/on-mouse-down @hover selected edition drawing-tool text-editing? node-editing?
- drawing-path? create-comment? space? viewport-ref zoom)
+ drawing-path? create-comment? space? viewport-ref zoom panning)
on-mouse-up (actions/on-mouse-up disable-paste)
on-pointer-down (actions/on-pointer-down)
on-pointer-enter (actions/on-pointer-enter in-viewport?)
on-pointer-leave (actions/on-pointer-leave in-viewport?)
on-pointer-move (actions/on-pointer-move viewport-ref zoom move-stream)
on-pointer-up (actions/on-pointer-up)
- on-move-selected (actions/on-move-selected hover hover-ids selected)
+ on-move-selected (actions/on-move-selected hover hover-ids selected space?)
on-menu-selected (actions/on-menu-selected hover hover-ids selected)
on-frame-enter (actions/on-frame-enter frame-hover)
@@ -142,7 +142,7 @@
show-draw-area? drawing-obj
show-gradient-handlers? (= (count selected) 1)
show-grids? (contains? layout :display-grid)
- show-outlines? (and (nil? transform) (not edition) (not drawing-obj) (not (#{:comments :path} drawing-tool)))
+ show-outlines? (and (nil? transform) (not edition) (not drawing-obj) (not (#{:comments :path :curve} drawing-tool)))
show-pixel-grid? (>= zoom 8)
show-presence? page-id
show-prototypes? (= options-mode :prototype)
@@ -162,15 +162,13 @@
(hooks/setup-dom-events viewport-ref zoom disable-paste in-viewport?)
(hooks/setup-viewport-size viewport-ref)
- (hooks/setup-cursor cursor alt? panning drawing-tool drawing-path? node-editing?)
+ (hooks/setup-cursor cursor alt? ctrl? space? panning drawing-tool drawing-path? node-editing?)
(hooks/setup-keyboard alt? ctrl? space?)
(hooks/setup-hover-shapes page-id move-stream base-objects transform selected ctrl? hover hover-ids @hover-disabled? zoom)
(hooks/setup-viewport-modifiers modifiers base-objects)
(hooks/setup-shortcuts node-editing? drawing-path?)
(hooks/setup-active-frames base-objects vbox hover active-frames)
-
-
[:div.viewport
[:div.viewport-overlays
@@ -245,24 +243,18 @@
[:& outline/shape-outlines
{:objects base-objects
:selected selected
- :hover (when (not= :frame (:type @hover))
+ :hover (when (or @ctrl? (not= :frame (:type @hover)))
#{(or @frame-hover (:id @hover))})
:edition edition
:zoom zoom}])
- [:& scroll-bars/viewport-scrollbars
- {:objects base-objects
- :zoom zoom
- :vbox vbox
- :viewport-ref viewport-ref}]
-
(when show-selection-handlers?
[:& selection/selection-handlers
{:selected selected
:shapes selected-shapes
:zoom zoom
:edition edition
- :disable-handlers (or drawing-tool edition)
+ :disable-handlers (or drawing-tool edition @space?)
:on-move-selected on-move-selected
:on-context-menu on-menu-selected}])
@@ -363,6 +355,12 @@
[:& widgets/viewport-actions]
+ [:& scroll-bars/viewport-scrollbars
+ {:objects base-objects
+ :zoom zoom
+ :vbox vbox
+ :viewport-ref viewport-ref}]
+
(when show-rules?
[:*
[:& rules/rules
diff --git a/frontend/src/app/main/ui/workspace/viewport/actions.cljs b/frontend/src/app/main/ui/workspace/viewport/actions.cljs
index 4862883743..084f9ec8f4 100644
--- a/frontend/src/app/main/ui/workspace/viewport/actions.cljs
+++ b/frontend/src/app/main/ui/workspace/viewport/actions.cljs
@@ -28,10 +28,11 @@
(defn on-mouse-down
[{:keys [id blocked hidden type]} selected edition drawing-tool text-editing?
- node-editing? drawing-path? create-comment? space? viewport-ref zoom]
+ node-editing? drawing-path? create-comment? space? viewport-ref zoom panning]
(mf/use-callback
(mf/deps id blocked hidden type selected edition drawing-tool text-editing?
- node-editing? drawing-path? create-comment? space? viewport-ref zoom)
+ node-editing? drawing-path? create-comment? @space? viewport-ref zoom
+ panning)
(fn [bevent]
(when (or (dom/class? (dom/get-target bevent) "viewport-controls")
(dom/class? (dom/get-target bevent) "viewport-selrect"))
@@ -42,62 +43,75 @@
shift? (kbd/shift? event)
alt? (kbd/alt? event)
- left-click? (= 1 (.-which event))
- middle-click? (= 2 (.-which event))
+ left-click? (and (not panning) (= 1 (.-which event)))
+ middle-click? (and (not panning) (= 2 (.-which event)))
frame? (= :frame type)
selected? (contains? selected id)]
- (when middle-click?
- (dom/prevent-default bevent)
- (if ctrl?
- (let [raw-pt (dom/get-client-position event)
- viewport (mf/ref-val viewport-ref)
- pt (utils/translate-point-to-viewport viewport zoom raw-pt)]
- (st/emit! (dw/start-zooming pt)))
- (st/emit! (dw/start-panning))))
+ (cond
+ middle-click?
+ (do
+ (dom/prevent-default bevent)
+ (if ctrl?
+ (let [raw-pt (dom/get-client-position event)
+ viewport (mf/ref-val viewport-ref)
+ pt (utils/translate-point-to-viewport viewport zoom raw-pt)]
+ (st/emit! (dw/start-zooming pt)))
+ (st/emit! (dw/start-panning))))
- (when left-click?
- (st/emit! (ms/->MouseEvent :down ctrl? shift? alt?))
- (when (and (not= edition id) text-editing?)
- (st/emit! dw/clear-edition-mode))
+ left-click?
+ (do
+ (st/emit! (ms/->MouseEvent :down ctrl? shift? alt?))
- (when (and (not text-editing?)
- (not blocked)
- (not hidden)
- (not create-comment?)
- (not drawing-path?))
- (cond
- drawing-tool
- (st/emit! (dd/start-drawing drawing-tool))
+ (when (and (not= edition id) text-editing?)
+ (st/emit! dw/clear-edition-mode))
- node-editing?
- ;; Handle path node area selection
- (st/emit! (dwdp/handle-area-selection shift?))
+ (when (and (not text-editing?)
+ (not blocked)
+ (not hidden)
+ (not create-comment?)
+ (not drawing-path?))
+ (cond
+ node-editing?
+ ;; Handle path node area selection
+ (st/emit! (dwdp/handle-area-selection shift?))
- @space?
- (st/emit! (dw/start-panning))
+ (and @space? ctrl?)
+ (let [raw-pt (dom/get-client-position event)
+ viewport (mf/ref-val viewport-ref)
+ pt (utils/translate-point-to-viewport viewport zoom raw-pt)]
+ (st/emit! (dw/start-zooming pt)))
- (or (not id) (and frame? (not selected?)))
- (st/emit! (dw/handle-area-selection shift?))
+ @space?
+ (st/emit! (dw/start-panning))
- (not drawing-tool)
- (st/emit! (when (or shift? (not selected?))
- (dw/select-shape id shift?))
- (dw/start-move-selected))))))))))
+ drawing-tool
+ (st/emit! (dd/start-drawing drawing-tool))
+
+ (or (not id) (and frame? (not selected?)) ctrl?)
+ (st/emit! (dw/handle-area-selection shift? ctrl?))
+
+ (not drawing-tool)
+ (st/emit! (when (or shift? (not selected?))
+ (dw/select-shape id shift?))
+ (dw/start-move-selected)))))))))))
(defn on-move-selected
- [hover hover-ids selected]
+ [hover hover-ids selected space?]
(mf/use-callback
- (mf/deps @hover @hover-ids selected)
+ (mf/deps @hover @hover-ids selected @space?)
(fn [bevent]
(let [event (.-nativeEvent bevent)
shift? (kbd/shift? event)
+ ctrl? (kbd/ctrl? event)
left-click? (= 1 (.-which event))]
(when (and left-click?
+ (not ctrl?)
(not shift?)
+ (not @space?)
(or (not @hover)
(= :frame (:type @hover))
(some #(contains? selected %) @hover-ids)))
@@ -130,9 +144,9 @@
(reset! frame-hover nil))))
(defn on-click
- [hover selected edition drawing-path? drawing-tool]
+ [hover selected edition drawing-path? drawing-tool space?]
(mf/use-callback
- (mf/deps @hover selected edition drawing-path? drawing-tool)
+ (mf/deps @hover selected edition drawing-path? drawing-tool @space?)
(fn [event]
(when (or (dom/class? (dom/get-target event) "viewport-controls")
(dom/class? (dom/get-target event) "viewport-selrect"))
@@ -147,7 +161,8 @@
(when (and hovering?
(not shift?)
- (not frame?)
+ (or ctrl? (not frame?))
+ (not @space?)
(not selected?)
(not edition)
(not drawing-path?)
@@ -229,17 +244,17 @@
middle-click? (= 2 (.-which event))]
(when left-click?
- (st/emit! (dw/finish-panning)
- (ms/->MouseEvent :up ctrl? shift? alt?)))
+ (st/emit! (ms/->MouseEvent :up ctrl? shift? alt?)))
(when middle-click?
(dom/prevent-default event)
;; We store this so in Firefox the middle button won't do a paste of the content
(reset! disable-paste true)
- (timers/schedule #(reset! disable-paste false))
- (st/emit! (dw/finish-panning)
- (dw/finish-zooming)))))))
+ (timers/schedule #(reset! disable-paste false)))
+
+ (st/emit! (dw/finish-panning)
+ (dw/finish-zooming))))))
(defn on-pointer-enter [in-viewport?]
(mf/use-callback
diff --git a/frontend/src/app/main/ui/workspace/viewport/hooks.cljs b/frontend/src/app/main/ui/workspace/viewport/hooks.cljs
index 21ea3e7001..49c1d9315c 100644
--- a/frontend/src/app/main/ui/workspace/viewport/hooks.cljs
+++ b/frontend/src/app/main/ui/workspace/viewport/hooks.cljs
@@ -57,13 +57,14 @@
;; We schedule the event so it fires after `initialize-page` event
(timers/schedule #(st/emit! (dw/initialize-viewport size)))))))
-(defn setup-cursor [cursor alt? panning drawing-tool drawing-path? path-editing?]
+(defn setup-cursor [cursor alt? ctrl? space? panning drawing-tool drawing-path? path-editing?]
(mf/use-effect
- (mf/deps @cursor @alt? panning drawing-tool drawing-path? path-editing?)
+ (mf/deps @cursor @alt? @ctrl? @space? panning drawing-tool drawing-path? path-editing?)
(fn []
(let [new-cursor
(cond
- panning (utils/get-cursor :hand)
+ (and @ctrl? @space?) (utils/get-cursor :zoom)
+ (or panning @space?) (utils/get-cursor :hand)
(= drawing-tool :comments) (utils/get-cursor :comments)
(= drawing-tool :frame) (utils/get-cursor :create-artboard)
(= drawing-tool :rect) (utils/get-cursor :create-rectangle)
diff --git a/frontend/src/app/main/ui/workspace/viewport/pixel_overlay.cljs b/frontend/src/app/main/ui/workspace/viewport/pixel_overlay.cljs
index fb35ba9976..9fda55c82b 100644
--- a/frontend/src/app/main/ui/workspace/viewport/pixel_overlay.cljs
+++ b/frontend/src/app/main/ui/workspace/viewport/pixel_overlay.cljs
@@ -10,6 +10,7 @@
[app.common.uuid :as uuid]
[app.main.data.modal :as modal]
[app.main.data.workspace.colors :as dwc]
+ [app.main.data.workspace.undo :as dwu]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.cursors :as cur]
@@ -101,14 +102,16 @@
(fn [event]
(dom/prevent-default event)
(dom/stop-propagation event)
- (st/emit! (dwc/pick-color-select true (kbd/shift? event)))))
+ (st/emit! (dwu/start-undo-transaction)
+ (dwc/pick-color-select true (kbd/shift? event)))))
handle-mouse-up-picker
(mf/use-callback
(fn [event]
(dom/prevent-default event)
(dom/stop-propagation event)
- (st/emit! (dwc/stop-picker))
+ (st/emit! (dwu/commit-undo-transaction)
+ (dwc/stop-picker))
(modal/disallow-click-outside!)))
handle-image-load
diff --git a/frontend/src/app/main/ui/workspace/viewport/scroll_bars.cljs b/frontend/src/app/main/ui/workspace/viewport/scroll_bars.cljs
index 70aa9746c2..61830c5665 100644
--- a/frontend/src/app/main/ui/workspace/viewport/scroll_bars.cljs
+++ b/frontend/src/app/main/ui/workspace/viewport/scroll_bars.cljs
@@ -157,20 +157,20 @@
(mf/use-callback
(mf/deps v-scrollbar-y scrollbar-height)
(fn [event axis]
- (let [viewport (mf/ref-val viewport-ref)
- start-pt (dom/get-client-position event)
- new-v-scrollbar-y (-> (utils/translate-point-to-viewport-raw viewport zoom start-pt) :y)
- new-h-scrollbar-x (-> (utils/translate-point-to-viewport-raw viewport zoom start-pt) :x)
- v-scrollbar-y-padding (- v-scrollbar-y new-v-scrollbar-y)
- h-scrollbar-x-padding (- h-scrollbar-x new-h-scrollbar-x)
- vbox-rect {:x vbox-x
- :y vbox-y
- :x1 vbox-x
- :y1 vbox-y
- :x2 (+ vbox-x (:width vbox))
- :y2 (+ vbox-y (:height vbox))
- :width (:width vbox)
- :height (:height vbox)}
+ (let [viewport (mf/ref-val viewport-ref)
+ start-pt (dom/get-client-position event)
+ new-v-scrollbar-y (-> (utils/translate-point-to-viewport-raw viewport zoom start-pt) :y)
+ new-h-scrollbar-x (-> (utils/translate-point-to-viewport-raw viewport zoom start-pt) :x)
+ v-scrollbar-y-padding (- v-scrollbar-y new-v-scrollbar-y)
+ h-scrollbar-x-padding (- h-scrollbar-x new-h-scrollbar-x)
+ vbox-rect {:x vbox-x
+ :y vbox-y
+ :x1 vbox-x
+ :y1 vbox-y
+ :x2 (+ vbox-x (:width vbox))
+ :y2 (+ vbox-y (:height vbox))
+ :width (:width vbox)
+ :height (:height vbox)}
containing-rect (gpr/join-selrects [base-objects-rect vbox-rect])
height-factor (/ (:height containing-rect) vbox-height)
width-factor (/ (:width containing-rect) vbox-width)]
@@ -207,7 +207,9 @@
:height scrollbar-height
:fill-opacity 0.4
:x v-scrollbar-x
- :y v-scrollbar-y}]])
+ :y v-scrollbar-y
+ :style {:stroke "white"
+ :stroke-width 0.15}}]])
(when show-h-scroll?
[:g.h-scroll
[:rect {:on-mouse-move #(on-mouse-move % :x)
@@ -219,4 +221,6 @@
:height (* inv-zoom 7)
:fill-opacity 0.4
:x h-scrollbar-x
- :y h-scrollbar-y}]])]))
+ :y h-scrollbar-y
+ :style {:stroke "white"
+ :stroke-width 0.15}}]])]))
diff --git a/frontend/src/app/main/ui/workspace/viewport/utils.cljs b/frontend/src/app/main/ui/workspace/viewport/utils.cljs
index 9c0734afaa..8730610705 100644
--- a/frontend/src/app/main/ui/workspace/viewport/utils.cljs
+++ b/frontend/src/app/main/ui/workspace/viewport/utils.cljs
@@ -179,4 +179,7 @@
:pencil cur/pencil
:create-shape cur/create-shape
:duplicate cur/duplicate
+ :zoom cur/zoom
+ :zoom-in cur/zoom-in
+ :zooom-out cur/zoom-out
cur/pointer-inner))
diff --git a/frontend/src/app/worker/selection.cljs b/frontend/src/app/worker/selection.cljs
index d0b2290341..ed0412f86e 100644
--- a/frontend/src/app/worker/selection.cljs
+++ b/frontend/src/app/worker/selection.cljs
@@ -105,7 +105,7 @@
(assoc data :index index :z-index z-index)))
(defn- query-index
- [{index :index z-index :z-index} rect frame-id full-frame? include-frames? clip-children? reverse?]
+ [{index :index z-index :z-index} rect frame-id full-frame? include-frames? ignore-groups? clip-children? reverse?]
(let [result (-> (qdt/search index (clj->js rect))
(es6-iterator-seq))
@@ -117,6 +117,7 @@
(or (not frame-id) (= frame-id (:frame-id shape)))
(case (:type shape)
:frame include-frames?
+ (:bool :group) (not ignore-groups?)
true)
(or (not full-frame?)
@@ -189,10 +190,10 @@
nil)
(defmethod impl/handler :selection/query
- [{:keys [page-id rect frame-id reverse? full-frame? include-frames? clip-children?]
+ [{:keys [page-id rect frame-id reverse? full-frame? include-frames? ignore-groups? clip-children?]
:or {reverse? false full-frame? false include-frames? false clip-children? true} :as message}]
(when-let [index (get @state page-id)]
- (query-index index rect frame-id full-frame? include-frames? clip-children? reverse?)))
+ (query-index index rect frame-id full-frame? include-frames? ignore-groups? clip-children? reverse?)))
(defmethod impl/handler :selection/query-z-index
[{:keys [page-id objects ids]}]