mirror of
https://github.com/penpot/penpot.git
synced 2026-08-06 12:58:55 +00:00
✨ Graphic tablet use improvements: add scroll bars
This commit is contained in:
parent
04f7169aef
commit
f72e140327
@ -6,8 +6,9 @@
|
|||||||
|
|
||||||
### :sparkles: New features
|
### :sparkles: New features
|
||||||
|
|
||||||
|
- Scroll bars [Taiga #2550](https://tree.taiga.io/project/penpot/task/2550)
|
||||||
- Add select layer option to context menu [Taiga #2474](https://tree.taiga.io/project/penpot/us/2474).
|
- Add select layer option to context menu [Taiga #2474](https://tree.taiga.io/project/penpot/us/2474).
|
||||||
- Guides [Taiga #290](https://tree.taiga.io/project/penpot/us/290?milestone=307334)
|
- Guides [Taiga #290](https://tree.taiga.io/project/penpot/us/290)
|
||||||
- Improve file menu by adding semantically groups [Github #1203](https://github.com/penpot/penpot/issues/1203).
|
- Improve file menu by adding semantically groups [Github #1203](https://github.com/penpot/penpot/issues/1203).
|
||||||
- Add update components in bulk option in context menu [Taiga #1975](https://tree.taiga.io/project/penpot/us/1975).
|
- 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)
|
- Create first E2E tests [Taiga #2608](https://tree.taiga.io/project/penpot/task/2608), [Taiga #2608](https://tree.taiga.io/project/penpot/task/2608)
|
||||||
|
|||||||
@ -138,10 +138,10 @@
|
|||||||
.coordinates {
|
.coordinates {
|
||||||
background-color: $color-dark-bg;
|
background-color: $color-dark-bg;
|
||||||
border-radius: $br-small;
|
border-radius: $br-small;
|
||||||
bottom: -10px;
|
bottom: 0px;
|
||||||
padding-left: 5px;
|
padding-left: 5px;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
right: calc(#{$width-settings-bar} + 10px);
|
right: calc(#{$width-settings-bar} + 14px);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
width: 125px;
|
width: 125px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|||||||
@ -28,6 +28,7 @@
|
|||||||
[app.main.ui.workspace.viewport.pixel-overlay :as pixel-overlay]
|
[app.main.ui.workspace.viewport.pixel-overlay :as pixel-overlay]
|
||||||
[app.main.ui.workspace.viewport.presence :as presence]
|
[app.main.ui.workspace.viewport.presence :as presence]
|
||||||
[app.main.ui.workspace.viewport.rules :as rules]
|
[app.main.ui.workspace.viewport.rules :as rules]
|
||||||
|
[app.main.ui.workspace.viewport.scroll-bars :as scroll-bars]
|
||||||
[app.main.ui.workspace.viewport.selection :as selection]
|
[app.main.ui.workspace.viewport.selection :as selection]
|
||||||
[app.main.ui.workspace.viewport.snap-distances :as snap-distances]
|
[app.main.ui.workspace.viewport.snap-distances :as snap-distances]
|
||||||
[app.main.ui.workspace.viewport.snap-points :as snap-points]
|
[app.main.ui.workspace.viewport.snap-points :as snap-points]
|
||||||
@ -250,7 +251,13 @@
|
|||||||
:hover (when (not= :frame (:type @hover))
|
:hover (when (not= :frame (:type @hover))
|
||||||
#{(or @frame-hover (:id @hover))})
|
#{(or @frame-hover (:id @hover))})
|
||||||
:edition edition
|
:edition edition
|
||||||
:zoom zoom}])
|
:zoom zoom}]
|
||||||
|
|
||||||
|
[:& scroll-bars/viewport-scrollbars
|
||||||
|
{:objects base-objects
|
||||||
|
:zoom zoom
|
||||||
|
:vbox vbox
|
||||||
|
:viewport-ref viewport-ref}])
|
||||||
|
|
||||||
(when show-selection-handlers?
|
(when show-selection-handlers?
|
||||||
[:& selection/selection-handlers
|
[:& selection/selection-handlers
|
||||||
|
|||||||
212
frontend/src/app/main/ui/workspace/viewport/scroll_bars.cljs
Normal file
212
frontend/src/app/main/ui/workspace/viewport/scroll_bars.cljs
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
;; 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
|
||||||
|
|
||||||
|
(ns app.main.ui.workspace.viewport.scroll-bars
|
||||||
|
(:require
|
||||||
|
[app.common.geom.shapes :as gsh]
|
||||||
|
[app.common.geom.shapes.rect :as gpr]
|
||||||
|
[app.common.pages.helpers :as cph]
|
||||||
|
[app.main.data.workspace :as dw]
|
||||||
|
[app.main.store :as st]
|
||||||
|
[app.main.ui.workspace.viewport.utils :as utils]
|
||||||
|
[app.util.dom :as dom]
|
||||||
|
[rumext.alpha :as mf]))
|
||||||
|
|
||||||
|
(mf/defc viewport-scrollbars
|
||||||
|
{::mf/wrap [mf/memo]}
|
||||||
|
[{:keys [objects viewport-ref zoom vbox]}]
|
||||||
|
|
||||||
|
(let [v-scrolling? (mf/use-state false)
|
||||||
|
h-scrolling? (mf/use-state false)
|
||||||
|
start-ref (mf/use-ref nil)
|
||||||
|
v-scrollbar-y-ref (mf/use-ref nil)
|
||||||
|
h-scrollbar-x-ref (mf/use-ref nil)
|
||||||
|
v-scrollbar-y-stored (mf/ref-val v-scrollbar-y-ref)
|
||||||
|
h-scrollbar-x-stored (mf/ref-val h-scrollbar-x-ref)
|
||||||
|
v-scrollbar-y-padding-ref (mf/use-ref nil)
|
||||||
|
h-scrollbar-x-padding-ref (mf/use-ref nil)
|
||||||
|
scrollbar-height-ref (mf/use-ref nil)
|
||||||
|
scrollbar-width-ref (mf/use-ref nil)
|
||||||
|
scrollbar-height-stored (mf/ref-val scrollbar-height-ref)
|
||||||
|
scrollbar-width-stored (mf/ref-val scrollbar-width-ref)
|
||||||
|
height-factor-ref (mf/use-ref nil)
|
||||||
|
width-factor-ref (mf/use-ref nil)
|
||||||
|
vbox-y-ref (mf/use-ref nil)
|
||||||
|
vbox-x-ref (mf/use-ref nil)
|
||||||
|
|
||||||
|
vbox-x (:x vbox)
|
||||||
|
vbox-y (:y vbox)
|
||||||
|
|
||||||
|
base-objects-rect
|
||||||
|
(mf/use-memo
|
||||||
|
(mf/deps objects)
|
||||||
|
(fn []
|
||||||
|
(let [root-shapes (-> objects cph/get-top-frame :shapes)
|
||||||
|
shapes (->> root-shapes (mapv #(get objects %)))]
|
||||||
|
(gsh/selection-rect shapes))))
|
||||||
|
|
||||||
|
inv-zoom (/ 1 zoom)
|
||||||
|
vbox-height (- (:height vbox) (* inv-zoom 44))
|
||||||
|
vbox-width (- (:width vbox) (* inv-zoom 34))
|
||||||
|
|
||||||
|
;; top space hidden because of the scroll
|
||||||
|
top-offset (-> (- vbox-y (:y base-objects-rect))
|
||||||
|
(max 0)
|
||||||
|
(* vbox-height)
|
||||||
|
(/ (:height base-objects-rect)))
|
||||||
|
;; left space hidden because of the scroll
|
||||||
|
left-offset (-> (- vbox-x (:x base-objects-rect))
|
||||||
|
(max 0)
|
||||||
|
(* vbox-width)
|
||||||
|
(/ (:width base-objects-rect)))
|
||||||
|
|
||||||
|
;; bottom space hidden because of the scroll
|
||||||
|
bottom-offset (-> (- (:y2 base-objects-rect) (+ vbox-y vbox-height))
|
||||||
|
(max 0)
|
||||||
|
(* vbox-height)
|
||||||
|
(/ (:height base-objects-rect)))
|
||||||
|
|
||||||
|
;; right space hidden because of the scroll
|
||||||
|
right-offset (-> (- (:x2 base-objects-rect) (+ vbox-x vbox-width))
|
||||||
|
(max 0)
|
||||||
|
(* vbox-width)
|
||||||
|
(/ (:width base-objects-rect)))
|
||||||
|
|
||||||
|
show-v-scroll? (or @v-scrolling? (> top-offset 0) (> bottom-offset 0))
|
||||||
|
show-h-scroll? (or @h-scrolling? (> left-offset 0) (> right-offset 0))
|
||||||
|
|
||||||
|
v-scrollbar-x (+ vbox-x (:width vbox) (* inv-zoom -32))
|
||||||
|
v-scrollbar-y (+ vbox-y top-offset)
|
||||||
|
|
||||||
|
h-scrollbar-x (+ vbox-x left-offset)
|
||||||
|
h-scrollbar-y (+ vbox-y (:height vbox) (* inv-zoom -40))
|
||||||
|
|
||||||
|
scrollbar-height (-> (- (+ vbox-y vbox-height) bottom-offset v-scrollbar-y))
|
||||||
|
scrollbar-height (-> (cond
|
||||||
|
@v-scrolling? scrollbar-height-stored
|
||||||
|
:else scrollbar-height)
|
||||||
|
(max (* inv-zoom 100)))
|
||||||
|
|
||||||
|
scrollbar-width (-> (- (+ vbox-x vbox-width) right-offset h-scrollbar-x))
|
||||||
|
scrollbar-width (-> (cond
|
||||||
|
@h-scrolling? scrollbar-width-stored
|
||||||
|
:else scrollbar-width)
|
||||||
|
(max (* inv-zoom 100)))
|
||||||
|
|
||||||
|
v-scrollbar-y (-> (cond
|
||||||
|
@v-scrolling? (- v-scrollbar-y-stored (- (- vbox-y (mf/ref-val vbox-y-ref))))
|
||||||
|
:else v-scrollbar-y)
|
||||||
|
(max (+ vbox-y (* inv-zoom 26))))
|
||||||
|
|
||||||
|
v-scrollbar-y (if (> (+ v-scrollbar-y scrollbar-height) (+ vbox-y vbox-height)) ;; the scroll bar is stick to the bottom
|
||||||
|
(-> (+ vbox-y vbox-height)
|
||||||
|
(- scrollbar-height))
|
||||||
|
v-scrollbar-y)
|
||||||
|
|
||||||
|
h-scrollbar-x (-> (cond
|
||||||
|
@h-scrolling? (- h-scrollbar-x-stored (- (- vbox-x (mf/ref-val vbox-x-ref))))
|
||||||
|
:else h-scrollbar-x)
|
||||||
|
(max (+ vbox-x (* inv-zoom 26))))
|
||||||
|
|
||||||
|
h-scrollbar-x (if (> (+ h-scrollbar-x scrollbar-width) (+ vbox-x vbox-width)) ;; the scroll bar is stick to the right
|
||||||
|
(-> (+ vbox-x vbox-width)
|
||||||
|
(- scrollbar-width))
|
||||||
|
h-scrollbar-x)
|
||||||
|
|
||||||
|
on-mouse-move
|
||||||
|
(mf/use-callback
|
||||||
|
(mf/deps zoom v-scrolling?)
|
||||||
|
(fn [event axis]
|
||||||
|
(when-let [_ (or @v-scrolling? @h-scrolling?)]
|
||||||
|
(let [viewport (mf/ref-val viewport-ref)
|
||||||
|
start-pt (mf/ref-val start-ref)
|
||||||
|
current-pt (dom/get-client-position event)
|
||||||
|
current-pt-viewport (utils/translate-point-to-viewport-raw viewport zoom current-pt)
|
||||||
|
y-delta (/ (* (mf/ref-val height-factor-ref) (- (:y current-pt) (:y start-pt))) zoom)
|
||||||
|
x-delta (/ (* (mf/ref-val width-factor-ref) (- (:x current-pt) (:x start-pt))) zoom)
|
||||||
|
new-v-scrollbar-y (-> current-pt-viewport
|
||||||
|
(:y)
|
||||||
|
(+ (mf/ref-val v-scrollbar-y-padding-ref)))
|
||||||
|
new-h-scrollbar-x (-> current-pt-viewport
|
||||||
|
(:x)
|
||||||
|
(+ (mf/ref-val h-scrollbar-x-padding-ref)))
|
||||||
|
viewport-update (-> {}
|
||||||
|
(cond-> (= axis :y) (assoc :y #(+ % y-delta)))
|
||||||
|
(cond-> (= axis :x) (assoc :x #(+ % x-delta))))]
|
||||||
|
(mf/set-ref-val! vbox-y-ref vbox-y)
|
||||||
|
(mf/set-ref-val! vbox-x-ref vbox-x)
|
||||||
|
(st/emit! (dw/update-viewport-position viewport-update))
|
||||||
|
(mf/set-ref-val! v-scrollbar-y-ref new-v-scrollbar-y)
|
||||||
|
(mf/set-ref-val! h-scrollbar-x-ref new-h-scrollbar-x)
|
||||||
|
(mf/set-ref-val! start-ref current-pt)))))
|
||||||
|
|
||||||
|
on-mouse-down
|
||||||
|
(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)}
|
||||||
|
containing-rect (gpr/join-selrects [base-objects-rect vbox-rect])
|
||||||
|
height-factor (/ (:height containing-rect) vbox-height)
|
||||||
|
width-factor (/ (:width containing-rect) vbox-width)]
|
||||||
|
(mf/set-ref-val! start-ref start-pt)
|
||||||
|
(mf/set-ref-val! v-scrollbar-y-padding-ref v-scrollbar-y-padding)
|
||||||
|
(mf/set-ref-val! h-scrollbar-x-padding v-scrollbar-y-padding)
|
||||||
|
(mf/set-ref-val! v-scrollbar-y-ref (+ new-v-scrollbar-y v-scrollbar-y-padding))
|
||||||
|
(mf/set-ref-val! h-scrollbar-x-ref (+ new-h-scrollbar-x h-scrollbar-x-padding))
|
||||||
|
(mf/set-ref-val! vbox-y-ref vbox-y)
|
||||||
|
(mf/set-ref-val! vbox-x-ref vbox-x)
|
||||||
|
(mf/set-ref-val! scrollbar-height-ref scrollbar-height)
|
||||||
|
(mf/set-ref-val! scrollbar-width-ref scrollbar-width)
|
||||||
|
(mf/set-ref-val! height-factor-ref height-factor)
|
||||||
|
(mf/set-ref-val! width-factor-ref width-factor)
|
||||||
|
(reset! v-scrolling? (= axis :y))
|
||||||
|
(reset! h-scrolling? (= axis :x)))))
|
||||||
|
|
||||||
|
on-mouse-up
|
||||||
|
(mf/use-callback
|
||||||
|
(mf/deps)
|
||||||
|
(fn [_]
|
||||||
|
(reset! v-scrolling? false)
|
||||||
|
(reset! h-scrolling? false)))]
|
||||||
|
|
||||||
|
[*
|
||||||
|
(when show-v-scroll?
|
||||||
|
[:g.v-scroll
|
||||||
|
[:rect {:on-mouse-move #(on-mouse-move % :y)
|
||||||
|
:on-mouse-down #(on-mouse-down % :y)
|
||||||
|
:on-mouse-up #(on-mouse-up % :y)
|
||||||
|
:width (* inv-zoom 7)
|
||||||
|
:rx (* inv-zoom 3)
|
||||||
|
:ry (* inv-zoom 3)
|
||||||
|
:height scrollbar-height
|
||||||
|
:fill-opacity 0.4
|
||||||
|
:x v-scrollbar-x
|
||||||
|
:y v-scrollbar-y}]])
|
||||||
|
(when show-h-scroll?
|
||||||
|
[:g.h-scroll
|
||||||
|
[:rect {:on-mouse-move #(on-mouse-move % :x)
|
||||||
|
:on-mouse-down #(on-mouse-down % :x)
|
||||||
|
:on-mouse-up #(on-mouse-up % :x)
|
||||||
|
:width scrollbar-width
|
||||||
|
:rx (* inv-zoom 3)
|
||||||
|
:ry (* inv-zoom 3)
|
||||||
|
:height (* inv-zoom 7)
|
||||||
|
:fill-opacity 0.4
|
||||||
|
:x h-scrollbar-x
|
||||||
|
:y h-scrollbar-y}]])]))
|
||||||
@ -153,7 +153,7 @@
|
|||||||
(:width vbox 0)
|
(:width vbox 0)
|
||||||
(:height vbox 0)]))
|
(:height vbox 0)]))
|
||||||
|
|
||||||
(defn translate-point-to-viewport [viewport zoom pt]
|
(defn translate-point-to-viewport-raw [viewport zoom pt]
|
||||||
(let [vbox (.. ^js viewport -viewBox -baseVal)
|
(let [vbox (.. ^js viewport -viewBox -baseVal)
|
||||||
brect (dom/get-bounding-rect viewport)
|
brect (dom/get-bounding-rect viewport)
|
||||||
brect (gpt/point (d/parse-integer (:left brect))
|
brect (gpt/point (d/parse-integer (:left brect))
|
||||||
@ -162,8 +162,11 @@
|
|||||||
zoom (gpt/point zoom)]
|
zoom (gpt/point zoom)]
|
||||||
(-> (gpt/subtract pt brect)
|
(-> (gpt/subtract pt brect)
|
||||||
(gpt/divide zoom)
|
(gpt/divide zoom)
|
||||||
(gpt/add box)
|
(gpt/add box))))
|
||||||
(gpt/round 0))))
|
|
||||||
|
(defn translate-point-to-viewport [viewport zoom pt]
|
||||||
|
(-> (translate-point-to-viewport-raw viewport zoom pt)
|
||||||
|
(gpt/round 0)))
|
||||||
|
|
||||||
(defn get-cursor [cursor]
|
(defn get-cursor [cursor]
|
||||||
(case cursor
|
(case cursor
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user