diff --git a/rio-window/src/application.rs b/rio-window/src/application.rs --- a/rio-window/src/application.rs +++ b/rio-window/src/application.rs @@ -1,6 +1,7 @@ //! End user application handling. -use crate::event::{DeviceEvent, DeviceId, Hook, StartCause, WindowEvent}; +use crate::event::{DeviceEvent, DeviceId, StartCause, WindowEvent}; +use crate::event::{KeyEvent, Modifiers}; use crate::event_loop::ActiveEventLoop; use crate::window::WindowId; @@ -231,7 +232,12 @@ let _ = event_loop; } - fn hook_event(&mut self, event_loop: &ActiveEventLoop, hook: &Hook) { - let _ = (event_loop, hook); + fn hook_event( + &mut self, + event_loop: &ActiveEventLoop, + hook: &KeyEvent, + modifiers: &Modifiers, + ) { + let _ = (event_loop, hook, modifiers); } } diff --git a/rio-window/src/event.rs b/rio-window/src/event.rs --- a/rio-window/src/event.rs +++ b/rio-window/src/event.rs @@ -113,7 +113,7 @@ }, OpenConfig, - HookEvent(Hook), + HookEvent(KeyEvent, Modifiers), } impl Event { @@ -132,19 +132,9 @@ MemoryWarning => Ok(MemoryWarning), Opened { urls } => Ok(Opened { urls }), OpenConfig => Ok(OpenConfig), - HookEvent(hook) => Ok(HookEvent(hook)), + HookEvent(hook, modifiers) => Ok(HookEvent(hook, modifiers)), } } -} - -#[derive(Debug, Clone, PartialEq)] -pub enum Hook { - CreateTab, - Close, - Copy, - Paste, - SplitDown, - SplitRight, } /// Describes the reason the event loop is resuming. diff --git a/rio-window/src/event_loop.rs b/rio-window/src/event_loop.rs --- a/rio-window/src/event_loop.rs +++ b/rio-window/src/event_loop.rs @@ -696,7 +696,9 @@ Event::LoopExiting => app.exiting(event_loop), Event::MemoryWarning => app.memory_warning(event_loop), Event::Opened { urls } => app.open_urls(event_loop, urls), - Event::HookEvent(hook) => app.hook_event(event_loop, &hook), + Event::HookEvent(hook, modifiers) => { + app.hook_event(event_loop, &hook, &modifiers) + } Event::OpenConfig => app.open_config(event_loop), } } diff --git a/frontends/rioterm/src/application.rs b/frontends/rioterm/src/application.rs --- a/frontends/rioterm/src/application.rs +++ b/frontends/rioterm/src/application.rs @@ -10,8 +10,7 @@ use rio_backend::config::colors::ColorRgb; use rio_window::application::ApplicationHandler; use rio_window::event::{ - ElementState, Hook, Ime, MouseButton, MouseScrollDelta, StartCause, TouchPhase, - WindowEvent, + ElementState, Ime, MouseButton, MouseScrollDelta, StartCause, TouchPhase, WindowEvent, }; use rio_window::event_loop::ActiveEventLoop; use rio_window::event_loop::ControlFlow; @@ -1170,7 +1169,12 @@ } } - fn hook_event(&mut self, _event_loop: &ActiveEventLoop, hook: &Hook) { + fn hook_event( + &mut self, + _event_loop: &ActiveEventLoop, + key: &rio_window::event::KeyEvent, + modifiers: &rio_window::event::Modifiers, + ) { let window_id = match self.router.get_focused_route() { Some(window_id) => window_id, None => return, @@ -1181,38 +1185,18 @@ None => return, }; - match hook { - Hook::Copy => { - route.window.screen.copy_selection(ClipboardType::Clipboard); - } - Hook::Paste => { - let content = route - .window - .screen - .clipboard - .borrow_mut() - .get(ClipboardType::Selection); - route.window.screen.paste(&content, true); - } - Hook::CreateTab => { - if self.config.navigation.has_navigation_key_bindings() { - route.window.screen.create_tab(); - } - } - Hook::Close => { - route.window.screen.close_split_or_tab(); - } - Hook::SplitDown => { - if self.config.navigation.use_split { - route.window.screen.split_down(); - } - } - Hook::SplitRight => { - if self.config.navigation.use_split { - route.window.screen.split_right(); - } - } - } + // For menu-triggered events, we need to temporarily set the correct modifiers + // since menu events don't trigger ModifiersChanged events. + let original_modifiers = route.window.screen.modifiers; + + // Use the modifiers passed from the menu action + route.window.screen.set_modifiers(*modifiers); + + // Process the key event + route.window.screen.process_key_event(&key); + + // Restore the original modifiers + route.window.screen.set_modifiers(original_modifiers); } // Emitted when the event loop is being shut down. diff --git a/rio-window/src/platform_impl/macos/app_delegate.rs b/rio-window/src/platform_impl/macos/app_delegate.rs --- a/rio-window/src/platform_impl/macos/app_delegate.rs +++ b/rio-window/src/platform_impl/macos/app_delegate.rs @@ -1,3 +1,5 @@ +use crate::event::KeyEvent; +use crate::keyboard::{KeyCode, ModifiersState}; use crate::platform_impl::platform::menu::menu_item; use objc2::sel; use objc2_app_kit::NSMenu; @@ -25,7 +27,9 @@ use super::window::WinitWindow; use super::{menu, WindowId, DEVICE_ID}; use crate::dpi::PhysicalSize; -use crate::event::{DeviceEvent, Event, Hook, InnerSizeWriter, StartCause, WindowEvent}; +use crate::event::{ + DeviceEvent, Event, InnerSizeWriter, Modifiers, StartCause, WindowEvent, +}; use crate::event_loop::{ActiveEventLoop as RootActiveEventLoop, ControlFlow}; use crate::window::WindowId as RootWindowId; @@ -264,42 +268,84 @@ #[method(copy:)] fn copy(&self, _sender: Option<&AnyObject>) { if self.is_launched() { - self.dispatch_hook(Hook::Copy); + let modifiers_state = ModifiersState::SUPER; + let modifiers: Modifiers = modifiers_state.into(); + let key_event = Self::create_key_event( + KeyCode::KeyC, + modifiers_state, + Some("c") + ); + self.dispatch_hook(key_event, modifiers); } } #[method(paste:)] fn paste(&self, _sender: Option<&AnyObject>) { if self.is_launched() { - self.dispatch_hook(Hook::Paste); + let modifiers_state = ModifiersState::SUPER; + let modifiers: Modifiers = modifiers_state.into(); + let key_event = Self::create_key_event( + KeyCode::KeyV, + modifiers_state, + Some("v") + ); + self.dispatch_hook(key_event, modifiers); } } #[method(rioCreateTab:)] fn create_tab(&self, _sender: Option<&AnyObject>) { if self.is_launched() { - self.dispatch_hook(Hook::CreateTab); + let modifiers_state = ModifiersState::SUPER; + let modifiers: Modifiers = modifiers_state.into(); + let key_event = Self::create_key_event( + KeyCode::KeyT, + modifiers_state, + Some("t") + ); + self.dispatch_hook(key_event, modifiers); } } #[method(rioClose:)] fn close_tab(&self, _sender: Option<&AnyObject>) { if self.is_launched() { - self.dispatch_hook(Hook::Close); - } - } - - #[method(rioSplitDown:)] - fn split_down(&self, _sender: Option<&AnyObject>) { - if self.is_launched() { - self.dispatch_hook(Hook::SplitDown); + let modifiers_state = ModifiersState::SUPER; + let modifiers: Modifiers = modifiers_state.into(); + let key_event = Self::create_key_event( + KeyCode::KeyW, + modifiers_state, + Some("w") + ); + self.dispatch_hook(key_event, modifiers); } } #[method(rioSplitRight:)] fn split_right(&self, _sender: Option<&AnyObject>) { if self.is_launched() { - self.dispatch_hook(Hook::SplitRight); + let modifiers_state = ModifiersState::SUPER; + let modifiers: Modifiers = modifiers_state.into(); + let key_event = Self::create_key_event( + KeyCode::KeyD, + modifiers_state, + Some("d") + ); + self.dispatch_hook(key_event, modifiers); + } + } + + #[method(rioSplitDown:)] + fn split_down(&self, _sender: Option<&AnyObject>) { + if self.is_launched() { + let modifiers_state = ModifiersState::SUPER | ModifiersState::SHIFT; + let modifiers: Modifiers = modifiers_state.into(); + let key_event = Self::create_key_event( + KeyCode::KeyD, + modifiers_state, + Some("d") + ); + self.dispatch_hook(key_event, modifiers); } } @@ -519,8 +565,40 @@ self.handle_event(Event::NewEvents(StartCause::CreateWindow)); } - pub fn dispatch_hook(&self, hook: Hook) { - self.handle_event(Event::HookEvent(hook)); + pub fn dispatch_hook(&self, key: KeyEvent, modifiers: Modifiers) { + self.handle_event(Event::HookEvent(key, modifiers)); + } + + /// Create a KeyEvent for common shortcuts + fn create_key_event( + key_code: KeyCode, + _modifiers: ModifiersState, + character: Option<&str>, + ) -> crate::event::KeyEvent { + use crate::event::ElementState; + use crate::keyboard::{Key, KeyLocation, NativeKey, PhysicalKey}; + use crate::platform_impl::KeyEventExtra; + use smol_str::SmolStr; + + let logical_key = if let Some(ch) = character { + Key::Character(SmolStr::new(ch)) + } else { + // For keys without character representation, use Unidentified + Key::Unidentified(NativeKey::MacOS(0)) + }; + + crate::event::KeyEvent { + physical_key: PhysicalKey::Code(key_code), + logical_key: logical_key.clone(), + text: character.map(SmolStr::new), + location: KeyLocation::Standard, + state: ElementState::Pressed, + repeat: false, + platform_specific: KeyEventExtra { + text_with_all_modifiers: character.map(SmolStr::new), + key_without_modifiers: logical_key, + }, + } } pub fn dispatch_open_configuration(&self) {