Improve scrollbar behavior (#351)

* Change scrollbar behavior

* Leave space at the end of the scrollbar

* Change mid to center

* Use shorter array initialization

* Add space around scrollbar

* Fix scrollbar spacing

* Smooth end of scrollbars

* Add page up and down

* Page up and down on click in scrollbar track

* Add shift pageup to translate horizontally
This commit is contained in:
0HyperCube 2021-08-16 21:25:00 +01:00 committed by Keavon Chambers
parent f63b0abfde
commit 6deecad9c0
10 changed files with 63 additions and 14 deletions

View file

@ -19,6 +19,11 @@ pub const LINE_ROTATE_SNAP_ANGLE: f64 = 15.;
// SELECT TOOL
pub const SELECTION_TOLERANCE: f64 = 1.0;
// SCROLLBARS
pub const SCROLLBAR_SPACING: f64 = 0.1;
pub const ASYMPTOTIC_EFFECT: f64 = 0.5;
pub const SCALE_EFFECT: f64 = 0.5;
pub const DEFAULT_DOCUMENT_NAME: &str = "Untitled Document";
pub const FILE_SAVE_SUFFIX: &str = ".graphite";
pub const FILE_EXPORT_SUFFIX: &str = ".svg";

View file

@ -1,6 +1,6 @@
pub use super::layer_panel::*;
use crate::{
consts::{FILE_EXPORT_SUFFIX, FILE_SAVE_SUFFIX},
consts::{ASYMPTOTIC_EFFECT, FILE_EXPORT_SUFFIX, FILE_SAVE_SUFFIX, SCALE_EFFECT, SCROLLBAR_SPACING},
frontend::layer_panel::*,
EditorError,
};
@ -406,15 +406,16 @@ impl MessageHandler<DocumentMessage, &InputPreprocessor> for DocumentMessageHand
}
.into(),
);
let root = self.layerdata(&[]);
let viewport = ipp.viewport_bounds.size();
let [bounds1, bounds2] = self.document.visible_layers_bounding_box().unwrap_or_default();
let bounds1 = bounds1.min(DVec2::ZERO) - viewport * (f64::powf(2., root.scale / 3.) * 0.5);
let bounds2 = bounds2.max(viewport) + viewport * (f64::powf(2., root.scale / 3.) * 0.5);
let bounds_length = bounds2 - bounds1;
let scrollbar_multiplier = bounds_length - viewport;
let scrollbar_position = bounds1.abs() / scrollbar_multiplier;
let scrollbar_size = viewport / bounds_length;
let scale = 0.5 + ASYMPTOTIC_EFFECT + self.layerdata(&[]).scale * SCALE_EFFECT;
let viewport_size = ipp.viewport_bounds.size();
let viewport_mid = ipp.viewport_bounds.center();
let [bounds1, bounds2] = self.document.visible_layers_bounding_box().unwrap_or([viewport_mid; 2]);
let bounds1 = bounds1.min(viewport_mid) - viewport_size * scale;
let bounds2 = bounds2.max(viewport_mid) + viewport_size * scale;
let bounds_length = (bounds2 - bounds1) * (1. + SCROLLBAR_SPACING);
let scrollbar_position = DVec2::splat(0.5) - (bounds1.lerp(bounds2, 0.5) - viewport_mid) / (bounds_length - viewport_size);
let scrollbar_multiplier = bounds_length - viewport_size;
let scrollbar_size = viewport_size / bounds_length;
responses.push_back(
FrontendMessage::UpdateScrollbars {
position: scrollbar_position.into(),

View file

@ -30,7 +30,8 @@ pub enum MovementMessage {
DecreaseCanvasZoom,
WheelCanvasZoom,
ZoomCanvasToFitAll,
TranslateCanvas(glam::DVec2),
TranslateCanvas(DVec2),
TranslateCanvasByViewportFraction(DVec2),
}
#[derive(Debug, Clone, Default, PartialEq)]
@ -193,6 +194,12 @@ impl MessageHandler<MovementMessage, (&mut LayerData, &Document, &InputPreproces
TranslateCanvas(delta) => {
let transformed_delta = document.root.transform.inverse().transform_vector2(delta);
layerdata.translation += transformed_delta;
self.create_document_transform_from_layerdata(layerdata, &ipp.viewport_bounds, responses);
}
TranslateCanvasByViewportFraction(delta) => {
let transformed_delta = document.root.transform.inverse().transform_vector2(delta * ipp.viewport_bounds.size());
layerdata.translation += transformed_delta;
self.create_document_transform_from_layerdata(layerdata, &ipp.viewport_bounds, responses);
}
@ -212,6 +219,8 @@ impl MessageHandler<MovementMessage, (&mut LayerData, &Document, &InputPreproces
DecreaseCanvasZoom,
WheelCanvasTranslate,
ZoomCanvasToFitAll,
TranslateCanvas,
TranslateCanvasByViewportFraction,
);
if self.rotating {

View file

@ -1,3 +1,5 @@
use glam::DVec2;
use super::{
keyboard::{Key, KeyStates, NUMBER_OF_KEYS},
InputPreprocessor,
@ -192,6 +194,7 @@ impl Default for Mapping {
entry! {action=DocumentMessage::ExportDocument, key_down=KeyE, modifiers=[KeyControl]},
entry! {action=DocumentMessage::SaveDocument, key_down=KeyS, modifiers=[KeyControl]},
entry! {action=DocumentMessage::SaveDocument, key_down=KeyS, modifiers=[KeyControl, KeyShift]},
// Document movement
entry! {action=MovementMessage::MouseMove, message=InputMapperMessage::PointerMove},
entry! {action=MovementMessage::RotateCanvasBegin{snap:false}, key_down=Mmb, modifiers=[KeyControl]},
entry! {action=MovementMessage::RotateCanvasBegin{snap:true}, key_down=Mmb, modifiers=[KeyControl, KeyShift]},
@ -207,6 +210,11 @@ impl Default for Mapping {
entry! {action=MovementMessage::WheelCanvasZoom, message=InputMapperMessage::MouseScroll, modifiers=[KeyControl]},
entry! {action=MovementMessage::WheelCanvasTranslate{use_y_as_x: true}, message=InputMapperMessage::MouseScroll, modifiers=[KeyShift]},
entry! {action=MovementMessage::WheelCanvasTranslate{use_y_as_x: false}, message=InputMapperMessage::MouseScroll},
entry! {action=MovementMessage::TranslateCanvasByViewportFraction(DVec2::new(1., 0.)), key_down=KeyPageUp, modifiers=[KeyShift]},
entry! {action=MovementMessage::TranslateCanvasByViewportFraction(DVec2::new(-1., 0.)), key_down=KeyPageDown, modifiers=[KeyShift]},
entry! {action=MovementMessage::TranslateCanvasByViewportFraction(DVec2::new(0., 1.)), key_down=KeyPageUp},
entry! {action=MovementMessage::TranslateCanvasByViewportFraction(DVec2::new(0., -1.)), key_down=KeyPageDown},
// Document actions
entry! {action=DocumentsMessage::NewDocument, key_down=KeyN, modifiers=[KeyControl]},
entry! {action=DocumentsMessage::NextDocument, key_down=KeyTab, modifiers=[KeyControl]},
entry! {action=DocumentsMessage::PrevDocument, key_down=KeyTab, modifiers=[KeyControl, KeyShift]},
@ -214,6 +222,7 @@ impl Default for Mapping {
entry! {action=DocumentsMessage::CloseActiveDocumentWithConfirmation, key_down=KeyW, modifiers=[KeyControl]},
entry! {action=DocumentMessage::DuplicateSelectedLayers, key_down=KeyD, modifiers=[KeyControl]},
entry! {action=DocumentsMessage::CopySelectedLayers, key_down=KeyC, modifiers=[KeyControl]},
// Nudging
entry! {action=DocumentMessage::NudgeSelectedLayers(-SHIFT_NUDGE_AMOUNT, -SHIFT_NUDGE_AMOUNT), key_down=KeyArrowUp, modifiers=[KeyShift, KeyArrowLeft]},
entry! {action=DocumentMessage::NudgeSelectedLayers(SHIFT_NUDGE_AMOUNT, -SHIFT_NUDGE_AMOUNT), key_down=KeyArrowUp, modifiers=[KeyShift, KeyArrowRight]},
entry! {action=DocumentMessage::NudgeSelectedLayers(0., -SHIFT_NUDGE_AMOUNT), key_down=KeyArrowUp, modifiers=[KeyShift]},

View file

@ -76,6 +76,8 @@ pub enum Key {
KeyRightBracket,
KeyLeftCurlyBracket,
KeyRightCurlyBracket,
KeyPageUp,
KeyPageDown,
// This has to be the last element in the enum.
NumKeys,

View file

@ -22,6 +22,10 @@ impl ViewportBounds {
pub fn size(&self) -> DVec2 {
self.bottom_right - self.top_left
}
pub fn center(&self) -> DVec2 {
self.bottom_right.lerp(self.top_left, 0.5)
}
}
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]

View file

@ -128,6 +128,7 @@
:handlePosition="scrollbarPos.y"
@update:handlePosition="translateCanvasY"
v-model:handleLength="scrollbarSize.y"
@pressTrack="pageY"
:class="'right-scrollbar'"
/>
</LayoutCol>
@ -138,6 +139,7 @@
:handlePosition="scrollbarPos.x"
@update:handlePosition="translateCanvasX"
v-model:handleLength="scrollbarSize.x"
@pressTrack="pageX"
:class="'bottom-scrollbar'"
/>
</LayoutRow>
@ -293,6 +295,14 @@ export default defineComponent({
this.scrollbarPos.y = newValue;
(await wasm).translate_canvas(0, -delta * this.scrollbarMultiplier.y);
},
async pageX(delta: number) {
const move = delta < 0 ? 1 : -1;
(await wasm).translate_canvas_by_fraction(move, 0);
},
async pageY(delta: number) {
const move = delta < 0 ? 1 : -1;
(await wasm).translate_canvas_by_fraction(0, move);
},
async selectTool(toolName: string) {
(await wasm).select_tool(toolName);
},

View file

@ -186,9 +186,9 @@ export default defineComponent({
},
grabArea(e: MouseEvent) {
if (!this.dragging) {
this.dragging = true;
this.mousePos = mousePosition(this.direction, e);
this.clampHandlePosition(((this.mousePos - this.trackOffset()) / this.trackLength() - this.handleLength / 2) / (1 - this.handleLength));
const mousePos = mousePosition(this.direction, e);
const oldMouse = handleToTrack(this.handleLength, this.handlePosition) * this.trackLength() + this.trackOffset();
this.$emit("pressTrack", mousePos - oldMouse);
}
},
mouseUp() {

View file

@ -312,6 +312,13 @@ pub fn translate_canvas(delta_x: f64, delta_y: f64) -> Result<(), JsValue> {
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(ev)).map_err(convert_error)
}
/// Translates document (in viewport coords)
#[wasm_bindgen]
pub fn translate_canvas_by_fraction(delta_x: f64, delta_y: f64) -> Result<(), JsValue> {
let ev = MovementMessage::TranslateCanvasByViewportFraction((delta_x, delta_y).into());
EDITOR_STATE.with(|editor| editor.borrow_mut().handle_message(ev)).map_err(convert_error)
}
/// Update the list of selected layers. The layer paths have to be stored in one array and are separated by LayerId::MAX
#[wasm_bindgen]
pub fn select_layers(paths: Vec<LayerId>) -> Result<(), JsValue> {

View file

@ -151,6 +151,8 @@ pub fn translate_key(name: &str) -> Key {
"]" => KeyRightBracket,
"{" => KeyLeftCurlyBracket,
"}" => KeyRightCurlyBracket,
"pageup" => KeyPageUp,
"pagedown" => KeyPageDown,
_ => UnknownKey,
}
}