Simplify platform-specific keyboard layouts with Accel key and global platform variable (#748)

* Simplify platform-specific keyboard layouts with Accel key and global platform variable


Co-authored-by: Dennis <dennis@kobert.dev>
This commit is contained in:
Keavon Chambers
2022-08-13 13:28:02 +02:00
committed by GitHub
co-authored by Dennis
parent d8ae727ea9
commit 776992a297
24 changed files with 240 additions and 315 deletions
Generated
+1
View File
@@ -215,6 +215,7 @@ dependencies = [
"graphite-proc-macros", "graphite-proc-macros",
"kurbo", "kurbo",
"log", "log",
"once_cell",
"rand_chacha", "rand_chacha",
"remain", "remain",
"serde", "serde",
+1
View File
@@ -25,6 +25,7 @@ kurbo = { git = "https://github.com/linebender/kurbo.git", features = [
] } ] }
remain = "0.2.2" remain = "0.2.2"
derivative = "2.2.0" derivative = "2.2.0"
once_cell = "1.13.0" # Remove when `core::cell::OnceCell` is stabilized (<https://doc.rust-lang.org/core/cell/struct.OnceCell.html>)
[dependencies.graphene] [dependencies.graphene]
path = "../graphene" path = "../graphene"
+15 -18
View File
@@ -17,6 +17,7 @@ struct DispatcherMessageHandlers {
broadcast_message_handler: BroadcastMessageHandler, broadcast_message_handler: BroadcastMessageHandler,
debug_message_handler: DebugMessageHandler, debug_message_handler: DebugMessageHandler,
dialog_message_handler: DialogMessageHandler, dialog_message_handler: DialogMessageHandler,
globals_message_handler: GlobalsMessageHandler,
input_mapper_message_handler: InputMapperMessageHandler, input_mapper_message_handler: InputMapperMessageHandler,
input_preprocessor_message_handler: InputPreprocessorMessageHandler, input_preprocessor_message_handler: InputPreprocessorMessageHandler,
layout_message_handler: LayoutMessageHandler, layout_message_handler: LayoutMessageHandler,
@@ -127,26 +128,25 @@ impl Dispatcher {
self.responses.push(message); self.responses.push(message);
} }
} }
Globals(message) => {
self.message_handlers.globals_message_handler.process_message(message, (), &mut queue);
}
InputMapper(message) => { InputMapper(message) => {
let actions = self.collect_actions(); let actions = self.collect_actions();
let keyboard_platform = self.message_handlers.portfolio_message_handler.platform.as_keyboard_platform_layout();
self.message_handlers self.message_handlers
.input_mapper_message_handler .input_mapper_message_handler
.process_message(message, (&self.message_handlers.input_preprocessor_message_handler, keyboard_platform, actions), &mut queue); .process_message(message, (&self.message_handlers.input_preprocessor_message_handler, actions), &mut queue);
} }
InputPreprocessor(message) => { InputPreprocessor(message) => {
let keyboard_platform = self.message_handlers.portfolio_message_handler.platform.as_keyboard_platform_layout(); let keyboard_platform = GLOBAL_PLATFORM.get().expect("Failed to get GLOBAL_PLATFORM").as_keyboard_platform_layout();
self.message_handlers.input_preprocessor_message_handler.process_message(message, keyboard_platform, &mut queue); self.message_handlers.input_preprocessor_message_handler.process_message(message, keyboard_platform, &mut queue);
} }
Layout(message) => { Layout(message) => {
let keyboard_platform = self.message_handlers.portfolio_message_handler.platform.as_keyboard_platform_layout(); let action_input_mapping = &|action_to_find: &MessageDiscriminant| self.message_handlers.input_mapper_message_handler.action_input_mapping(action_to_find);
let action_input_mapping = &|action_to_find: &MessageDiscriminant| self.message_handlers.input_mapper_message_handler.action_input_mapping(action_to_find, keyboard_platform);
self.message_handlers self.message_handlers.layout_message_handler.process_message(message, action_input_mapping, &mut queue);
.layout_message_handler
.process_message(message, (action_input_mapping, keyboard_platform), &mut queue);
} }
Portfolio(message) => { Portfolio(message) => {
self.message_handlers self.message_handlers
@@ -243,7 +243,6 @@ impl Dispatcher {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::application::set_uuid_seed;
use crate::application::Editor; use crate::application::Editor;
use crate::messages::portfolio::document::utility_types::clipboards::Clipboard; use crate::messages::portfolio::document::utility_types::clipboards::Clipboard;
use crate::messages::prelude::*; use crate::messages::prelude::*;
@@ -262,14 +261,17 @@ mod test {
/// 2. A blue shape /// 2. A blue shape
/// 3. A green ellipse /// 3. A green ellipse
fn create_editor_with_three_layers() -> Editor { fn create_editor_with_three_layers() -> Editor {
set_uuid_seed(0); init_logger();
let mut editor = Editor::new(); let mut editor = Editor::create();
editor.new_document(); editor.new_document();
editor.select_primary_color(Color::RED); editor.select_primary_color(Color::RED);
editor.draw_rect(100., 200., 300., 400.); editor.draw_rect(100., 200., 300., 400.);
editor.select_primary_color(Color::BLUE); editor.select_primary_color(Color::BLUE);
editor.draw_shape(10., 1200., 1300., 400.); editor.draw_shape(10., 1200., 1300., 400.);
editor.select_primary_color(Color::GREEN); editor.select_primary_color(Color::GREEN);
editor.draw_ellipse(104., 1200., 1300., 400.); editor.draw_ellipse(104., 1200., 1300., 400.);
@@ -282,7 +284,6 @@ mod test {
/// - paste /// - paste
/// - assert that ellipse was copied /// - assert that ellipse was copied
fn copy_paste_single_layer() { fn copy_paste_single_layer() {
init_logger();
let mut editor = create_editor_with_three_layers(); let mut editor = create_editor_with_three_layers();
let document_before_copy = editor.dispatcher.message_handlers.portfolio_message_handler.active_document().unwrap().graphene_document.clone(); let document_before_copy = editor.dispatcher.message_handlers.portfolio_message_handler.active_document().unwrap().graphene_document.clone();
@@ -316,7 +317,6 @@ mod test {
/// - paste /// - paste
/// - assert that shape was copied /// - assert that shape was copied
fn copy_paste_single_layer_from_middle() { fn copy_paste_single_layer_from_middle() {
init_logger();
let mut editor = create_editor_with_three_layers(); let mut editor = create_editor_with_three_layers();
let document_before_copy = editor.dispatcher.message_handlers.portfolio_message_handler.active_document().unwrap().graphene_document.clone(); let document_before_copy = editor.dispatcher.message_handlers.portfolio_message_handler.active_document().unwrap().graphene_document.clone();
@@ -351,7 +351,6 @@ mod test {
#[test] #[test]
fn copy_paste_folder() { fn copy_paste_folder() {
init_logger();
let mut editor = create_editor_with_three_layers(); let mut editor = create_editor_with_three_layers();
const FOLDER_INDEX: usize = 3; const FOLDER_INDEX: usize = 3;
@@ -447,7 +446,6 @@ mod test {
/// - paste /// - paste
/// - paste /// - paste
fn copy_paste_deleted_layers() { fn copy_paste_deleted_layers() {
init_logger();
let mut editor = create_editor_with_three_layers(); let mut editor = create_editor_with_three_layers();
const ELLIPSE_INDEX: usize = 2; const ELLIPSE_INDEX: usize = 2;
@@ -499,7 +497,6 @@ mod test {
/// - select ellipse and rect /// - select ellipse and rect
/// - move them down and back up again /// - move them down and back up again
fn move_selection() { fn move_selection() {
init_logger();
let mut editor = create_editor_with_three_layers(); let mut editor = create_editor_with_three_layers();
fn map_to_vec(paths: Vec<&[LayerId]>) -> Vec<Vec<LayerId>> { fn map_to_vec(paths: Vec<&[LayerId]>) -> Vec<Vec<LayerId>> {
@@ -555,8 +552,8 @@ mod test {
}; };
init_logger(); init_logger();
set_uuid_seed(0); let mut editor = Editor::create();
let mut editor = Editor::new();
let test_file = include_str!("../graphite-test-document.graphite"); let test_file = include_str!("../graphite-test-document.graphite");
let responses = editor.handle_message(PortfolioMessage::OpenDocumentFile { let responses = editor.handle_message(PortfolioMessage::OpenDocumentFile {
document_name: "Graphite Version Test".into(), document_name: "Graphite Version Test".into(),
@@ -0,0 +1,5 @@
use crate::messages::portfolio::document::utility_types::misc::Platform;
use once_cell::sync::OnceCell;
pub static GLOBAL_PLATFORM: OnceCell<Platform> = OnceCell::new();
@@ -0,0 +1,10 @@
use crate::messages::portfolio::document::utility_types::misc::Platform;
use crate::messages::prelude::*;
use serde::{Deserialize, Serialize};
#[impl_message(Message, Globals)]
#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)]
pub enum GlobalsMessage {
SetPlatform { platform: Platform },
}
@@ -0,0 +1,18 @@
use crate::messages::prelude::*;
#[derive(Debug, Default)]
pub struct GlobalsMessageHandler {}
impl MessageHandler<GlobalsMessage, ()> for GlobalsMessageHandler {
#[remain::check]
fn process_message(&mut self, message: GlobalsMessage, _data: (), _responses: &mut VecDeque<Message>) {
match message {
GlobalsMessage::SetPlatform { platform } => {
GLOBAL_PLATFORM.set(platform).expect("Failed to set GLOBAL_PLATFORM");
}
}
}
advertise_actions!(GlobalsMessageDiscriminant;
);
}
+9
View File
@@ -0,0 +1,9 @@
mod globals_message;
mod globals_message_handler;
pub mod global_variables;
#[doc(inline)]
pub use globals_message::{GlobalsMessage, GlobalsMessageDiscriminant};
#[doc(inline)]
pub use globals_message_handler::GlobalsMessageHandler;
@@ -4,7 +4,6 @@ use crate::messages::input_mapper::utility_types::macros::*;
use crate::messages::input_mapper::utility_types::misc::MappingEntry; use crate::messages::input_mapper::utility_types::misc::MappingEntry;
use crate::messages::input_mapper::utility_types::misc::{KeyMappingEntries, Mapping}; use crate::messages::input_mapper::utility_types::misc::{KeyMappingEntries, Mapping};
use crate::messages::portfolio::document::utility_types::clipboards::Clipboard; use crate::messages::portfolio::document::utility_types::clipboards::Clipboard;
use crate::messages::portfolio::document::utility_types::misc::KeyboardPlatformLayout;
use crate::messages::prelude::*; use crate::messages::prelude::*;
use glam::DVec2; use glam::DVec2;
@@ -75,10 +74,7 @@ pub fn default_mapping() -> Mapping {
// TextToolMessage // TextToolMessage
entry!(KeyUp(Lmb); action_dispatch=TextToolMessage::Interact), entry!(KeyUp(Lmb); action_dispatch=TextToolMessage::Interact),
entry!(KeyDown(Escape); action_dispatch=TextToolMessage::Abort), entry!(KeyDown(Escape); action_dispatch=TextToolMessage::Abort),
entry_multiplatform!( entry!(KeyDown(Enter); modifiers=[Accel], action_dispatch=TextToolMessage::CommitText),
standard!(KeyDown(Enter); modifiers=[Control], action_dispatch=TextToolMessage::CommitText),
mac_only!(KeyDown(Enter); modifiers=[Command], action_dispatch=TextToolMessage::CommitText),
),
// //
// GradientToolMessage // GradientToolMessage
entry!(KeyDown(Lmb); action_dispatch=GradientToolMessage::PointerDown), entry!(KeyDown(Lmb); action_dispatch=GradientToolMessage::PointerDown),
@@ -159,10 +155,7 @@ pub fn default_mapping() -> Mapping {
entry!(KeyDown(KeyM); action_dispatch=ToolMessage::ActivateToolRectangle), entry!(KeyDown(KeyM); action_dispatch=ToolMessage::ActivateToolRectangle),
entry!(KeyDown(KeyE); action_dispatch=ToolMessage::ActivateToolEllipse), entry!(KeyDown(KeyE); action_dispatch=ToolMessage::ActivateToolEllipse),
entry!(KeyDown(KeyY); action_dispatch=ToolMessage::ActivateToolShape), entry!(KeyDown(KeyY); action_dispatch=ToolMessage::ActivateToolShape),
entry_multiplatform!( entry!(KeyDown(KeyX); modifiers=[Shift, Accel], action_dispatch=ToolMessage::ResetColors),
standard!(KeyDown(KeyX); modifiers=[Shift, Control], action_dispatch=ToolMessage::ResetColors),
mac_only!(KeyDown(KeyX); modifiers=[Shift, Command], action_dispatch=ToolMessage::ResetColors),
),
entry!(KeyDown(KeyX); modifiers=[Shift], action_dispatch=ToolMessage::SwapColors), entry!(KeyDown(KeyX); modifiers=[Shift], action_dispatch=ToolMessage::SwapColors),
entry!(KeyDown(KeyC); modifiers=[Alt], action_dispatch=ToolMessage::SelectRandomPrimaryColor), entry!(KeyDown(KeyC); modifiers=[Alt], action_dispatch=ToolMessage::SelectRandomPrimaryColor),
// //
@@ -170,70 +163,22 @@ pub fn default_mapping() -> Mapping {
entry!(KeyDown(Delete); action_dispatch=DocumentMessage::DeleteSelectedLayers), entry!(KeyDown(Delete); action_dispatch=DocumentMessage::DeleteSelectedLayers),
entry!(KeyDown(Backspace); action_dispatch=DocumentMessage::DeleteSelectedLayers), entry!(KeyDown(Backspace); action_dispatch=DocumentMessage::DeleteSelectedLayers),
entry!(KeyDown(KeyP); modifiers=[Alt], action_dispatch=DocumentMessage::DebugPrintDocument), entry!(KeyDown(KeyP); modifiers=[Alt], action_dispatch=DocumentMessage::DebugPrintDocument),
entry_multiplatform!( entry!(KeyDown(KeyZ); modifiers=[Accel, Shift], action_dispatch=DocumentMessage::Redo),
standard!(KeyDown(KeyZ); modifiers=[Control, Shift], action_dispatch=DocumentMessage::Redo), entry!(KeyDown(KeyZ); modifiers=[Accel], action_dispatch=DocumentMessage::Undo),
mac_only!(KeyDown(KeyZ); modifiers=[Command, Shift], action_dispatch=DocumentMessage::Redo), entry!(KeyDown(KeyA); modifiers=[Accel, Alt], action_dispatch=DocumentMessage::DeselectAllLayers),
), entry!(KeyDown(KeyA); modifiers=[Accel], action_dispatch=DocumentMessage::SelectAllLayers),
entry_multiplatform!( entry!(KeyDown(KeyS); modifiers=[Accel], action_dispatch=DocumentMessage::SaveDocument),
standard!(KeyDown(KeyZ); modifiers=[Control], action_dispatch=DocumentMessage::Undo), entry!(KeyDown(KeyD); modifiers=[Accel], action_dispatch=DocumentMessage::DuplicateSelectedLayers),
mac_only!(KeyDown(KeyZ); modifiers=[Command], action_dispatch=DocumentMessage::Undo), entry!(KeyDown(KeyG); modifiers=[Accel], action_dispatch=DocumentMessage::GroupSelectedLayers),
), entry!(KeyDown(KeyG); modifiers=[Accel, Shift], action_dispatch=DocumentMessage::UngroupSelectedLayers),
entry_multiplatform!( entry!(KeyDown(KeyN); modifiers=[Accel, Shift], action_dispatch=DocumentMessage::CreateEmptyFolder { container_path: vec![] }),
standard!(KeyDown(KeyA); modifiers=[Control, Alt], action_dispatch=DocumentMessage::DeselectAllLayers), entry!(KeyDown(Digit0); modifiers=[Accel], action_dispatch=DocumentMessage::ZoomCanvasToFitAll),
mac_only!(KeyDown(KeyA); modifiers=[Command, Alt], action_dispatch=DocumentMessage::DeselectAllLayers), entry!(KeyDown(Digit1); modifiers=[Accel], action_dispatch=DocumentMessage::ZoomCanvasTo100Percent),
), entry!(KeyDown(Digit2); modifiers=[Accel], action_dispatch=DocumentMessage::ZoomCanvasTo200Percent),
entry_multiplatform!( entry!(KeyDown(BracketLeft); modifiers=[Accel, Shift], action_dispatch=DocumentMessage::SelectedLayersLowerToBack),
standard!(KeyDown(KeyA); modifiers=[Control], action_dispatch=DocumentMessage::SelectAllLayers), entry!(KeyDown(BracketRight); modifiers=[Accel, Shift], action_dispatch=DocumentMessage::SelectedLayersRaiseToFront),
mac_only!(KeyDown(KeyA); modifiers=[Command], action_dispatch=DocumentMessage::SelectAllLayers), entry!(KeyDown(BracketLeft); modifiers=[Accel], action_dispatch=DocumentMessage::SelectedLayersLower),
), entry!(KeyDown(BracketRight); modifiers=[Accel], action_dispatch=DocumentMessage::SelectedLayersRaise),
entry_multiplatform!(
standard!(KeyDown(KeyS); modifiers=[Control], action_dispatch=DocumentMessage::SaveDocument),
mac_only!(KeyDown(KeyS); modifiers=[Command], action_dispatch=DocumentMessage::SaveDocument),
),
entry_multiplatform!(
standard!(KeyDown(KeyD); modifiers=[Control], action_dispatch=DocumentMessage::DuplicateSelectedLayers),
mac_only!(KeyDown(KeyD); modifiers=[Command], action_dispatch=DocumentMessage::DuplicateSelectedLayers),
),
entry_multiplatform!(
standard!(KeyDown(KeyG); modifiers=[Control], action_dispatch=DocumentMessage::GroupSelectedLayers),
mac_only!(KeyDown(KeyG); modifiers=[Command], action_dispatch=DocumentMessage::GroupSelectedLayers),
),
entry_multiplatform!(
standard!(KeyDown(KeyG); modifiers=[Control, Shift], action_dispatch=DocumentMessage::UngroupSelectedLayers),
mac_only!(KeyDown(KeyG); modifiers=[Command, Shift], action_dispatch=DocumentMessage::UngroupSelectedLayers),
),
entry_multiplatform!(
standard!(KeyDown(KeyN); modifiers=[Control, Shift], action_dispatch=DocumentMessage::CreateEmptyFolder { container_path: vec![] }),
mac_only!(KeyDown(KeyN); modifiers=[Command, Shift], action_dispatch=DocumentMessage::CreateEmptyFolder { container_path: vec![] }),
),
entry_multiplatform!(
standard!(KeyDown(Digit0); modifiers=[Control], action_dispatch=DocumentMessage::ZoomCanvasToFitAll),
mac_only!(KeyDown(Digit0); modifiers=[Command], action_dispatch=DocumentMessage::ZoomCanvasToFitAll),
),
entry_multiplatform!(
standard!(KeyDown(Digit1); modifiers=[Control], action_dispatch=DocumentMessage::ZoomCanvasTo100Percent),
mac_only!(KeyDown(Digit1); modifiers=[Command], action_dispatch=DocumentMessage::ZoomCanvasTo100Percent),
),
entry_multiplatform!(
standard!(KeyDown(Digit2); modifiers=[Control], action_dispatch=DocumentMessage::ZoomCanvasTo200Percent),
mac_only!(KeyDown(Digit2); modifiers=[Command], action_dispatch=DocumentMessage::ZoomCanvasTo200Percent),
),
entry_multiplatform!(
standard!(KeyDown(BracketLeft); modifiers=[Control, Shift], action_dispatch=DocumentMessage::SelectedLayersLowerToBack),
mac_only!(KeyDown(BracketLeft); modifiers=[Command, Shift], action_dispatch=DocumentMessage::SelectedLayersLowerToBack),
),
entry_multiplatform!(
standard!(KeyDown(BracketRight); modifiers=[Control, Shift], action_dispatch=DocumentMessage::SelectedLayersRaiseToFront),
mac_only!(KeyDown(BracketRight); modifiers=[Command, Shift], action_dispatch=DocumentMessage::SelectedLayersRaiseToFront),
),
entry_multiplatform!(
standard!(KeyDown(BracketLeft); modifiers=[Control], action_dispatch=DocumentMessage::SelectedLayersLower),
mac_only!(KeyDown(BracketLeft); modifiers=[Command], action_dispatch=DocumentMessage::SelectedLayersLower),
),
entry_multiplatform!(
standard!(KeyDown(BracketRight); modifiers=[Control], action_dispatch=DocumentMessage::SelectedLayersRaise),
mac_only!(KeyDown(BracketRight); modifiers=[Command], action_dispatch=DocumentMessage::SelectedLayersRaise),
),
entry!(KeyDown(ArrowUp); modifiers=[Shift, ArrowLeft], action_dispatch=DocumentMessage::NudgeSelectedLayers { delta_x: -BIG_NUDGE_AMOUNT, delta_y: -BIG_NUDGE_AMOUNT }), entry!(KeyDown(ArrowUp); modifiers=[Shift, ArrowLeft], action_dispatch=DocumentMessage::NudgeSelectedLayers { delta_x: -BIG_NUDGE_AMOUNT, delta_y: -BIG_NUDGE_AMOUNT }),
entry!(KeyDown(ArrowUp); modifiers=[Shift, ArrowRight], action_dispatch=DocumentMessage::NudgeSelectedLayers { delta_x: BIG_NUDGE_AMOUNT, delta_y: -BIG_NUDGE_AMOUNT }), entry!(KeyDown(ArrowUp); modifiers=[Shift, ArrowRight], action_dispatch=DocumentMessage::NudgeSelectedLayers { delta_x: BIG_NUDGE_AMOUNT, delta_y: -BIG_NUDGE_AMOUNT }),
entry!(KeyDown(ArrowUp); modifiers=[Shift], action_dispatch=DocumentMessage::NudgeSelectedLayers { delta_x: 0., delta_y: -BIG_NUDGE_AMOUNT }), entry!(KeyDown(ArrowUp); modifiers=[Shift], action_dispatch=DocumentMessage::NudgeSelectedLayers { delta_x: 0., delta_y: -BIG_NUDGE_AMOUNT }),
@@ -271,18 +216,9 @@ pub fn default_mapping() -> Mapping {
entry!(KeyUp(Mmb); action_dispatch=NavigationMessage::TransformCanvasEnd), entry!(KeyUp(Mmb); action_dispatch=NavigationMessage::TransformCanvasEnd),
entry!(KeyDown(Lmb); modifiers=[Space], action_dispatch=NavigationMessage::TranslateCanvasBegin), entry!(KeyDown(Lmb); modifiers=[Space], action_dispatch=NavigationMessage::TranslateCanvasBegin),
entry!(KeyUp(Lmb); modifiers=[Space], action_dispatch=NavigationMessage::TransformCanvasEnd), entry!(KeyUp(Lmb); modifiers=[Space], action_dispatch=NavigationMessage::TransformCanvasEnd),
entry_multiplatform!( entry!(KeyDown(NumpadAdd); modifiers=[Accel], action_dispatch=NavigationMessage::IncreaseCanvasZoom { center_on_mouse: false }),
standard!(KeyDown(NumpadAdd); modifiers=[Control], action_dispatch=NavigationMessage::IncreaseCanvasZoom { center_on_mouse: false }), entry!(KeyDown(Equal); modifiers=[Accel], action_dispatch=NavigationMessage::IncreaseCanvasZoom { center_on_mouse: false }),
mac_only!(KeyDown(NumpadAdd); modifiers=[Command], action_dispatch=NavigationMessage::IncreaseCanvasZoom { center_on_mouse: false }), entry!(KeyDown(Minus); modifiers=[Accel], action_dispatch=NavigationMessage::DecreaseCanvasZoom { center_on_mouse: false }),
),
entry_multiplatform!(
standard!(KeyDown(Equal); modifiers=[Control], action_dispatch=NavigationMessage::IncreaseCanvasZoom { center_on_mouse: false }),
mac_only!(KeyDown(Equal); modifiers=[Command], action_dispatch=NavigationMessage::IncreaseCanvasZoom { center_on_mouse: false }),
),
entry_multiplatform!(
standard!(KeyDown(Minus); modifiers=[Control], action_dispatch=NavigationMessage::DecreaseCanvasZoom { center_on_mouse: false }),
mac_only!(KeyDown(Minus); modifiers=[Command], action_dispatch=NavigationMessage::DecreaseCanvasZoom { center_on_mouse: false }),
),
entry!(WheelScroll; modifiers=[Control], action_dispatch=NavigationMessage::WheelCanvasZoom), entry!(WheelScroll; modifiers=[Control], action_dispatch=NavigationMessage::WheelCanvasZoom),
entry!(WheelScroll; modifiers=[Shift], action_dispatch=NavigationMessage::WheelCanvasTranslate { use_y_as_x: true }), entry!(WheelScroll; modifiers=[Shift], action_dispatch=NavigationMessage::WheelCanvasTranslate { use_y_as_x: true }),
entry!(WheelScroll; action_dispatch=NavigationMessage::WheelCanvasTranslate { use_y_as_x: false }), entry!(WheelScroll; action_dispatch=NavigationMessage::WheelCanvasTranslate { use_y_as_x: false }),
@@ -292,47 +228,19 @@ pub fn default_mapping() -> Mapping {
entry!(KeyDown(PageDown); action_dispatch=NavigationMessage::TranslateCanvasByViewportFraction { delta: DVec2::new(0., -1.) }), entry!(KeyDown(PageDown); action_dispatch=NavigationMessage::TranslateCanvasByViewportFraction { delta: DVec2::new(0., -1.) }),
// //
// PortfolioMessage // PortfolioMessage
entry_multiplatform!( entry!(KeyDown(KeyO); modifiers=[Accel], action_dispatch=PortfolioMessage::OpenDocument),
standard!(KeyDown(KeyO); modifiers=[Control], action_dispatch=PortfolioMessage::OpenDocument), entry!(KeyDown(KeyI); modifiers=[Accel], action_dispatch=PortfolioMessage::Import),
mac_only!(KeyDown(KeyO); modifiers=[Command], action_dispatch=PortfolioMessage::OpenDocument),
),
entry_multiplatform!(
standard!(KeyDown(KeyI); modifiers=[Control], action_dispatch=PortfolioMessage::Import),
mac_only!(KeyDown(KeyI); modifiers=[Command], action_dispatch=PortfolioMessage::Import),
),
entry!(KeyDown(Tab); modifiers=[Control], action_dispatch=PortfolioMessage::NextDocument), entry!(KeyDown(Tab); modifiers=[Control], action_dispatch=PortfolioMessage::NextDocument),
entry!(KeyDown(Tab); modifiers=[Control, Shift], action_dispatch=PortfolioMessage::PrevDocument), entry!(KeyDown(Tab); modifiers=[Control, Shift], action_dispatch=PortfolioMessage::PrevDocument),
entry_multiplatform!( entry!(KeyDown(KeyW); modifiers=[Accel], action_dispatch=PortfolioMessage::CloseActiveDocumentWithConfirmation),
standard!(KeyDown(KeyW); modifiers=[Control], action_dispatch=PortfolioMessage::CloseActiveDocumentWithConfirmation), entry!(KeyDown(KeyX); modifiers=[Accel], action_dispatch=PortfolioMessage::Cut { clipboard: Clipboard::Device }),
mac_only!(KeyDown(KeyW); modifiers=[Command], action_dispatch=PortfolioMessage::CloseActiveDocumentWithConfirmation), entry!(KeyDown(KeyC); modifiers=[Accel], action_dispatch=PortfolioMessage::Copy { clipboard: Clipboard::Device }),
), entry!(KeyDown(KeyV); modifiers=[Accel], action_dispatch=FrontendMessage::TriggerPaste),
entry_multiplatform!(
standard!(KeyDown(KeyX); modifiers=[Control], action_dispatch=PortfolioMessage::Cut { clipboard: Clipboard::Device }),
mac_only!(KeyDown(KeyX); modifiers=[Command], action_dispatch=PortfolioMessage::Cut { clipboard: Clipboard::Device }),
),
entry_multiplatform!(
standard!(KeyDown(KeyC); modifiers=[Control], action_dispatch=PortfolioMessage::Copy { clipboard: Clipboard::Device }),
mac_only!(KeyDown(KeyC); modifiers=[Command], action_dispatch=PortfolioMessage::Copy { clipboard: Clipboard::Device }),
),
entry_multiplatform!(
// This shortcut is intercepted in the frontend; it exists here only as a shortcut mapping source
standard!(KeyDown(KeyV); modifiers=[Control], action_dispatch=FrontendMessage::TriggerPaste),
mac_only!(KeyDown(KeyV); modifiers=[Command], action_dispatch=FrontendMessage::TriggerPaste),
),
// //
// DialogMessage // DialogMessage
entry_multiplatform!( entry!(KeyDown(KeyN); modifiers=[Accel], action_dispatch=DialogMessage::RequestNewDocumentDialog),
standard!(KeyDown(KeyN); modifiers=[Control], action_dispatch=DialogMessage::RequestNewDocumentDialog), entry!(KeyDown(KeyW); modifiers=[Accel, Alt], action_dispatch=DialogMessage::CloseAllDocumentsWithConfirmation),
mac_only!(KeyDown(KeyN); modifiers=[Command], action_dispatch=DialogMessage::RequestNewDocumentDialog), entry!(KeyDown(KeyE); modifiers=[Accel], action_dispatch=DialogMessage::RequestExportDialog),
),
entry_multiplatform!(
standard!(KeyDown(KeyW); modifiers=[Control, Alt], action_dispatch=DialogMessage::CloseAllDocumentsWithConfirmation),
mac_only!(KeyDown(KeyW); modifiers=[Command, Alt], action_dispatch=DialogMessage::CloseAllDocumentsWithConfirmation),
),
entry_multiplatform!(
standard!(KeyDown(KeyE); modifiers=[Control], action_dispatch=DialogMessage::RequestExportDialog),
mac_only!(KeyDown(KeyE); modifiers=[Command], action_dispatch=DialogMessage::RequestExportDialog),
),
// //
// DebugMessage // DebugMessage
entry!(KeyDown(KeyT); modifiers=[Alt], action_dispatch=DebugMessage::ToggleTraceLogs), entry!(KeyDown(KeyT); modifiers=[Alt], action_dispatch=DebugMessage::ToggleTraceLogs),
@@ -350,7 +258,6 @@ pub fn default_mapping() -> Mapping {
MappingEntry { MappingEntry {
action: TransformLayerMessage::TypeDigit { digit: i as u8 }.into(), action: TransformLayerMessage::TypeDigit { digit: i as u8 }.into(),
input: InputMapperMessage::KeyDown(*key), input: InputMapperMessage::KeyDown(*key),
platform_layout: None,
modifiers: modifiers!(), modifiers: modifiers!(),
}, },
); );
@@ -1,7 +1,6 @@
use super::utility_types::input_keyboard::KeysGroup; use super::utility_types::input_keyboard::KeysGroup;
use super::utility_types::misc::Mapping; use super::utility_types::misc::Mapping;
use crate::messages::input_mapper::utility_types::input_keyboard::{self, Key}; use crate::messages::input_mapper::utility_types::input_keyboard::{self, Key};
use crate::messages::portfolio::document::utility_types::misc::KeyboardPlatformLayout;
use crate::messages::prelude::*; use crate::messages::prelude::*;
use std::fmt::Write; use std::fmt::Write;
@@ -11,11 +10,11 @@ pub struct InputMapperMessageHandler {
mapping: Mapping, mapping: Mapping,
} }
impl MessageHandler<InputMapperMessage, (&InputPreprocessorMessageHandler, KeyboardPlatformLayout, ActionList)> for InputMapperMessageHandler { impl MessageHandler<InputMapperMessage, (&InputPreprocessorMessageHandler, ActionList)> for InputMapperMessageHandler {
fn process_message(&mut self, message: InputMapperMessage, data: (&InputPreprocessorMessageHandler, KeyboardPlatformLayout, ActionList), responses: &mut VecDeque<Message>) { fn process_message(&mut self, message: InputMapperMessage, data: (&InputPreprocessorMessageHandler, ActionList), responses: &mut VecDeque<Message>) {
let (input, keyboard_platform, actions) = data; let (input, actions) = data;
if let Some(message) = self.mapping.match_input_message(message, &input.keyboard, actions, keyboard_platform) { if let Some(message) = self.mapping.match_input_message(message, &input.keyboard, actions) {
responses.push_back(message); responses.push_back(message);
} }
} }
@@ -44,7 +43,7 @@ impl InputMapperMessageHandler {
output.replace("Key", "") output.replace("Key", "")
} }
pub fn action_input_mapping(&self, action_to_find: &MessageDiscriminant, keyboard_platform: KeyboardPlatformLayout) -> Vec<KeysGroup> { pub fn action_input_mapping(&self, action_to_find: &MessageDiscriminant) -> Vec<KeysGroup> {
let key_up = self.mapping.key_up.iter(); let key_up = self.mapping.key_up.iter();
let key_down = self.mapping.key_down.iter(); let key_down = self.mapping.key_down.iter();
let double_click = std::iter::once(&self.mapping.double_click); let double_click = std::iter::once(&self.mapping.double_click);
@@ -56,8 +55,6 @@ impl InputMapperMessageHandler {
// Filter for the desired message // Filter for the desired message
let found_actions = all_mapping_entries.filter(|entry| entry.action.to_discriminant() == *action_to_find); let found_actions = all_mapping_entries.filter(|entry| entry.action.to_discriminant() == *action_to_find);
// Filter for a compatible keyboard platform layout
let found_actions = found_actions.filter(|entry| if let Some(layout) = entry.platform_layout { layout == keyboard_platform } else { true });
// Find the key combinations for all keymaps matching the desired action // Find the key combinations for all keymaps matching the desired action
assert!(std::mem::size_of::<usize>() >= std::mem::size_of::<Key>()); assert!(std::mem::size_of::<usize>() >= std::mem::size_of::<Key>());
@@ -20,6 +20,14 @@ const KEY_MASK_STORAGE_LENGTH: usize = (NUMBER_OF_KEYS + STORAGE_SIZE_BITS - 1)
pub type KeyStates = BitVector<KEY_MASK_STORAGE_LENGTH>; pub type KeyStates = BitVector<KEY_MASK_STORAGE_LENGTH>;
pub fn all_required_modifiers_pressed(keyboard_state: &KeyStates, modifiers: &KeyStates) -> bool {
// Find which currently pressed keys are also the modifiers in this hotkey entry, then compare those against the required modifiers to see if there are zero missing
let pressed_modifiers = *keyboard_state & *modifiers;
let all_modifiers_without_pressed_modifiers = *modifiers ^ pressed_modifiers;
all_modifiers_without_pressed_modifiers.is_empty()
}
pub enum KeyPosition { pub enum KeyPosition {
Pressed, Pressed,
Released, Released,
@@ -29,10 +37,10 @@ bitflags! {
#[derive(Default, Serialize, Deserialize)] #[derive(Default, Serialize, Deserialize)]
#[repr(transparent)] #[repr(transparent)]
pub struct ModifierKeys: u8 { pub struct ModifierKeys: u8 {
const SHIFT = 0b0000_0001; const SHIFT = 0b_0000_0001;
const ALT = 0b0000_0010; const ALT = 0b_0000_0010;
const CONTROL = 0b0000_0100; const CONTROL = 0b_0000_0100;
const META_OR_COMMAND = 0b0000_1000; const META_OR_COMMAND = 0b_0000_1000;
} }
} }
@@ -193,6 +201,7 @@ pub enum Key {
// Other keys that aren't part of the W3C spec // Other keys that aren't part of the W3C spec
Command, Command,
Accel,
Lmb, Lmb,
Rmb, Rmb,
Mmb, Mmb,
@@ -228,6 +237,8 @@ impl fmt::Display for Key {
return write!(f, "{}", key_name.chars().skip(KEY_PREFIX.len()).collect::<String>()); return write!(f, "{}", key_name.chars().skip(KEY_PREFIX.len()).collect::<String>());
} }
let keyboard_layout = || GLOBAL_PLATFORM.get().expect("Failed to get GLOBAL_PLATFORM").as_keyboard_platform_layout();
let name = match self { let name = match self {
// Writing system keys // Writing system keys
Self::Backquote => "`", Self::Backquote => "`",
@@ -243,7 +254,23 @@ impl fmt::Display for Key {
Self::Slash => "/", Self::Slash => "/",
// Functional keys // Functional keys
Self::Control => "Ctrl", Self::Alt => match keyboard_layout() {
KeyboardPlatformLayout::Standard => "Alt",
KeyboardPlatformLayout::Mac => "⌥",
},
Self::Meta => match keyboard_layout() {
KeyboardPlatformLayout::Standard => "⊞",
KeyboardPlatformLayout::Mac => "⌘",
},
Self::Shift => match keyboard_layout() {
KeyboardPlatformLayout::Standard => "Shift",
KeyboardPlatformLayout::Mac => "⇧",
},
Self::Control => match keyboard_layout() {
KeyboardPlatformLayout::Standard => "Ctrl",
KeyboardPlatformLayout::Mac => "⌃",
},
Self::Backspace => "⌫",
// Control pad keys // Control pad keys
Self::Delete => "Del", Self::Delete => "Del",
@@ -267,6 +294,13 @@ impl fmt::Display for Key {
Self::Escape => "Esc", Self::Escape => "Esc",
Self::PrintScreen => "PrtScr", Self::PrintScreen => "PrtScr",
// Other keys that aren't part of the W3C spec
Self::Command => "⌘",
Self::Accel => match keyboard_layout() {
KeyboardPlatformLayout::Standard => "Ctrl",
KeyboardPlatformLayout::Mac => "⌘",
},
_ => key_name.as_str(), _ => key_name.as_str(),
}; };
@@ -280,36 +314,31 @@ pub const NUMBER_OF_KEYS: usize = Key::NumKeys as usize;
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct KeysGroup(pub Vec<Key>); pub struct KeysGroup(pub Vec<Key>);
impl KeysGroup { impl fmt::Display for KeysGroup {
pub fn keys_text_shortcut(&self, keyboard_platform: KeyboardPlatformLayout) -> String { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
const JOINER_MARK: &str = "+"; const JOINER_MARK: &str = " ";
let mut joined = self let mut joined = self
.0 .0
.iter() .iter()
.map(|key| { .map(|key| {
let key_string = key.to_string(); let keyboard_layout = GLOBAL_PLATFORM.get().expect("Failed to get GLOBAL_PLATFORM").as_keyboard_platform_layout();
let key_is_modifier = matches!(*key, Key::Control | Key::Command | Key::Alt | Key::Shift | Key::Meta | Key::Accel);
if keyboard_platform == KeyboardPlatformLayout::Mac { if keyboard_layout == KeyboardPlatformLayout::Mac && key_is_modifier {
match key_string.as_str() { key.to_string()
"Command" => "⌘".to_string(),
"Control" => "⌃".to_string(),
"Alt" => "⌥".to_string(),
"Shift" => "⇧".to_string(),
_ => key_string + JOINER_MARK,
}
} else { } else {
key_string + JOINER_MARK key.to_string() + JOINER_MARK
} }
}) })
.collect::<String>(); .collect::<String>();
// Truncate to cut the joining character off the end if it's present // Cut the joining character off the end, if present
if joined.ends_with(JOINER_MARK) { if joined.ends_with(JOINER_MARK) {
joined.truncate(joined.len() - JOINER_MARK.len()); joined.truncate(joined.len() - JOINER_MARK.len());
} }
joined write!(f, "{}", joined)
} }
} }
@@ -13,28 +13,24 @@ macro_rules! modifiers {
/// Builds a slice of `MappingEntry` struct(s) that are used to: /// Builds a slice of `MappingEntry` struct(s) that are used to:
/// - ...dispatch the given `action_dispatch` as an output `Message` if its discriminant is a currently available action /// - ...dispatch the given `action_dispatch` as an output `Message` if its discriminant is a currently available action
/// - ...when the `InputMapperMessage` enum variant, as specified at the start and followed by a semicolon, is received /// - ...when the `InputMapperMessage` enum variant, as specified at the start and followed by a semicolon, is received
/// - ...while any further conditions are met, like the optional `modifiers` being pressed or `layout` matching the OS. /// - ...while the optional `modifiers` being pressed.
/// ///
/// Syntax: /// Syntax:
/// ```rs /// ```rs
/// entry_for_layout!(Key; modifiers?: Key[], refresh_keys?: Key[], action_dispatch: Message, layout: Option<KeyboardPlatformLayout>) /// entry_for_layout!(Key; modifiers?: Key[], refresh_keys?: Key[], action_dispatch: Message)
/// ``` /// ```
/// ///
/// To avoid having to specify the final `layout` argument, instead use the wrapper macros: [entry]!, [standard]!, and [mac]!.
/// The former sets the layout to `None` which means the key mapping is layout-agnostic and compatible with all platforms.
///
/// The actions system controls which actions are currently available. Those are provided by the different message handlers based on the current application state and context. /// The actions system controls which actions are currently available. Those are provided by the different message handlers based on the current application state and context.
/// Each handler adds or removes actions in the form of message discriminants. Here, we tie an input condition (such as a hotkey) to an action's full message. /// Each handler adds or removes actions in the form of message discriminants. Here, we tie an input condition (such as a hotkey) to an action's full message.
/// When an action is currently available, and the user enters that input, the action's message is dispatched on the message bus. /// When an action is currently available, and the user enters that input, the action's message is dispatched on the message bus.
macro_rules! entry_for_layout { macro_rules! entry {
($input:expr; $(modifiers=[$($modifier:ident),*],)? $(refresh_keys=[$($refresh:ident),* $(,)?],)? action_dispatch=$action_dispatch:expr,$(,)? layout=$layout:expr) => { ($input:expr; $(modifiers=[$($modifier:ident),*],)? $(refresh_keys=[$($refresh:ident),* $(,)?],)? action_dispatch=$action_dispatch:expr$(,)?) => {
&[ &[&[
// Cause the `action_dispatch` message to be sent when the specified input occurs. // Cause the `action_dispatch` message to be sent when the specified input occurs.
MappingEntry { MappingEntry {
action: $action_dispatch.into(), action: $action_dispatch.into(),
input: $input, input: $input,
modifiers: modifiers!($($($modifier),*)?), modifiers: modifiers!($($($modifier),*)?),
platform_layout: $layout,
}, },
// Also cause the `action_dispatch` message to be sent when any of the specified refresh keys change. // Also cause the `action_dispatch` message to be sent when any of the specified refresh keys change.
@@ -48,70 +44,15 @@ macro_rules! entry_for_layout {
action: $action_dispatch.into(), action: $action_dispatch.into(),
input: InputMapperMessage::KeyDown(Key::$refresh), input: InputMapperMessage::KeyDown(Key::$refresh),
modifiers: modifiers!(), modifiers: modifiers!(),
platform_layout: $layout,
}, },
MappingEntry { MappingEntry {
action: $action_dispatch.into(), action: $action_dispatch.into(),
input: InputMapperMessage::KeyUp(Key::$refresh), input: InputMapperMessage::KeyUp(Key::$refresh),
modifiers: modifiers!(), modifiers: modifiers!(),
platform_layout: $layout,
}, },
)* )*
)* )*
] ]]
};
}
/// Wraps [entry_for_layout]! and calls it with an agnostic (`None`) keyboard platform `layout` to avoid having to specify that argument.
///
/// Syntax:
/// ```rs
/// entry!(Key; modifiers?: Key[], refresh_keys?: Key[], action_dispatch: Message)
/// ```
macro_rules! entry {
($($arg:tt)*) => {
&[entry_for_layout!($($arg)*, layout=None)]
};
}
/// Wraps [entry_for_layout]! and calls it with a `Standard` keyboard platform `layout` to avoid having to specify that argument.
///
/// Syntax:
/// ```rs
/// standard!(Key; modifiers?: Key[], refresh_keys?: Key[], action_dispatch: Message)
/// ```
macro_rules! standard {
($($arg:tt)*) => {
entry_for_layout!($($arg)*, layout=Some(KeyboardPlatformLayout::Standard))
};
}
/// Wraps [entry_for_layout]! and calls it with a `Mac` keyboard platform `layout` to avoid having to specify that argument.
///
/// Syntax:
/// ```rs
/// mac_only!(Key; modifiers?: Key[], refresh_keys?: Key[], action_dispatch: Message)
/// ```
macro_rules! mac_only {
($($arg:tt)*) => {
entry_for_layout!($($arg)*, layout=Some(KeyboardPlatformLayout::Mac))
};
}
/// Groups multiple related entries for different platforms.
/// When a keyboard shortcut is not platform-agnostic, this should be used to contain a [mac]! and/or [standard]! entry.
///
/// Syntax:
///
/// ```rs
/// entry_multiplatform!(
/// standard!(Key; modifiers?: Key[], refresh_keys?: Key[], action_dispatch: Message),
/// mac_only!(Key; modifiers?: Key[], refresh_keys?: Key[], action_dispatch: Message),
/// )
/// ```
macro_rules! entry_multiplatform {
{$($arg:expr),*,} => {
&[$($arg ),*]
}; };
} }
@@ -159,9 +100,5 @@ macro_rules! action_keys {
pub(crate) use action_keys; pub(crate) use action_keys;
pub(crate) use entry; pub(crate) use entry;
pub(crate) use entry_for_layout;
pub(crate) use entry_multiplatform;
pub(crate) use mac_only;
pub(crate) use mapping; pub(crate) use mapping;
pub(crate) use modifiers; pub(crate) use modifiers;
pub(crate) use standard;
@@ -1,7 +1,6 @@
use super::input_keyboard::KeysGroup; use super::input_keyboard::{all_required_modifiers_pressed, KeysGroup};
use crate::messages::input_mapper::default_mapping::default_mapping; use crate::messages::input_mapper::default_mapping::default_mapping;
use crate::messages::input_mapper::utility_types::input_keyboard::{KeyStates, NUMBER_OF_KEYS}; use crate::messages::input_mapper::utility_types::input_keyboard::{KeyStates, NUMBER_OF_KEYS};
use crate::messages::portfolio::document::utility_types::misc::KeyboardPlatformLayout;
use crate::messages::prelude::*; use crate::messages::prelude::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@@ -16,7 +15,7 @@ pub struct Mapping {
} }
impl Mapping { impl Mapping {
pub fn match_input_message(&self, message: InputMapperMessage, keyboard_state: &KeyStates, actions: ActionList, keyboard_platform: KeyboardPlatformLayout) -> Option<Message> { pub fn match_input_message(&self, message: InputMapperMessage, keyboard_state: &KeyStates, actions: ActionList) -> Option<Message> {
let list = match message { let list = match message {
InputMapperMessage::KeyDown(key) => &self.key_down[key as usize], InputMapperMessage::KeyDown(key) => &self.key_down[key as usize],
InputMapperMessage::KeyUp(key) => &self.key_up[key as usize], InputMapperMessage::KeyUp(key) => &self.key_up[key as usize],
@@ -24,7 +23,7 @@ impl Mapping {
InputMapperMessage::WheelScroll => &self.wheel_scroll, InputMapperMessage::WheelScroll => &self.wheel_scroll,
InputMapperMessage::PointerMove => &self.pointer_move, InputMapperMessage::PointerMove => &self.pointer_move,
}; };
list.match_mapping(keyboard_state, actions, keyboard_platform) list.match_mapping(keyboard_state, actions)
} }
} }
@@ -38,26 +37,15 @@ impl Default for Mapping {
pub struct KeyMappingEntries(pub Vec<MappingEntry>); pub struct KeyMappingEntries(pub Vec<MappingEntry>);
impl KeyMappingEntries { impl KeyMappingEntries {
pub fn match_mapping(&self, keyboard_state: &KeyStates, actions: ActionList, keyboard_platform: KeyboardPlatformLayout) -> Option<Message> { pub fn match_mapping(&self, keyboard_state: &KeyStates, actions: ActionList) -> Option<Message> {
for entry in self.0.iter() { for mapping in self.0.iter() {
// Skip this entry if it is platform-specific, and for a layout that does not match the user's keyboard platform layout
if let Some(entry_platform_layout) = entry.platform_layout {
if entry_platform_layout != keyboard_platform {
continue;
}
}
// Find which currently pressed keys are also the modifiers in this hotkey entry, then compare those against the required modifiers to see if there are zero missing
let pressed_modifiers = *keyboard_state & entry.modifiers;
let all_modifiers_without_pressed_modifiers = entry.modifiers ^ pressed_modifiers;
let all_required_modifiers_pressed = all_modifiers_without_pressed_modifiers.is_empty();
// Skip this entry if any of the required modifiers are missing // Skip this entry if any of the required modifiers are missing
if !all_required_modifiers_pressed { if all_required_modifiers_pressed(keyboard_state, &mapping.modifiers) {
continue; // Search for the action in the list of available actions to see if it's currently available to activate
} let matching_action_found = actions.iter().flatten().any(|action| mapping.action.to_discriminant() == *action);
if matching_action_found {
if actions.iter().flatten().any(|action| entry.action.to_discriminant() == *action) { return Some(mapping.action.clone());
return Some(entry.action.clone()); }
} }
} }
None None
@@ -87,8 +75,6 @@ pub struct MappingEntry {
pub input: InputMapperMessage, pub input: InputMapperMessage,
/// Any additional keys that must be also pressed for this input mapping to match /// Any additional keys that must be also pressed for this input mapping to match
pub modifiers: KeyStates, pub modifiers: KeyStates,
/// The keyboard platform layout which this mapping is exclusive to, or `None` if it's platform-agnostic
pub platform_layout: Option<KeyboardPlatformLayout>,
} }
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
@@ -54,7 +54,7 @@ impl MessageHandler<InputPreprocessorMessage, KeyboardPlatformLayout> for InputP
} }
} }
InputPreprocessorMessage::DoubleClick { editor_mouse_state, modifier_keys } => { InputPreprocessorMessage::DoubleClick { editor_mouse_state, modifier_keys } => {
self.handle_modifier_keys(modifier_keys, keyboard_platform, responses); self.update_states_of_modifier_keys(modifier_keys, keyboard_platform, responses);
let mouse_state = editor_mouse_state.to_mouse_state(&self.viewport_bounds); let mouse_state = editor_mouse_state.to_mouse_state(&self.viewport_bounds);
self.mouse.position = mouse_state.position; self.mouse.position = mouse_state.position;
@@ -62,17 +62,17 @@ impl MessageHandler<InputPreprocessorMessage, KeyboardPlatformLayout> for InputP
responses.push_back(InputMapperMessage::DoubleClick.into()); responses.push_back(InputMapperMessage::DoubleClick.into());
} }
InputPreprocessorMessage::KeyDown { key, modifier_keys } => { InputPreprocessorMessage::KeyDown { key, modifier_keys } => {
self.handle_modifier_keys(modifier_keys, keyboard_platform, responses); self.update_states_of_modifier_keys(modifier_keys, keyboard_platform, responses);
self.keyboard.set(key as usize); self.keyboard.set(key as usize);
responses.push_back(InputMapperMessage::KeyDown(key).into()); responses.push_back(InputMapperMessage::KeyDown(key).into());
} }
InputPreprocessorMessage::KeyUp { key, modifier_keys } => { InputPreprocessorMessage::KeyUp { key, modifier_keys } => {
self.handle_modifier_keys(modifier_keys, keyboard_platform, responses); self.update_states_of_modifier_keys(modifier_keys, keyboard_platform, responses);
self.keyboard.unset(key as usize); self.keyboard.unset(key as usize);
responses.push_back(InputMapperMessage::KeyUp(key).into()); responses.push_back(InputMapperMessage::KeyUp(key).into());
} }
InputPreprocessorMessage::PointerDown { editor_mouse_state, modifier_keys } => { InputPreprocessorMessage::PointerDown { editor_mouse_state, modifier_keys } => {
self.handle_modifier_keys(modifier_keys, keyboard_platform, responses); self.update_states_of_modifier_keys(modifier_keys, keyboard_platform, responses);
let mouse_state = editor_mouse_state.to_mouse_state(&self.viewport_bounds); let mouse_state = editor_mouse_state.to_mouse_state(&self.viewport_bounds);
self.mouse.position = mouse_state.position; self.mouse.position = mouse_state.position;
@@ -80,7 +80,7 @@ impl MessageHandler<InputPreprocessorMessage, KeyboardPlatformLayout> for InputP
self.translate_mouse_event(mouse_state, true, responses); self.translate_mouse_event(mouse_state, true, responses);
} }
InputPreprocessorMessage::PointerMove { editor_mouse_state, modifier_keys } => { InputPreprocessorMessage::PointerMove { editor_mouse_state, modifier_keys } => {
self.handle_modifier_keys(modifier_keys, keyboard_platform, responses); self.update_states_of_modifier_keys(modifier_keys, keyboard_platform, responses);
let mouse_state = editor_mouse_state.to_mouse_state(&self.viewport_bounds); let mouse_state = editor_mouse_state.to_mouse_state(&self.viewport_bounds);
self.mouse.position = mouse_state.position; self.mouse.position = mouse_state.position;
@@ -91,7 +91,7 @@ impl MessageHandler<InputPreprocessorMessage, KeyboardPlatformLayout> for InputP
self.translate_mouse_event(mouse_state, false, responses); self.translate_mouse_event(mouse_state, false, responses);
} }
InputPreprocessorMessage::PointerUp { editor_mouse_state, modifier_keys } => { InputPreprocessorMessage::PointerUp { editor_mouse_state, modifier_keys } => {
self.handle_modifier_keys(modifier_keys, keyboard_platform, responses); self.update_states_of_modifier_keys(modifier_keys, keyboard_platform, responses);
let mouse_state = editor_mouse_state.to_mouse_state(&self.viewport_bounds); let mouse_state = editor_mouse_state.to_mouse_state(&self.viewport_bounds);
self.mouse.position = mouse_state.position; self.mouse.position = mouse_state.position;
@@ -99,7 +99,7 @@ impl MessageHandler<InputPreprocessorMessage, KeyboardPlatformLayout> for InputP
self.translate_mouse_event(mouse_state, false, responses); self.translate_mouse_event(mouse_state, false, responses);
} }
InputPreprocessorMessage::WheelScroll { editor_mouse_state, modifier_keys } => { InputPreprocessorMessage::WheelScroll { editor_mouse_state, modifier_keys } => {
self.handle_modifier_keys(modifier_keys, keyboard_platform, responses); self.update_states_of_modifier_keys(modifier_keys, keyboard_platform, responses);
let mouse_state = editor_mouse_state.to_mouse_state(&self.viewport_bounds); let mouse_state = editor_mouse_state.to_mouse_state(&self.viewport_bounds);
self.mouse.position = mouse_state.position; self.mouse.position = mouse_state.position;
@@ -138,18 +138,30 @@ impl InputPreprocessorMessageHandler {
self.mouse = new_state; self.mouse = new_state;
} }
fn handle_modifier_keys(&mut self, modifier_keys: ModifierKeys, keyboard_platform: KeyboardPlatformLayout, responses: &mut VecDeque<Message>) { fn update_states_of_modifier_keys(&mut self, pressed_modifier_keys: ModifierKeys, keyboard_platform: KeyboardPlatformLayout, responses: &mut VecDeque<Message>) {
self.handle_modifier_key(Key::Shift, modifier_keys.contains(ModifierKeys::SHIFT), responses); let is_key_pressed = |key_to_check: ModifierKeys| pressed_modifier_keys.contains(key_to_check);
self.handle_modifier_key(Key::Alt, modifier_keys.contains(ModifierKeys::ALT), responses);
self.handle_modifier_key(Key::Control, modifier_keys.contains(ModifierKeys::CONTROL), responses); // Update the state of the concrete modifier keys based on the source state
self.update_modifier_key(Key::Shift, is_key_pressed(ModifierKeys::SHIFT), responses);
self.update_modifier_key(Key::Alt, is_key_pressed(ModifierKeys::ALT), responses);
self.update_modifier_key(Key::Control, is_key_pressed(ModifierKeys::CONTROL), responses);
// Update the state of either the concrete Meta or the Command keys based on which one is applicable for this platform
let meta_or_command = match keyboard_platform { let meta_or_command = match keyboard_platform {
KeyboardPlatformLayout::Mac => Key::Command, KeyboardPlatformLayout::Mac => Key::Command,
KeyboardPlatformLayout::Standard => Key::Meta, KeyboardPlatformLayout::Standard => Key::Meta,
}; };
self.handle_modifier_key(meta_or_command, modifier_keys.contains(ModifierKeys::META_OR_COMMAND), responses); self.update_modifier_key(meta_or_command, is_key_pressed(ModifierKeys::META_OR_COMMAND), responses);
// Update the state of the virtual Accel key (the primary accelerator key) based on the source state of the Control or Command key, whichever is relevant on this platform
let accel_virtual_key_state = match keyboard_platform {
KeyboardPlatformLayout::Mac => is_key_pressed(ModifierKeys::META_OR_COMMAND),
KeyboardPlatformLayout::Standard => is_key_pressed(ModifierKeys::CONTROL),
};
self.update_modifier_key(Key::Accel, accel_virtual_key_state, responses);
} }
fn handle_modifier_key(&mut self, key: Key, key_is_down: bool, responses: &mut VecDeque<Message>) { fn update_modifier_key(&mut self, key: Key, key_is_down: bool, responses: &mut VecDeque<Message>) {
let key_was_down = self.keyboard.get(key as usize); let key_was_down = self.keyboard.get(key as usize);
if key_was_down && !key_is_down { if key_was_down && !key_is_down {
@@ -2,7 +2,6 @@ use super::utility_types::misc::LayoutTarget;
use crate::messages::input_mapper::utility_types::input_keyboard::KeysGroup; use crate::messages::input_mapper::utility_types::input_keyboard::KeysGroup;
use crate::messages::layout::utility_types::layout_widget::Layout; use crate::messages::layout::utility_types::layout_widget::Layout;
use crate::messages::layout::utility_types::layout_widget::Widget; use crate::messages::layout::utility_types::layout_widget::Widget;
use crate::messages::portfolio::document::utility_types::misc::KeyboardPlatformLayout;
use crate::messages::prelude::*; use crate::messages::prelude::*;
use graphene::layers::text_layer::Font; use graphene::layers::text_layer::Font;
@@ -14,21 +13,21 @@ pub struct LayoutMessageHandler {
layouts: [Layout; LayoutTarget::LayoutTargetLength as usize], layouts: [Layout; LayoutTarget::LayoutTargetLength as usize],
} }
impl<F: Fn(&MessageDiscriminant) -> Vec<KeysGroup>> MessageHandler<LayoutMessage, (F, KeyboardPlatformLayout)> for LayoutMessageHandler { impl<F: Fn(&MessageDiscriminant) -> Vec<KeysGroup>> MessageHandler<LayoutMessage, F> for LayoutMessageHandler {
#[remain::check] #[remain::check]
fn process_message(&mut self, message: LayoutMessage, data: (F, KeyboardPlatformLayout), responses: &mut std::collections::VecDeque<Message>) { fn process_message(&mut self, message: LayoutMessage, data: F, responses: &mut std::collections::VecDeque<Message>) {
let (action_input_mapping, keyboard_platform) = data; let action_input_mapping = data;
use LayoutMessage::*; use LayoutMessage::*;
#[remain::sorted] #[remain::sorted]
match message { match message {
RefreshLayout { layout_target } => { RefreshLayout { layout_target } => {
self.send_layout(layout_target, responses, &action_input_mapping, keyboard_platform); self.send_layout(layout_target, responses, &action_input_mapping);
} }
SendLayout { layout, layout_target } => { SendLayout { layout, layout_target } => {
self.layouts[layout_target as usize] = layout; self.layouts[layout_target as usize] = layout;
self.send_layout(layout_target, responses, &action_input_mapping, keyboard_platform); self.send_layout(layout_target, responses, &action_input_mapping);
} }
UpdateLayout { layout_target, widget_id, value } => { UpdateLayout { layout_target, widget_id, value } => {
let layout = &mut self.layouts[layout_target as usize]; let layout = &mut self.layouts[layout_target as usize];
@@ -152,55 +151,49 @@ impl<F: Fn(&MessageDiscriminant) -> Vec<KeysGroup>> MessageHandler<LayoutMessage
impl LayoutMessageHandler { impl LayoutMessageHandler {
#[remain::check] #[remain::check]
fn send_layout( fn send_layout(&self, layout_target: LayoutTarget, responses: &mut VecDeque<Message>, action_input_mapping: &impl Fn(&MessageDiscriminant) -> Vec<KeysGroup>) {
&self,
layout_target: LayoutTarget,
responses: &mut VecDeque<Message>,
action_input_mapping: &impl Fn(&MessageDiscriminant) -> Vec<KeysGroup>,
keyboard_platform: KeyboardPlatformLayout,
) {
let layout = &self.layouts[layout_target as usize]; let layout = &self.layouts[layout_target as usize];
#[remain::sorted] #[remain::sorted]
let message = match layout_target { let message = match layout_target {
LayoutTarget::DialogDetails => FrontendMessage::UpdateDialogDetails { LayoutTarget::DialogDetails => FrontendMessage::UpdateDialogDetails {
layout_target, layout_target,
layout: layout.clone().unwrap_widget_layout(action_input_mapping, keyboard_platform).layout, layout: layout.clone().unwrap_widget_layout(action_input_mapping).layout,
}, },
LayoutTarget::DocumentBar => FrontendMessage::UpdateDocumentBarLayout { LayoutTarget::DocumentBar => FrontendMessage::UpdateDocumentBarLayout {
layout_target, layout_target,
layout: layout.clone().unwrap_widget_layout(action_input_mapping, keyboard_platform).layout, layout: layout.clone().unwrap_widget_layout(action_input_mapping).layout,
}, },
LayoutTarget::DocumentMode => FrontendMessage::UpdateDocumentModeLayout { LayoutTarget::DocumentMode => FrontendMessage::UpdateDocumentModeLayout {
layout_target, layout_target,
layout: layout.clone().unwrap_widget_layout(action_input_mapping, keyboard_platform).layout, layout: layout.clone().unwrap_widget_layout(action_input_mapping).layout,
}, },
LayoutTarget::LayerTreeOptions => FrontendMessage::UpdateLayerTreeOptionsLayout { LayoutTarget::LayerTreeOptions => FrontendMessage::UpdateLayerTreeOptionsLayout {
layout_target, layout_target,
layout: layout.clone().unwrap_widget_layout(action_input_mapping, keyboard_platform).layout, layout: layout.clone().unwrap_widget_layout(action_input_mapping).layout,
}, },
LayoutTarget::MenuBar => FrontendMessage::UpdateMenuBarLayout { LayoutTarget::MenuBar => FrontendMessage::UpdateMenuBarLayout {
layout_target, layout_target,
layout: layout.clone().unwrap_menu_layout(action_input_mapping, keyboard_platform).layout, layout: layout.clone().unwrap_menu_layout(action_input_mapping).layout,
}, },
LayoutTarget::PropertiesOptions => FrontendMessage::UpdatePropertyPanelOptionsLayout { LayoutTarget::PropertiesOptions => FrontendMessage::UpdatePropertyPanelOptionsLayout {
layout_target, layout_target,
layout: layout.clone().unwrap_widget_layout(action_input_mapping, keyboard_platform).layout, layout: layout.clone().unwrap_widget_layout(action_input_mapping).layout,
}, },
LayoutTarget::PropertiesSections => FrontendMessage::UpdatePropertyPanelSectionsLayout { LayoutTarget::PropertiesSections => FrontendMessage::UpdatePropertyPanelSectionsLayout {
layout_target, layout_target,
layout: layout.clone().unwrap_widget_layout(action_input_mapping, keyboard_platform).layout, layout: layout.clone().unwrap_widget_layout(action_input_mapping).layout,
}, },
LayoutTarget::ToolOptions => FrontendMessage::UpdateToolOptionsLayout { LayoutTarget::ToolOptions => FrontendMessage::UpdateToolOptionsLayout {
layout_target, layout_target,
layout: layout.clone().unwrap_widget_layout(action_input_mapping, keyboard_platform).layout, layout: layout.clone().unwrap_widget_layout(action_input_mapping).layout,
}, },
LayoutTarget::ToolShelf => FrontendMessage::UpdateToolShelfLayout { LayoutTarget::ToolShelf => FrontendMessage::UpdateToolShelfLayout {
layout_target, layout_target,
layout: layout.clone().unwrap_widget_layout(action_input_mapping, keyboard_platform).layout, layout: layout.clone().unwrap_widget_layout(action_input_mapping).layout,
}, },
LayoutTarget::WorkingColors => FrontendMessage::UpdateWorkingColorsLayout { LayoutTarget::WorkingColors => FrontendMessage::UpdateWorkingColorsLayout {
layout_target, layout_target,
layout: layout.clone().unwrap_widget_layout(action_input_mapping, keyboard_platform).layout, layout: layout.clone().unwrap_widget_layout(action_input_mapping).layout,
}, },
#[remain::unsorted] #[remain::unsorted]
@@ -6,7 +6,6 @@ use crate::application::generate_uuid;
use crate::messages::input_mapper::utility_types::input_keyboard::KeysGroup; use crate::messages::input_mapper::utility_types::input_keyboard::KeysGroup;
use crate::messages::input_mapper::utility_types::misc::ActionKeys; use crate::messages::input_mapper::utility_types::misc::ActionKeys;
use crate::messages::layout::utility_types::misc::LayoutTarget; use crate::messages::layout::utility_types::misc::LayoutTarget;
use crate::messages::portfolio::document::utility_types::misc::KeyboardPlatformLayout;
use crate::messages::prelude::*; use crate::messages::prelude::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@@ -35,14 +34,14 @@ pub enum Layout {
} }
impl Layout { impl Layout {
pub fn unwrap_widget_layout(self, action_input_mapping: &impl Fn(&MessageDiscriminant) -> Vec<KeysGroup>, keyboard_platform: KeyboardPlatformLayout) -> WidgetLayout { pub fn unwrap_widget_layout(self, action_input_mapping: &impl Fn(&MessageDiscriminant) -> Vec<KeysGroup>) -> WidgetLayout {
if let Layout::WidgetLayout(mut widget_layout) = self { if let Layout::WidgetLayout(mut widget_layout) = self {
// Function used multiple times later in this code block to convert `ActionKeys::Action` to `ActionKeys::Keys` and append its shortcut to the tooltip // Function used multiple times later in this code block to convert `ActionKeys::Action` to `ActionKeys::Keys` and append its shortcut to the tooltip
let apply_shortcut_to_tooltip = |tooltip_shortcut: &mut ActionKeys, tooltip: &mut String| { let apply_shortcut_to_tooltip = |tooltip_shortcut: &mut ActionKeys, tooltip: &mut String| {
tooltip_shortcut.to_keys(action_input_mapping); tooltip_shortcut.to_keys(action_input_mapping);
if let ActionKeys::Keys(keys) = tooltip_shortcut { if let ActionKeys::Keys(keys) = tooltip_shortcut {
let shortcut_text = keys.keys_text_shortcut(keyboard_platform); let shortcut_text = keys.to_string();
if !shortcut_text.is_empty() { if !shortcut_text.is_empty() {
if !tooltip.is_empty() { if !tooltip.is_empty() {
@@ -90,7 +89,7 @@ impl Layout {
} }
} }
pub fn unwrap_menu_layout(self, action_input_mapping: &impl Fn(&MessageDiscriminant) -> Vec<KeysGroup>, _keyboard_platform: KeyboardPlatformLayout) -> MenuLayout { pub fn unwrap_menu_layout(self, action_input_mapping: &impl Fn(&MessageDiscriminant) -> Vec<KeysGroup>) -> MenuLayout {
if let Layout::MenuLayout(mut menu_layout) = self { if let Layout::MenuLayout(mut menu_layout) = self {
for menu_column in &mut menu_layout.layout { for menu_column in &mut menu_layout.layout {
menu_column.children.fill_in_shortcut_actions_with_keys(action_input_mapping); menu_column.children.fill_in_shortcut_actions_with_keys(action_input_mapping);
+2
View File
@@ -24,6 +24,8 @@ pub enum Message {
#[child] #[child]
Frontend(FrontendMessage), Frontend(FrontendMessage),
#[child] #[child]
Globals(GlobalsMessage),
#[child]
InputMapper(InputMapperMessage), InputMapper(InputMapperMessage),
#[child] #[child]
InputPreprocessor(InputPreprocessorMessage), InputPreprocessor(InputPreprocessorMessage),
+1
View File
@@ -4,6 +4,7 @@ pub mod broadcast;
pub mod debug; pub mod debug;
pub mod dialog; pub mod dialog;
pub mod frontend; pub mod frontend;
pub mod globals;
pub mod input_mapper; pub mod input_mapper;
pub mod input_preprocessor; pub mod input_preprocessor;
pub mod layout; pub mod layout;
@@ -77,7 +77,7 @@ impl Platform {
match self { match self {
Platform::Mac => KeyboardPlatformLayout::Mac, Platform::Mac => KeyboardPlatformLayout::Mac,
Platform::Unknown => { Platform::Unknown => {
log::warn!("The platform has not been set, remember to send `PortfolioMessage::SetPlatform` during editor initialization."); log::warn!("The platform has not been set, remember to send `GlobalsMessage::SetPlatform` during editor initialization.");
KeyboardPlatformLayout::Standard KeyboardPlatformLayout::Standard
} }
_ => KeyboardPlatformLayout::Standard, _ => KeyboardPlatformLayout::Standard,
@@ -1,5 +1,4 @@
use crate::messages::portfolio::document::utility_types::clipboards::Clipboard; use crate::messages::portfolio::document::utility_types::clipboards::Clipboard;
use crate::messages::portfolio::document::utility_types::misc::Platform;
use crate::messages::prelude::*; use crate::messages::prelude::*;
use graphene::layers::text_layer::Font; use graphene::layers::text_layer::Font;
@@ -84,9 +83,6 @@ pub enum PortfolioMessage {
SetActiveDocument { SetActiveDocument {
document_id: u64, document_id: u64,
}, },
SetPlatform {
platform: Platform,
},
UpdateDocumentWidgets, UpdateDocumentWidgets,
UpdateOpenDocumentsList, UpdateOpenDocumentsList,
} }
@@ -5,7 +5,6 @@ use crate::messages::frontend::utility_types::FrontendDocumentDetails;
use crate::messages::layout::utility_types::layout_widget::PropertyHolder; use crate::messages::layout::utility_types::layout_widget::PropertyHolder;
use crate::messages::layout::utility_types::misc::LayoutTarget; use crate::messages::layout::utility_types::misc::LayoutTarget;
use crate::messages::portfolio::document::utility_types::clipboards::{Clipboard, CopyBufferEntry, INTERNAL_CLIPBOARD_COUNT}; use crate::messages::portfolio::document::utility_types::clipboards::{Clipboard, CopyBufferEntry, INTERNAL_CLIPBOARD_COUNT};
use crate::messages::portfolio::document::utility_types::misc::Platform;
use crate::messages::prelude::*; use crate::messages::prelude::*;
use graphene::layers::layer_info::LayerDataTypeDiscriminant; use graphene::layers::layer_info::LayerDataTypeDiscriminant;
@@ -22,7 +21,6 @@ pub struct PortfolioMessageHandler {
active_document_id: Option<u64>, active_document_id: Option<u64>,
copy_buffer: [Vec<CopyBufferEntry>; INTERNAL_CLIPBOARD_COUNT as usize], copy_buffer: [Vec<CopyBufferEntry>; INTERNAL_CLIPBOARD_COUNT as usize],
font_cache: FontCache, font_cache: FontCache,
pub platform: Platform,
} }
impl MessageHandler<PortfolioMessage, &InputPreprocessorMessageHandler> for PortfolioMessageHandler { impl MessageHandler<PortfolioMessage, &InputPreprocessorMessageHandler> for PortfolioMessageHandler {
@@ -396,7 +394,6 @@ impl MessageHandler<PortfolioMessage, &InputPreprocessorMessageHandler> for Port
responses.push_back(NavigationMessage::TranslateCanvas { delta: (0., 0.).into() }.into()); responses.push_back(NavigationMessage::TranslateCanvas { delta: (0., 0.).into() }.into());
} }
SetActiveDocument { document_id } => self.active_document_id = Some(document_id), SetActiveDocument { document_id } => self.active_document_id = Some(document_id),
SetPlatform { platform } => self.platform = platform,
UpdateDocumentWidgets => { UpdateDocumentWidgets => {
if let Some(document) = self.active_document() { if let Some(document) = self.active_document() {
document.update_document_widgets(responses); document.update_document_widgets(responses);
+3
View File
@@ -8,6 +8,7 @@ pub use crate::messages::dialog::export_dialog::{ExportDialogMessage, ExportDial
pub use crate::messages::dialog::new_document_dialog::{NewDocumentDialogMessage, NewDocumentDialogMessageDiscriminant, NewDocumentDialogMessageHandler}; pub use crate::messages::dialog::new_document_dialog::{NewDocumentDialogMessage, NewDocumentDialogMessageDiscriminant, NewDocumentDialogMessageHandler};
pub use crate::messages::dialog::{DialogMessage, DialogMessageDiscriminant, DialogMessageHandler}; pub use crate::messages::dialog::{DialogMessage, DialogMessageDiscriminant, DialogMessageHandler};
pub use crate::messages::frontend::{FrontendMessage, FrontendMessageDiscriminant}; pub use crate::messages::frontend::{FrontendMessage, FrontendMessageDiscriminant};
pub use crate::messages::globals::{GlobalsMessage, GlobalsMessageDiscriminant, GlobalsMessageHandler};
pub use crate::messages::input_mapper::{InputMapperMessage, InputMapperMessageDiscriminant, InputMapperMessageHandler}; pub use crate::messages::input_mapper::{InputMapperMessage, InputMapperMessageDiscriminant, InputMapperMessageHandler};
pub use crate::messages::input_preprocessor::{InputPreprocessorMessage, InputPreprocessorMessageDiscriminant, InputPreprocessorMessageHandler}; pub use crate::messages::input_preprocessor::{InputPreprocessorMessage, InputPreprocessorMessageDiscriminant, InputPreprocessorMessageHandler};
pub use crate::messages::layout::{LayoutMessage, LayoutMessageDiscriminant, LayoutMessageHandler}; pub use crate::messages::layout::{LayoutMessage, LayoutMessageDiscriminant, LayoutMessageHandler};
@@ -42,6 +43,8 @@ pub use crate::messages::tool::tool_messages::spline_tool::{SplineToolMessage, S
pub use crate::messages::tool::tool_messages::text_tool::{TextToolMessage, TextToolMessageDiscriminant}; pub use crate::messages::tool::tool_messages::text_tool::{TextToolMessage, TextToolMessageDiscriminant};
// Helper // Helper
pub use crate::messages::globals::global_variables::*;
pub use graphite_proc_macros::*; pub use graphite_proc_macros::*;
pub use std::collections::{HashMap, HashSet, VecDeque}; pub use std::collections::{HashMap, HashSet, VecDeque};
+18
View File
@@ -1,6 +1,8 @@
use crate::application::set_uuid_seed;
use crate::application::Editor; use crate::application::Editor;
use crate::messages::input_mapper::utility_types::input_keyboard::ModifierKeys; use crate::messages::input_mapper::utility_types::input_keyboard::ModifierKeys;
use crate::messages::input_mapper::utility_types::input_mouse::{EditorMouseState, MouseKeys, ScrollDelta, ViewportPosition}; use crate::messages::input_mapper::utility_types::input_mouse::{EditorMouseState, MouseKeys, ScrollDelta, ViewportPosition};
use crate::messages::portfolio::document::utility_types::misc::Platform;
use crate::messages::prelude::*; use crate::messages::prelude::*;
use crate::messages::tool::utility_types::ToolType; use crate::messages::tool::utility_types::ToolType;
@@ -8,6 +10,8 @@ use graphene::color::Color;
/// A set of utility functions to make the writing of editor test more declarative /// A set of utility functions to make the writing of editor test more declarative
pub trait EditorTestUtils { pub trait EditorTestUtils {
fn create() -> Editor;
fn new_document(&mut self); fn new_document(&mut self);
fn draw_rect(&mut self, x1: f64, y1: f64, x2: f64, y2: f64); fn draw_rect(&mut self, x1: f64, y1: f64, x2: f64, y2: f64);
@@ -26,6 +30,20 @@ pub trait EditorTestUtils {
} }
impl EditorTestUtils for Editor { impl EditorTestUtils for Editor {
fn create() -> Editor {
set_uuid_seed(0);
let mut editor = Editor::new();
// We have to set this directly instead of using `GlobalsMessage::SetPlatform` because race conditions with multiple tests can cause that message handler to set it more than once, which is a failure.
// It isn't sufficient to guard the message dispatch here with a check if the once_cell is empty, because that isn't atomic and the time between checking and handling the dispatch can let multiple through.
let _ = GLOBAL_PLATFORM.set(Platform::Windows).is_ok();
editor.handle_message(Message::Init);
editor
}
fn new_document(&mut self) { fn new_document(&mut self) {
self.handle_message(Message::Portfolio(PortfolioMessage::NewDocumentWithName { name: String::from("Test document") })); self.handle_message(Message::Portfolio(PortfolioMessage::NewDocumentWithName { name: String::from("Test document") }));
} }
@@ -195,8 +195,11 @@ export default defineComponent({
let key = keyWithLabel.key; let key = keyWithLabel.key;
const label = keyWithLabel.label; const label = keyWithLabel.label;
// Replace Alt with Option on Mac // Replace Alt and Accel keys with their Mac-specific equivalents
if (key === "Alt" && platformIsMac()) key = "Option"; if (platformIsMac()) {
if (key === "Alt") key = "Option";
if (key === "Accel") key = "Command";
}
// Either display an icon... // Either display an icon...
// @ts-expect-error We want undefined if it isn't in the object // @ts-expect-error We want undefined if it isn't in the object
+6 -2
View File
@@ -66,8 +66,11 @@ impl JsEditorHandle {
return; return;
} }
// Get the editor instances, dispatch the message, and store the `FrontendMessage` queue response
let frontend_messages = EDITOR_INSTANCES.with(|instances| { let frontend_messages = EDITOR_INSTANCES.with(|instances| {
// Mutably borrow the editors, and if successful, we can access them in the closure
instances.try_borrow_mut().map(|mut editors| { instances.try_borrow_mut().map(|mut editors| {
// Get the editor instance for this editor ID, then dispatch the message to the backend, and return its response `FrontendMessage` queue
editors editors
.get_mut(&self.editor_id) .get_mut(&self.editor_id)
.expect("EDITOR_INSTANCES does not contain the current editor_id") .expect("EDITOR_INSTANCES does not contain the current editor_id")
@@ -75,9 +78,10 @@ impl JsEditorHandle {
}) })
}); });
// Process any `FrontendMessage` responses resulting from the backend processing the dispatched message
if let Ok(frontend_messages) = frontend_messages { if let Ok(frontend_messages) = frontend_messages {
// Send each `FrontendMessage` to the JavaScript frontend
for message in frontend_messages.into_iter() { for message in frontend_messages.into_iter() {
// Send each FrontendMessage to the JavaScript frontend
self.send_frontend_message_to_js(message); self.send_frontend_message_to_js(message);
} }
} }
@@ -115,7 +119,7 @@ impl JsEditorHandle {
_ => Platform::Unknown, _ => Platform::Unknown,
}; };
self.dispatch(PortfolioMessage::SetPlatform { platform }); self.dispatch(GlobalsMessage::SetPlatform { platform });
self.dispatch(Message::Init); self.dispatch(Message::Init);
} }