mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-08-04 13:30:48 +00:00
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:
parent
41bead9028
commit
863c17b86f
24 changed files with 240 additions and 315 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -215,6 +215,7 @@ dependencies = [
|
|||
"graphite-proc-macros",
|
||||
"kurbo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"rand_chacha",
|
||||
"remain",
|
||||
"serde",
|
||||
|
|
|
@ -25,6 +25,7 @@ kurbo = { git = "https://github.com/linebender/kurbo.git", features = [
|
|||
] }
|
||||
remain = "0.2.2"
|
||||
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]
|
||||
path = "../graphene"
|
||||
|
|
|
@ -17,6 +17,7 @@ struct DispatcherMessageHandlers {
|
|||
broadcast_message_handler: BroadcastMessageHandler,
|
||||
debug_message_handler: DebugMessageHandler,
|
||||
dialog_message_handler: DialogMessageHandler,
|
||||
globals_message_handler: GlobalsMessageHandler,
|
||||
input_mapper_message_handler: InputMapperMessageHandler,
|
||||
input_preprocessor_message_handler: InputPreprocessorMessageHandler,
|
||||
layout_message_handler: LayoutMessageHandler,
|
||||
|
@ -127,26 +128,25 @@ impl Dispatcher {
|
|||
self.responses.push(message);
|
||||
}
|
||||
}
|
||||
Globals(message) => {
|
||||
self.message_handlers.globals_message_handler.process_message(message, (), &mut queue);
|
||||
}
|
||||
InputMapper(message) => {
|
||||
let actions = self.collect_actions();
|
||||
let keyboard_platform = self.message_handlers.portfolio_message_handler.platform.as_keyboard_platform_layout();
|
||||
|
||||
self.message_handlers
|
||||
.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) => {
|
||||
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);
|
||||
}
|
||||
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, keyboard_platform);
|
||||
let action_input_mapping = &|action_to_find: &MessageDiscriminant| self.message_handlers.input_mapper_message_handler.action_input_mapping(action_to_find);
|
||||
|
||||
self.message_handlers
|
||||
.layout_message_handler
|
||||
.process_message(message, (action_input_mapping, keyboard_platform), &mut queue);
|
||||
self.message_handlers.layout_message_handler.process_message(message, action_input_mapping, &mut queue);
|
||||
}
|
||||
Portfolio(message) => {
|
||||
self.message_handlers
|
||||
|
@ -243,7 +243,6 @@ impl Dispatcher {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::application::set_uuid_seed;
|
||||
use crate::application::Editor;
|
||||
use crate::messages::portfolio::document::utility_types::clipboards::Clipboard;
|
||||
use crate::messages::prelude::*;
|
||||
|
@ -262,14 +261,17 @@ mod test {
|
|||
/// 2. A blue shape
|
||||
/// 3. A green ellipse
|
||||
fn create_editor_with_three_layers() -> Editor {
|
||||
set_uuid_seed(0);
|
||||
let mut editor = Editor::new();
|
||||
init_logger();
|
||||
let mut editor = Editor::create();
|
||||
|
||||
editor.new_document();
|
||||
|
||||
editor.select_primary_color(Color::RED);
|
||||
editor.draw_rect(100., 200., 300., 400.);
|
||||
|
||||
editor.select_primary_color(Color::BLUE);
|
||||
editor.draw_shape(10., 1200., 1300., 400.);
|
||||
|
||||
editor.select_primary_color(Color::GREEN);
|
||||
editor.draw_ellipse(104., 1200., 1300., 400.);
|
||||
|
||||
|
@ -282,7 +284,6 @@ mod test {
|
|||
/// - paste
|
||||
/// - assert that ellipse was copied
|
||||
fn copy_paste_single_layer() {
|
||||
init_logger();
|
||||
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();
|
||||
|
@ -316,7 +317,6 @@ mod test {
|
|||
/// - paste
|
||||
/// - assert that shape was copied
|
||||
fn copy_paste_single_layer_from_middle() {
|
||||
init_logger();
|
||||
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();
|
||||
|
@ -351,7 +351,6 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn copy_paste_folder() {
|
||||
init_logger();
|
||||
let mut editor = create_editor_with_three_layers();
|
||||
|
||||
const FOLDER_INDEX: usize = 3;
|
||||
|
@ -447,7 +446,6 @@ mod test {
|
|||
/// - paste
|
||||
/// - paste
|
||||
fn copy_paste_deleted_layers() {
|
||||
init_logger();
|
||||
let mut editor = create_editor_with_three_layers();
|
||||
|
||||
const ELLIPSE_INDEX: usize = 2;
|
||||
|
@ -499,7 +497,6 @@ mod test {
|
|||
/// - select ellipse and rect
|
||||
/// - move them down and back up again
|
||||
fn move_selection() {
|
||||
init_logger();
|
||||
let mut editor = create_editor_with_three_layers();
|
||||
|
||||
fn map_to_vec(paths: Vec<&[LayerId]>) -> Vec<Vec<LayerId>> {
|
||||
|
@ -555,8 +552,8 @@ mod test {
|
|||
};
|
||||
|
||||
init_logger();
|
||||
set_uuid_seed(0);
|
||||
let mut editor = Editor::new();
|
||||
let mut editor = Editor::create();
|
||||
|
||||
let test_file = include_str!("../graphite-test-document.graphite");
|
||||
let responses = editor.handle_message(PortfolioMessage::OpenDocumentFile {
|
||||
document_name: "Graphite Version Test".into(),
|
||||
|
|
5
editor/src/messages/globals/global_variables.rs
Normal file
5
editor/src/messages/globals/global_variables.rs
Normal file
|
@ -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();
|
10
editor/src/messages/globals/globals_message.rs
Normal file
10
editor/src/messages/globals/globals_message.rs
Normal file
|
@ -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 },
|
||||
}
|
18
editor/src/messages/globals/globals_message_handler.rs
Normal file
18
editor/src/messages/globals/globals_message_handler.rs
Normal file
|
@ -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
editor/src/messages/globals/mod.rs
Normal file
9
editor/src/messages/globals/mod.rs
Normal 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::{KeyMappingEntries, Mapping};
|
||||
use crate::messages::portfolio::document::utility_types::clipboards::Clipboard;
|
||||
use crate::messages::portfolio::document::utility_types::misc::KeyboardPlatformLayout;
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
use glam::DVec2;
|
||||
|
@ -75,10 +74,7 @@ pub fn default_mapping() -> Mapping {
|
|||
// TextToolMessage
|
||||
entry!(KeyUp(Lmb); action_dispatch=TextToolMessage::Interact),
|
||||
entry!(KeyDown(Escape); action_dispatch=TextToolMessage::Abort),
|
||||
entry_multiplatform!(
|
||||
standard!(KeyDown(Enter); modifiers=[Control], action_dispatch=TextToolMessage::CommitText),
|
||||
mac_only!(KeyDown(Enter); modifiers=[Command], action_dispatch=TextToolMessage::CommitText),
|
||||
),
|
||||
entry!(KeyDown(Enter); modifiers=[Accel], action_dispatch=TextToolMessage::CommitText),
|
||||
//
|
||||
// GradientToolMessage
|
||||
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(KeyE); action_dispatch=ToolMessage::ActivateToolEllipse),
|
||||
entry!(KeyDown(KeyY); action_dispatch=ToolMessage::ActivateToolShape),
|
||||
entry_multiplatform!(
|
||||
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, Accel], action_dispatch=ToolMessage::ResetColors),
|
||||
entry!(KeyDown(KeyX); modifiers=[Shift], action_dispatch=ToolMessage::SwapColors),
|
||||
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(Backspace); action_dispatch=DocumentMessage::DeleteSelectedLayers),
|
||||
entry!(KeyDown(KeyP); modifiers=[Alt], action_dispatch=DocumentMessage::DebugPrintDocument),
|
||||
entry_multiplatform!(
|
||||
standard!(KeyDown(KeyZ); modifiers=[Control, Shift], action_dispatch=DocumentMessage::Redo),
|
||||
mac_only!(KeyDown(KeyZ); modifiers=[Command, Shift], action_dispatch=DocumentMessage::Redo),
|
||||
),
|
||||
entry_multiplatform!(
|
||||
standard!(KeyDown(KeyZ); modifiers=[Control], action_dispatch=DocumentMessage::Undo),
|
||||
mac_only!(KeyDown(KeyZ); modifiers=[Command], action_dispatch=DocumentMessage::Undo),
|
||||
),
|
||||
entry_multiplatform!(
|
||||
standard!(KeyDown(KeyA); modifiers=[Control, Alt], action_dispatch=DocumentMessage::DeselectAllLayers),
|
||||
mac_only!(KeyDown(KeyA); modifiers=[Command, Alt], action_dispatch=DocumentMessage::DeselectAllLayers),
|
||||
),
|
||||
entry_multiplatform!(
|
||||
standard!(KeyDown(KeyA); modifiers=[Control], action_dispatch=DocumentMessage::SelectAllLayers),
|
||||
mac_only!(KeyDown(KeyA); modifiers=[Command], action_dispatch=DocumentMessage::SelectAllLayers),
|
||||
),
|
||||
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(KeyZ); modifiers=[Accel, Shift], action_dispatch=DocumentMessage::Redo),
|
||||
entry!(KeyDown(KeyZ); modifiers=[Accel], action_dispatch=DocumentMessage::Undo),
|
||||
entry!(KeyDown(KeyA); modifiers=[Accel, Alt], action_dispatch=DocumentMessage::DeselectAllLayers),
|
||||
entry!(KeyDown(KeyA); modifiers=[Accel], action_dispatch=DocumentMessage::SelectAllLayers),
|
||||
entry!(KeyDown(KeyS); modifiers=[Accel], action_dispatch=DocumentMessage::SaveDocument),
|
||||
entry!(KeyDown(KeyD); modifiers=[Accel], action_dispatch=DocumentMessage::DuplicateSelectedLayers),
|
||||
entry!(KeyDown(KeyG); modifiers=[Accel], action_dispatch=DocumentMessage::GroupSelectedLayers),
|
||||
entry!(KeyDown(KeyG); modifiers=[Accel, Shift], action_dispatch=DocumentMessage::UngroupSelectedLayers),
|
||||
entry!(KeyDown(KeyN); modifiers=[Accel, Shift], action_dispatch=DocumentMessage::CreateEmptyFolder { container_path: vec![] }),
|
||||
entry!(KeyDown(Digit0); modifiers=[Accel], action_dispatch=DocumentMessage::ZoomCanvasToFitAll),
|
||||
entry!(KeyDown(Digit1); modifiers=[Accel], action_dispatch=DocumentMessage::ZoomCanvasTo100Percent),
|
||||
entry!(KeyDown(Digit2); modifiers=[Accel], action_dispatch=DocumentMessage::ZoomCanvasTo200Percent),
|
||||
entry!(KeyDown(BracketLeft); modifiers=[Accel, Shift], action_dispatch=DocumentMessage::SelectedLayersLowerToBack),
|
||||
entry!(KeyDown(BracketRight); modifiers=[Accel, Shift], action_dispatch=DocumentMessage::SelectedLayersRaiseToFront),
|
||||
entry!(KeyDown(BracketLeft); modifiers=[Accel], action_dispatch=DocumentMessage::SelectedLayersLower),
|
||||
entry!(KeyDown(BracketRight); modifiers=[Accel], 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, 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 }),
|
||||
|
@ -271,18 +216,9 @@ pub fn default_mapping() -> Mapping {
|
|||
entry!(KeyUp(Mmb); action_dispatch=NavigationMessage::TransformCanvasEnd),
|
||||
entry!(KeyDown(Lmb); modifiers=[Space], action_dispatch=NavigationMessage::TranslateCanvasBegin),
|
||||
entry!(KeyUp(Lmb); modifiers=[Space], action_dispatch=NavigationMessage::TransformCanvasEnd),
|
||||
entry_multiplatform!(
|
||||
standard!(KeyDown(NumpadAdd); modifiers=[Control], action_dispatch=NavigationMessage::IncreaseCanvasZoom { center_on_mouse: false }),
|
||||
mac_only!(KeyDown(NumpadAdd); modifiers=[Command], action_dispatch=NavigationMessage::IncreaseCanvasZoom { 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!(KeyDown(NumpadAdd); modifiers=[Accel], action_dispatch=NavigationMessage::IncreaseCanvasZoom { center_on_mouse: false }),
|
||||
entry!(KeyDown(Equal); modifiers=[Accel], action_dispatch=NavigationMessage::IncreaseCanvasZoom { center_on_mouse: false }),
|
||||
entry!(KeyDown(Minus); modifiers=[Accel], action_dispatch=NavigationMessage::DecreaseCanvasZoom { center_on_mouse: false }),
|
||||
entry!(WheelScroll; modifiers=[Control], action_dispatch=NavigationMessage::WheelCanvasZoom),
|
||||
entry!(WheelScroll; modifiers=[Shift], action_dispatch=NavigationMessage::WheelCanvasTranslate { use_y_as_x: true }),
|
||||
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.) }),
|
||||
//
|
||||
// PortfolioMessage
|
||||
entry_multiplatform!(
|
||||
standard!(KeyDown(KeyO); modifiers=[Control], action_dispatch=PortfolioMessage::OpenDocument),
|
||||
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(KeyO); modifiers=[Accel], action_dispatch=PortfolioMessage::OpenDocument),
|
||||
entry!(KeyDown(KeyI); modifiers=[Accel], action_dispatch=PortfolioMessage::Import),
|
||||
entry!(KeyDown(Tab); modifiers=[Control], action_dispatch=PortfolioMessage::NextDocument),
|
||||
entry!(KeyDown(Tab); modifiers=[Control, Shift], action_dispatch=PortfolioMessage::PrevDocument),
|
||||
entry_multiplatform!(
|
||||
standard!(KeyDown(KeyW); modifiers=[Control], action_dispatch=PortfolioMessage::CloseActiveDocumentWithConfirmation),
|
||||
mac_only!(KeyDown(KeyW); modifiers=[Command], action_dispatch=PortfolioMessage::CloseActiveDocumentWithConfirmation),
|
||||
),
|
||||
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),
|
||||
),
|
||||
entry!(KeyDown(KeyW); modifiers=[Accel], action_dispatch=PortfolioMessage::CloseActiveDocumentWithConfirmation),
|
||||
entry!(KeyDown(KeyX); modifiers=[Accel], action_dispatch=PortfolioMessage::Cut { clipboard: Clipboard::Device }),
|
||||
entry!(KeyDown(KeyC); modifiers=[Accel], action_dispatch=PortfolioMessage::Copy { clipboard: Clipboard::Device }),
|
||||
entry!(KeyDown(KeyV); modifiers=[Accel], action_dispatch=FrontendMessage::TriggerPaste),
|
||||
//
|
||||
// DialogMessage
|
||||
entry_multiplatform!(
|
||||
standard!(KeyDown(KeyN); modifiers=[Control], action_dispatch=DialogMessage::RequestNewDocumentDialog),
|
||||
mac_only!(KeyDown(KeyN); modifiers=[Command], action_dispatch=DialogMessage::RequestNewDocumentDialog),
|
||||
),
|
||||
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),
|
||||
),
|
||||
entry!(KeyDown(KeyN); modifiers=[Accel], action_dispatch=DialogMessage::RequestNewDocumentDialog),
|
||||
entry!(KeyDown(KeyW); modifiers=[Accel, Alt], action_dispatch=DialogMessage::CloseAllDocumentsWithConfirmation),
|
||||
entry!(KeyDown(KeyE); modifiers=[Accel], action_dispatch=DialogMessage::RequestExportDialog),
|
||||
//
|
||||
// DebugMessage
|
||||
entry!(KeyDown(KeyT); modifiers=[Alt], action_dispatch=DebugMessage::ToggleTraceLogs),
|
||||
|
@ -350,7 +258,6 @@ pub fn default_mapping() -> Mapping {
|
|||
MappingEntry {
|
||||
action: TransformLayerMessage::TypeDigit { digit: i as u8 }.into(),
|
||||
input: InputMapperMessage::KeyDown(*key),
|
||||
platform_layout: None,
|
||||
modifiers: modifiers!(),
|
||||
},
|
||||
);
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use super::utility_types::input_keyboard::KeysGroup;
|
||||
use super::utility_types::misc::Mapping;
|
||||
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 std::fmt::Write;
|
||||
|
@ -11,11 +10,11 @@ pub struct InputMapperMessageHandler {
|
|||
mapping: Mapping,
|
||||
}
|
||||
|
||||
impl MessageHandler<InputMapperMessage, (&InputPreprocessorMessageHandler, KeyboardPlatformLayout, ActionList)> for InputMapperMessageHandler {
|
||||
fn process_message(&mut self, message: InputMapperMessage, data: (&InputPreprocessorMessageHandler, KeyboardPlatformLayout, ActionList), responses: &mut VecDeque<Message>) {
|
||||
let (input, keyboard_platform, actions) = data;
|
||||
impl MessageHandler<InputMapperMessage, (&InputPreprocessorMessageHandler, ActionList)> for InputMapperMessageHandler {
|
||||
fn process_message(&mut self, message: InputMapperMessage, data: (&InputPreprocessorMessageHandler, ActionList), responses: &mut VecDeque<Message>) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +43,7 @@ impl InputMapperMessageHandler {
|
|||
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_down = self.mapping.key_down.iter();
|
||||
let double_click = std::iter::once(&self.mapping.double_click);
|
||||
|
@ -56,8 +55,6 @@ impl InputMapperMessageHandler {
|
|||
|
||||
// Filter for the desired message
|
||||
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
|
||||
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 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 {
|
||||
Pressed,
|
||||
Released,
|
||||
|
@ -29,10 +37,10 @@ bitflags! {
|
|||
#[derive(Default, Serialize, Deserialize)]
|
||||
#[repr(transparent)]
|
||||
pub struct ModifierKeys: u8 {
|
||||
const SHIFT = 0b0000_0001;
|
||||
const ALT = 0b0000_0010;
|
||||
const CONTROL = 0b0000_0100;
|
||||
const META_OR_COMMAND = 0b0000_1000;
|
||||
const SHIFT = 0b_0000_0001;
|
||||
const ALT = 0b_0000_0010;
|
||||
const CONTROL = 0b_0000_0100;
|
||||
const META_OR_COMMAND = 0b_0000_1000;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -193,6 +201,7 @@ pub enum Key {
|
|||
|
||||
// Other keys that aren't part of the W3C spec
|
||||
Command,
|
||||
Accel,
|
||||
Lmb,
|
||||
Rmb,
|
||||
Mmb,
|
||||
|
@ -228,6 +237,8 @@ impl fmt::Display for Key {
|
|||
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 {
|
||||
// Writing system keys
|
||||
Self::Backquote => "`",
|
||||
|
@ -243,7 +254,23 @@ impl fmt::Display for Key {
|
|||
Self::Slash => "/",
|
||||
|
||||
// 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
|
||||
Self::Delete => "Del",
|
||||
|
@ -267,6 +294,13 @@ impl fmt::Display for Key {
|
|||
Self::Escape => "Esc",
|
||||
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(),
|
||||
};
|
||||
|
||||
|
@ -280,36 +314,31 @@ pub const NUMBER_OF_KEYS: usize = Key::NumKeys as usize;
|
|||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct KeysGroup(pub Vec<Key>);
|
||||
|
||||
impl KeysGroup {
|
||||
pub fn keys_text_shortcut(&self, keyboard_platform: KeyboardPlatformLayout) -> String {
|
||||
const JOINER_MARK: &str = "+";
|
||||
impl fmt::Display for KeysGroup {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
const JOINER_MARK: &str = " ";
|
||||
|
||||
let mut joined = self
|
||||
.0
|
||||
.iter()
|
||||
.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 {
|
||||
match key_string.as_str() {
|
||||
"Command" => "⌘".to_string(),
|
||||
"Control" => "⌃".to_string(),
|
||||
"Alt" => "⌥".to_string(),
|
||||
"Shift" => "⇧".to_string(),
|
||||
_ => key_string + JOINER_MARK,
|
||||
}
|
||||
if keyboard_layout == KeyboardPlatformLayout::Mac && key_is_modifier {
|
||||
key.to_string()
|
||||
} else {
|
||||
key_string + JOINER_MARK
|
||||
key.to_string() + JOINER_MARK
|
||||
}
|
||||
})
|
||||
.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) {
|
||||
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:
|
||||
/// - ...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
|
||||
/// - ...while any further conditions are met, like the optional `modifiers` being pressed or `layout` matching the OS.
|
||||
/// - ...while the optional `modifiers` being pressed.
|
||||
///
|
||||
/// Syntax:
|
||||
/// ```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.
|
||||
/// 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.
|
||||
macro_rules! entry_for_layout {
|
||||
($input:expr; $(modifiers=[$($modifier:ident),*],)? $(refresh_keys=[$($refresh:ident),* $(,)?],)? action_dispatch=$action_dispatch:expr,$(,)? layout=$layout:expr) => {
|
||||
&[
|
||||
macro_rules! entry {
|
||||
($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.
|
||||
MappingEntry {
|
||||
action: $action_dispatch.into(),
|
||||
input: $input,
|
||||
modifiers: modifiers!($($($modifier),*)?),
|
||||
platform_layout: $layout,
|
||||
},
|
||||
|
||||
// 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(),
|
||||
input: InputMapperMessage::KeyDown(Key::$refresh),
|
||||
modifiers: modifiers!(),
|
||||
platform_layout: $layout,
|
||||
},
|
||||
MappingEntry {
|
||||
action: $action_dispatch.into(),
|
||||
input: InputMapperMessage::KeyUp(Key::$refresh),
|
||||
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 entry;
|
||||
pub(crate) use entry_for_layout;
|
||||
pub(crate) use entry_multiplatform;
|
||||
pub(crate) use mac_only;
|
||||
pub(crate) use mapping;
|
||||
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::utility_types::input_keyboard::{KeyStates, NUMBER_OF_KEYS};
|
||||
use crate::messages::portfolio::document::utility_types::misc::KeyboardPlatformLayout;
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -16,7 +15,7 @@ pub struct 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 {
|
||||
InputMapperMessage::KeyDown(key) => &self.key_down[key as usize],
|
||||
InputMapperMessage::KeyUp(key) => &self.key_up[key as usize],
|
||||
|
@ -24,7 +23,7 @@ impl Mapping {
|
|||
InputMapperMessage::WheelScroll => &self.wheel_scroll,
|
||||
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>);
|
||||
|
||||
impl KeyMappingEntries {
|
||||
pub fn match_mapping(&self, keyboard_state: &KeyStates, actions: ActionList, keyboard_platform: KeyboardPlatformLayout) -> Option<Message> {
|
||||
for entry 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();
|
||||
pub fn match_mapping(&self, keyboard_state: &KeyStates, actions: ActionList) -> Option<Message> {
|
||||
for mapping in self.0.iter() {
|
||||
// Skip this entry if any of the required modifiers are missing
|
||||
if !all_required_modifiers_pressed {
|
||||
continue;
|
||||
}
|
||||
|
||||
if actions.iter().flatten().any(|action| entry.action.to_discriminant() == *action) {
|
||||
return Some(entry.action.clone());
|
||||
if all_required_modifiers_pressed(keyboard_state, &mapping.modifiers) {
|
||||
// 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 {
|
||||
return Some(mapping.action.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
|
@ -87,8 +75,6 @@ pub struct MappingEntry {
|
|||
pub input: InputMapperMessage,
|
||||
/// Any additional keys that must be also pressed for this input mapping to match
|
||||
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)]
|
||||
|
|
|
@ -54,7 +54,7 @@ impl MessageHandler<InputPreprocessorMessage, KeyboardPlatformLayout> for InputP
|
|||
}
|
||||
}
|
||||
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);
|
||||
self.mouse.position = mouse_state.position;
|
||||
|
@ -62,17 +62,17 @@ impl MessageHandler<InputPreprocessorMessage, KeyboardPlatformLayout> for InputP
|
|||
responses.push_back(InputMapperMessage::DoubleClick.into());
|
||||
}
|
||||
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);
|
||||
responses.push_back(InputMapperMessage::KeyDown(key).into());
|
||||
}
|
||||
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);
|
||||
responses.push_back(InputMapperMessage::KeyUp(key).into());
|
||||
}
|
||||
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);
|
||||
self.mouse.position = mouse_state.position;
|
||||
|
@ -80,7 +80,7 @@ impl MessageHandler<InputPreprocessorMessage, KeyboardPlatformLayout> for InputP
|
|||
self.translate_mouse_event(mouse_state, true, responses);
|
||||
}
|
||||
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);
|
||||
self.mouse.position = mouse_state.position;
|
||||
|
@ -91,7 +91,7 @@ impl MessageHandler<InputPreprocessorMessage, KeyboardPlatformLayout> for InputP
|
|||
self.translate_mouse_event(mouse_state, false, responses);
|
||||
}
|
||||
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);
|
||||
self.mouse.position = mouse_state.position;
|
||||
|
@ -99,7 +99,7 @@ impl MessageHandler<InputPreprocessorMessage, KeyboardPlatformLayout> for InputP
|
|||
self.translate_mouse_event(mouse_state, false, responses);
|
||||
}
|
||||
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);
|
||||
self.mouse.position = mouse_state.position;
|
||||
|
@ -138,18 +138,30 @@ impl InputPreprocessorMessageHandler {
|
|||
self.mouse = new_state;
|
||||
}
|
||||
|
||||
fn handle_modifier_keys(&mut self, modifier_keys: ModifierKeys, keyboard_platform: KeyboardPlatformLayout, responses: &mut VecDeque<Message>) {
|
||||
self.handle_modifier_key(Key::Shift, modifier_keys.contains(ModifierKeys::SHIFT), responses);
|
||||
self.handle_modifier_key(Key::Alt, modifier_keys.contains(ModifierKeys::ALT), responses);
|
||||
self.handle_modifier_key(Key::Control, modifier_keys.contains(ModifierKeys::CONTROL), responses);
|
||||
fn update_states_of_modifier_keys(&mut self, pressed_modifier_keys: ModifierKeys, keyboard_platform: KeyboardPlatformLayout, responses: &mut VecDeque<Message>) {
|
||||
let is_key_pressed = |key_to_check: ModifierKeys| pressed_modifier_keys.contains(key_to_check);
|
||||
|
||||
// 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 {
|
||||
KeyboardPlatformLayout::Mac => Key::Command,
|
||||
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);
|
||||
|
||||
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::layout::utility_types::layout_widget::Layout;
|
||||
use crate::messages::layout::utility_types::layout_widget::Widget;
|
||||
use crate::messages::portfolio::document::utility_types::misc::KeyboardPlatformLayout;
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
use graphene::layers::text_layer::Font;
|
||||
|
@ -14,21 +13,21 @@ pub struct LayoutMessageHandler {
|
|||
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]
|
||||
fn process_message(&mut self, message: LayoutMessage, data: (F, KeyboardPlatformLayout), responses: &mut std::collections::VecDeque<Message>) {
|
||||
let (action_input_mapping, keyboard_platform) = data;
|
||||
fn process_message(&mut self, message: LayoutMessage, data: F, responses: &mut std::collections::VecDeque<Message>) {
|
||||
let action_input_mapping = data;
|
||||
|
||||
use LayoutMessage::*;
|
||||
#[remain::sorted]
|
||||
match message {
|
||||
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 } => {
|
||||
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 } => {
|
||||
let layout = &mut self.layouts[layout_target as usize];
|
||||
|
@ -152,55 +151,49 @@ impl<F: Fn(&MessageDiscriminant) -> Vec<KeysGroup>> MessageHandler<LayoutMessage
|
|||
|
||||
impl LayoutMessageHandler {
|
||||
#[remain::check]
|
||||
fn send_layout(
|
||||
&self,
|
||||
layout_target: LayoutTarget,
|
||||
responses: &mut VecDeque<Message>,
|
||||
action_input_mapping: &impl Fn(&MessageDiscriminant) -> Vec<KeysGroup>,
|
||||
keyboard_platform: KeyboardPlatformLayout,
|
||||
) {
|
||||
fn send_layout(&self, layout_target: LayoutTarget, responses: &mut VecDeque<Message>, action_input_mapping: &impl Fn(&MessageDiscriminant) -> Vec<KeysGroup>) {
|
||||
let layout = &self.layouts[layout_target as usize];
|
||||
#[remain::sorted]
|
||||
let message = match layout_target {
|
||||
LayoutTarget::DialogDetails => FrontendMessage::UpdateDialogDetails {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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]
|
||||
|
|
|
@ -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::misc::ActionKeys;
|
||||
use crate::messages::layout::utility_types::misc::LayoutTarget;
|
||||
use crate::messages::portfolio::document::utility_types::misc::KeyboardPlatformLayout;
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -35,14 +34,14 @@ pub enum 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 {
|
||||
// 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| {
|
||||
tooltip_shortcut.to_keys(action_input_mapping);
|
||||
|
||||
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 !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 {
|
||||
for menu_column in &mut menu_layout.layout {
|
||||
menu_column.children.fill_in_shortcut_actions_with_keys(action_input_mapping);
|
||||
|
|
|
@ -24,6 +24,8 @@ pub enum Message {
|
|||
#[child]
|
||||
Frontend(FrontendMessage),
|
||||
#[child]
|
||||
Globals(GlobalsMessage),
|
||||
#[child]
|
||||
InputMapper(InputMapperMessage),
|
||||
#[child]
|
||||
InputPreprocessor(InputPreprocessorMessage),
|
||||
|
|
|
@ -4,6 +4,7 @@ pub mod broadcast;
|
|||
pub mod debug;
|
||||
pub mod dialog;
|
||||
pub mod frontend;
|
||||
pub mod globals;
|
||||
pub mod input_mapper;
|
||||
pub mod input_preprocessor;
|
||||
pub mod layout;
|
||||
|
|
|
@ -77,7 +77,7 @@ impl Platform {
|
|||
match self {
|
||||
Platform::Mac => KeyboardPlatformLayout::Mac,
|
||||
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,
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use crate::messages::portfolio::document::utility_types::clipboards::Clipboard;
|
||||
use crate::messages::portfolio::document::utility_types::misc::Platform;
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
use graphene::layers::text_layer::Font;
|
||||
|
@ -84,9 +83,6 @@ pub enum PortfolioMessage {
|
|||
SetActiveDocument {
|
||||
document_id: u64,
|
||||
},
|
||||
SetPlatform {
|
||||
platform: Platform,
|
||||
},
|
||||
UpdateDocumentWidgets,
|
||||
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::misc::LayoutTarget;
|
||||
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 graphene::layers::layer_info::LayerDataTypeDiscriminant;
|
||||
|
@ -22,7 +21,6 @@ pub struct PortfolioMessageHandler {
|
|||
active_document_id: Option<u64>,
|
||||
copy_buffer: [Vec<CopyBufferEntry>; INTERNAL_CLIPBOARD_COUNT as usize],
|
||||
font_cache: FontCache,
|
||||
pub platform: Platform,
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
SetActiveDocument { document_id } => self.active_document_id = Some(document_id),
|
||||
SetPlatform { platform } => self.platform = platform,
|
||||
UpdateDocumentWidgets => {
|
||||
if let Some(document) = self.active_document() {
|
||||
document.update_document_widgets(responses);
|
||||
|
|
|
@ -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::{DialogMessage, DialogMessageDiscriminant, DialogMessageHandler};
|
||||
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_preprocessor::{InputPreprocessorMessage, InputPreprocessorMessageDiscriminant, InputPreprocessorMessageHandler};
|
||||
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};
|
||||
|
||||
// Helper
|
||||
pub use crate::messages::globals::global_variables::*;
|
||||
|
||||
pub use graphite_proc_macros::*;
|
||||
|
||||
pub use std::collections::{HashMap, HashSet, VecDeque};
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use crate::application::set_uuid_seed;
|
||||
use crate::application::Editor;
|
||||
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::portfolio::document::utility_types::misc::Platform;
|
||||
use crate::messages::prelude::*;
|
||||
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
|
||||
pub trait EditorTestUtils {
|
||||
fn create() -> Editor;
|
||||
|
||||
fn new_document(&mut self);
|
||||
|
||||
fn draw_rect(&mut self, x1: f64, y1: f64, x2: f64, y2: f64);
|
||||
|
@ -26,6 +30,20 @@ pub trait EditorTestUtils {
|
|||
}
|
||||
|
||||
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) {
|
||||
self.handle_message(Message::Portfolio(PortfolioMessage::NewDocumentWithName { name: String::from("Test document") }));
|
||||
}
|
||||
|
|
|
@ -195,8 +195,11 @@ export default defineComponent({
|
|||
let key = keyWithLabel.key;
|
||||
const label = keyWithLabel.label;
|
||||
|
||||
// Replace Alt with Option on Mac
|
||||
if (key === "Alt" && platformIsMac()) key = "Option";
|
||||
// Replace Alt and Accel keys with their Mac-specific equivalents
|
||||
if (platformIsMac()) {
|
||||
if (key === "Alt") key = "Option";
|
||||
if (key === "Accel") key = "Command";
|
||||
}
|
||||
|
||||
// Either display an icon...
|
||||
// @ts-expect-error We want undefined if it isn't in the object
|
||||
|
|
|
@ -66,8 +66,11 @@ impl JsEditorHandle {
|
|||
return;
|
||||
}
|
||||
|
||||
// Get the editor instances, dispatch the message, and store the `FrontendMessage` queue response
|
||||
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| {
|
||||
// Get the editor instance for this editor ID, then dispatch the message to the backend, and return its response `FrontendMessage` queue
|
||||
editors
|
||||
.get_mut(&self.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 {
|
||||
// Send each `FrontendMessage` to the JavaScript frontend
|
||||
for message in frontend_messages.into_iter() {
|
||||
// Send each FrontendMessage to the JavaScript frontend
|
||||
self.send_frontend_message_to_js(message);
|
||||
}
|
||||
}
|
||||
|
@ -115,7 +119,7 @@ impl JsEditorHandle {
|
|||
_ => Platform::Unknown,
|
||||
};
|
||||
|
||||
self.dispatch(PortfolioMessage::SetPlatform { platform });
|
||||
self.dispatch(GlobalsMessage::SetPlatform { platform });
|
||||
self.dispatch(Message::Init);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue