From 4b9560583facc81ca1b8fde26fed3c48ce60cbca Mon Sep 17 00:00:00 2001 From: 0hypercube <0hypercube@gmail.com> Date: Mon, 16 Aug 2021 08:32:44 +0100 Subject: [PATCH 01/10] Change scrollbar behaviour --- editor/src/document/document_file.rs | 17 +++++++++-------- editor/src/input/mouse.rs | 4 ++++ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/editor/src/document/document_file.rs b/editor/src/document/document_file.rs index 2ef404d4c5..a5a145cddb 100644 --- a/editor/src/document/document_file.rs +++ b/editor/src/document/document_file.rs @@ -406,15 +406,16 @@ impl MessageHandler 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 scale = 0.5 + 0.5 * self.layerdata(&[]).scale; + let viewport_size = ipp.viewport_bounds.size(); + let viewport_mid = ipp.viewport_bounds.mid(); + let [bounds1, bounds2] = self.document.visible_layers_bounding_box().unwrap_or([viewport_mid, viewport_mid]); + let bounds1 = bounds1.min(viewport_mid) - viewport_size * scale; + let bounds2 = bounds2.max(viewport_mid) + viewport_size * scale; 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 scrollbar_multiplier = bounds_length - viewport_size; + let scrollbar_position = DVec2::splat(0.5) - (bounds1.lerp(bounds2, 0.5) - viewport_mid) / scrollbar_multiplier; + let scrollbar_size = viewport_size / bounds_length; responses.push_back( FrontendMessage::UpdateScrollbars { position: scrollbar_position.into(), diff --git a/editor/src/input/mouse.rs b/editor/src/input/mouse.rs index f63c5f12f3..b149a1ca25 100644 --- a/editor/src/input/mouse.rs +++ b/editor/src/input/mouse.rs @@ -22,6 +22,10 @@ impl ViewportBounds { pub fn size(&self) -> DVec2 { self.bottom_right - self.top_left } + + pub fn mid(&self) -> DVec2 { + self.bottom_right.lerp(self.top_left, 0.5) + } } #[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)] From f0852e0bb298dc5fe97aaa3cc769cbf18d37a84a Mon Sep 17 00:00:00 2001 From: 0hypercube <0hypercube@gmail.com> Date: Mon, 16 Aug 2021 09:11:04 +0100 Subject: [PATCH 02/10] Leave space at the end of the scrollbar --- editor/src/document/document_file.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editor/src/document/document_file.rs b/editor/src/document/document_file.rs index a5a145cddb..1aafe246e7 100644 --- a/editor/src/document/document_file.rs +++ b/editor/src/document/document_file.rs @@ -406,7 +406,7 @@ impl MessageHandler for DocumentMessageHand } .into(), ); - let scale = 0.5 + 0.5 * self.layerdata(&[]).scale; + let scale = 0.7 + 0.3 * self.layerdata(&[]).scale; let viewport_size = ipp.viewport_bounds.size(); let viewport_mid = ipp.viewport_bounds.mid(); let [bounds1, bounds2] = self.document.visible_layers_bounding_box().unwrap_or([viewport_mid, viewport_mid]); From 1c38bd30e2a0da3da03e6c30f4d99c0b1f01a58f Mon Sep 17 00:00:00 2001 From: 0hypercube <0hypercube@gmail.com> Date: Mon, 16 Aug 2021 09:13:24 +0100 Subject: [PATCH 03/10] Change mid to center --- editor/src/document/document_file.rs | 2 +- editor/src/input/mouse.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/editor/src/document/document_file.rs b/editor/src/document/document_file.rs index 1aafe246e7..4ab82606f2 100644 --- a/editor/src/document/document_file.rs +++ b/editor/src/document/document_file.rs @@ -408,7 +408,7 @@ impl MessageHandler for DocumentMessageHand ); let scale = 0.7 + 0.3 * self.layerdata(&[]).scale; let viewport_size = ipp.viewport_bounds.size(); - let viewport_mid = ipp.viewport_bounds.mid(); + let viewport_mid = ipp.viewport_bounds.center(); let [bounds1, bounds2] = self.document.visible_layers_bounding_box().unwrap_or([viewport_mid, viewport_mid]); let bounds1 = bounds1.min(viewport_mid) - viewport_size * scale; let bounds2 = bounds2.max(viewport_mid) + viewport_size * scale; diff --git a/editor/src/input/mouse.rs b/editor/src/input/mouse.rs index b149a1ca25..9a16661fa0 100644 --- a/editor/src/input/mouse.rs +++ b/editor/src/input/mouse.rs @@ -23,7 +23,7 @@ impl ViewportBounds { self.bottom_right - self.top_left } - pub fn mid(&self) -> DVec2 { + pub fn center(&self) -> DVec2 { self.bottom_right.lerp(self.top_left, 0.5) } } From 99849402e150c30fa3ad186a334a9af5139ea185 Mon Sep 17 00:00:00 2001 From: 0hypercube <0hypercube@gmail.com> Date: Mon, 16 Aug 2021 09:18:32 +0100 Subject: [PATCH 04/10] Use shorter array initialisation --- editor/src/document/document_file.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editor/src/document/document_file.rs b/editor/src/document/document_file.rs index 4ab82606f2..cf5921837f 100644 --- a/editor/src/document/document_file.rs +++ b/editor/src/document/document_file.rs @@ -409,7 +409,7 @@ impl MessageHandler for DocumentMessageHand let scale = 0.7 + 0.3 * self.layerdata(&[]).scale; 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, viewport_mid]); + 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; From b9abace41586c932a55d276f4f2feabbd6e3df9c Mon Sep 17 00:00:00 2001 From: 0hypercube <0hypercube@gmail.com> Date: Mon, 16 Aug 2021 09:53:41 +0100 Subject: [PATCH 05/10] Add space around scrollbar --- editor/src/consts.rs | 3 +++ editor/src/document/document_file.rs | 8 +++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/editor/src/consts.rs b/editor/src/consts.rs index 9d85fee1fa..62dd7c8917 100644 --- a/editor/src/consts.rs +++ b/editor/src/consts.rs @@ -19,6 +19,9 @@ pub const LINE_ROTATE_SNAP_ANGLE: f64 = 15.; // SELECT TOOL pub const SELECTION_TOLERANCE: f64 = 1.0; +// SCROLLBARS +pub const SCROLLBAR_SPACING: f64 = 0.2; + pub const DEFAULT_DOCUMENT_NAME: &str = "Untitled Document"; pub const FILE_SAVE_SUFFIX: &str = ".graphite"; pub const FILE_EXPORT_SUFFIX: &str = ".svg"; diff --git a/editor/src/document/document_file.rs b/editor/src/document/document_file.rs index cf5921837f..cc3e55fb44 100644 --- a/editor/src/document/document_file.rs +++ b/editor/src/document/document_file.rs @@ -1,6 +1,6 @@ pub use super::layer_panel::*; use crate::{ - consts::{FILE_EXPORT_SUFFIX, FILE_SAVE_SUFFIX}, + consts::{FILE_EXPORT_SUFFIX, FILE_SAVE_SUFFIX, SCROLLBAR_SPACING}, frontend::layer_panel::*, EditorError, }; @@ -406,15 +406,17 @@ impl MessageHandler for DocumentMessageHand } .into(), ); - let scale = 0.7 + 0.3 * self.layerdata(&[]).scale; + let scale = 0.5 + 0.5 * self.layerdata(&[]).scale; 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; + let scrollbar_position = DVec2::splat(0.5) - (bounds1.lerp(bounds2, 0.5) - viewport_mid) / (bounds_length - viewport_size); + let scrollbar_position = scrollbar_position * (1. - SCROLLBAR_SPACING) + SCROLLBAR_SPACING / 2.; + let bounds_length = bounds_length * (1. + SCROLLBAR_SPACING); let scrollbar_multiplier = bounds_length - viewport_size; - let scrollbar_position = DVec2::splat(0.5) - (bounds1.lerp(bounds2, 0.5) - viewport_mid) / scrollbar_multiplier; let scrollbar_size = viewport_size / bounds_length; responses.push_back( FrontendMessage::UpdateScrollbars { From b99a1e18e16cad2fea56c91ea43943bd15a44d32 Mon Sep 17 00:00:00 2001 From: 0hypercube <0hypercube@gmail.com> Date: Mon, 16 Aug 2021 10:14:29 +0100 Subject: [PATCH 06/10] Fix scrollbar spacing --- editor/src/document/document_file.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/editor/src/document/document_file.rs b/editor/src/document/document_file.rs index cc3e55fb44..e94d55f161 100644 --- a/editor/src/document/document_file.rs +++ b/editor/src/document/document_file.rs @@ -406,16 +406,15 @@ impl MessageHandler for DocumentMessageHand } .into(), ); - let scale = 0.5 + 0.5 * self.layerdata(&[]).scale; 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; + let bounds1 = bounds1.min(viewport_mid) - viewport_size * 0.5; + let bounds2 = bounds2.max(viewport_mid) + viewport_size * 0.5; + 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_position = scrollbar_position * (1. - SCROLLBAR_SPACING) + SCROLLBAR_SPACING / 2.; - let bounds_length = bounds_length * (1. + SCROLLBAR_SPACING); + //let scrollbar_position = scrollbar_position * (1. - SCROLLBAR_SPACING) + SCROLLBAR_SPACING / 2.; + //let bounds_length = bounds_length * (1. + SCROLLBAR_SPACING); let scrollbar_multiplier = bounds_length - viewport_size; let scrollbar_size = viewport_size / bounds_length; responses.push_back( From f94a898219f875aa5deeee42ff987694983a0543 Mon Sep 17 00:00:00 2001 From: 0hypercube <0hypercube@gmail.com> Date: Mon, 16 Aug 2021 11:14:41 +0100 Subject: [PATCH 07/10] Smooth end of scrollbars --- editor/src/consts.rs | 4 +++- editor/src/document/document_file.rs | 9 ++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/editor/src/consts.rs b/editor/src/consts.rs index 62dd7c8917..c9e42a81d7 100644 --- a/editor/src/consts.rs +++ b/editor/src/consts.rs @@ -20,7 +20,9 @@ pub const LINE_ROTATE_SNAP_ANGLE: f64 = 15.; pub const SELECTION_TOLERANCE: f64 = 1.0; // SCROLLBARS -pub const SCROLLBAR_SPACING: f64 = 0.2; +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"; diff --git a/editor/src/document/document_file.rs b/editor/src/document/document_file.rs index e94d55f161..700b21979c 100644 --- a/editor/src/document/document_file.rs +++ b/editor/src/document/document_file.rs @@ -1,6 +1,6 @@ pub use super::layer_panel::*; use crate::{ - consts::{FILE_EXPORT_SUFFIX, FILE_SAVE_SUFFIX, SCROLLBAR_SPACING}, + consts::{ASYMPTOTIC_EFFECT, FILE_EXPORT_SUFFIX, FILE_SAVE_SUFFIX, SCALE_EFFECT, SCROLLBAR_SPACING}, frontend::layer_panel::*, EditorError, }; @@ -406,15 +406,14 @@ impl MessageHandler for DocumentMessageHand } .into(), ); + 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 * 0.5; - let bounds2 = bounds2.max(viewport_mid) + viewport_size * 0.5; + 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_position = scrollbar_position * (1. - SCROLLBAR_SPACING) + SCROLLBAR_SPACING / 2.; - //let bounds_length = bounds_length * (1. + SCROLLBAR_SPACING); let scrollbar_multiplier = bounds_length - viewport_size; let scrollbar_size = viewport_size / bounds_length; responses.push_back( From 65bf7d9af24be30b3a7b74b1304e2c1b9d736ff3 Mon Sep 17 00:00:00 2001 From: 0hypercube <0hypercube@gmail.com> Date: Mon, 16 Aug 2021 11:30:49 +0100 Subject: [PATCH 08/10] Add page up and down --- editor/src/document/movement_handler.rs | 11 ++++++++++- editor/src/input/input_mapper.rs | 4 ++++ editor/src/input/keyboard.rs | 2 ++ frontend/wasm/src/wrappers.rs | 2 ++ 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/editor/src/document/movement_handler.rs b/editor/src/document/movement_handler.rs index 00fa5c6839..79ac524a1f 100644 --- a/editor/src/document/movement_handler.rs +++ b/editor/src/document/movement_handler.rs @@ -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 { 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 Key { "]" => KeyRightBracket, "{" => KeyLeftCurlyBracket, "}" => KeyRightCurlyBracket, + "pageup" => KeyPageUp, + "pagedown" => KeyPageDown, _ => UnknownKey, } } From 78d736dbc41971687097ee502464523ecd041978 Mon Sep 17 00:00:00 2001 From: 0hypercube <0hypercube@gmail.com> Date: Mon, 16 Aug 2021 11:56:29 +0100 Subject: [PATCH 09/10] Page up and down on click in scrollbar track --- frontend/src/components/panels/Document.vue | 10 ++++++++++ .../widgets/scrollbars/PersistentScrollbar.vue | 6 +++--- frontend/wasm/src/document.rs | 7 +++++++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/panels/Document.vue b/frontend/src/components/panels/Document.vue index 2fcb4af666..5515d5af8c 100644 --- a/frontend/src/components/panels/Document.vue +++ b/frontend/src/components/panels/Document.vue @@ -128,6 +128,7 @@ :handlePosition="scrollbarPos.y" @update:handlePosition="translateCanvasY" v-model:handleLength="scrollbarSize.y" + @pressTrack="pageY" :class="'right-scrollbar'" /> @@ -138,6 +139,7 @@ :handlePosition="scrollbarPos.x" @update:handlePosition="translateCanvasX" v-model:handleLength="scrollbarSize.x" + @pressTrack="pageX" :class="'bottom-scrollbar'" /> @@ -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); }, diff --git a/frontend/src/components/widgets/scrollbars/PersistentScrollbar.vue b/frontend/src/components/widgets/scrollbars/PersistentScrollbar.vue index e0d1b1293b..b26cca79c9 100644 --- a/frontend/src/components/widgets/scrollbars/PersistentScrollbar.vue +++ b/frontend/src/components/widgets/scrollbars/PersistentScrollbar.vue @@ -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() { diff --git a/frontend/wasm/src/document.rs b/frontend/wasm/src/document.rs index f85783fee1..eba56529e6 100644 --- a/frontend/wasm/src/document.rs +++ b/frontend/wasm/src/document.rs @@ -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) -> Result<(), JsValue> { From f6a2732117c4b4305a846bc5afc5c05c1f5c39da Mon Sep 17 00:00:00 2001 From: 0hypercube <0hypercube@gmail.com> Date: Mon, 16 Aug 2021 20:58:07 +0100 Subject: [PATCH 10/10] Add shift pageup to translate horizontally --- editor/src/input/input_mapper.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/editor/src/input/input_mapper.rs b/editor/src/input/input_mapper.rs index d2b92f4ab1..9c94f9b439 100644 --- a/editor/src/input/input_mapper.rs +++ b/editor/src/input/input_mapper.rs @@ -194,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]}, @@ -209,8 +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]}, @@ -218,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]},