Bring OptionAsAlt back for macOS

The correct handling of this setting requires to change the events
we're getting from the macOS on the fly and call `interpretKeyEvents`,
which could affect handling of the next events, meaning that we can't
provide them on `KeyEvent`.
This commit is contained in:
Kirill Chibisov 2023-06-20 19:07:49 +00:00 committed by GitHub
parent b2a46d0439
commit 7094a223af
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 214 additions and 6 deletions

View file

@ -67,6 +67,35 @@ extern_methods!(
}
}
pub fn keyEventWithType(
type_: NSEventType,
location: NSPoint,
modifier_flags: NSEventModifierFlags,
timestamp: NSTimeInterval,
window_num: NSInteger,
context: Option<&NSObject>,
characters: &NSString,
characters_ignoring_modifiers: &NSString,
is_a_repeat: bool,
scancode: c_ushort,
) -> Id<Self, Shared> {
unsafe {
msg_send_id![
Self::class(),
keyEventWithType: type_,
location: location,
modifierFlags: modifier_flags,
timestamp: timestamp,
windowNumber: window_num,
context: context,
characters: characters,
charactersIgnoringModifiers: characters_ignoring_modifiers,
isARepeat: is_a_repeat,
keyCode: scancode,
]
}
}
#[sel(locationInWindow)]
pub fn locationInWindow(&self) -> NSPoint;

View file

@ -28,6 +28,7 @@ use crate::{
TouchPhase, WindowEvent,
},
keyboard::{Key, KeyCode, KeyLocation, ModifiersState},
platform::macos::{OptionAsAlt, WindowExtMacOS},
platform::scancode::KeyCodeExtScancode,
platform_impl::platform::{
app_state::AppState,
@ -482,6 +483,7 @@ declare_class!(
// Get the characters from the event.
let old_ime_state = self.state.ime_state;
self.state.forward_key_to_app = false;
let event = replace_event(event, self.window().option_as_alt());
// The `interpretKeyEvents` function might call
// `setMarkedText`, `insertText`, and `doCommandBySelector`.
@ -500,7 +502,7 @@ declare_class!(
}
}
self.update_modifiers(event, false);
self.update_modifiers(&event, false);
let had_ime_input = match self.state.ime_state {
ImeState::Commited => {
@ -514,7 +516,7 @@ declare_class!(
};
if !had_ime_input || self.state.forward_key_to_app {
let key_event = create_key_event(event, true, event.is_a_repeat(), None);
let key_event = create_key_event(&event, true, event.is_a_repeat(), None);
self.queue_event(WindowEvent::KeyboardInput {
device_id: DEVICE_ID,
event: key_event,
@ -527,13 +529,14 @@ declare_class!(
fn key_up(&mut self, event: &NSEvent) {
trace_scope!("keyUp:");
self.update_modifiers(event, false);
let event = replace_event(event, self.window().option_as_alt());
self.update_modifiers(&event, false);
// We want to send keyboard input when we are currently in the ground state.
if matches!(self.state.ime_state, ImeState::Ground | ImeState::Disabled) {
self.queue_event(WindowEvent::KeyboardInput {
device_id: DEVICE_ID,
event: create_key_event(event, false, false, None),
event: create_key_event(&event, false, false, None),
is_synthetic: false,
});
}
@ -1038,3 +1041,38 @@ fn mouse_button(event: &NSEvent) -> MouseButton {
n => MouseButton::Other(n as u16),
}
}
// NOTE: to get option as alt working we need to rewrite events
// we're getting from the operating system, which makes it
// impossible to provide such events as extra in `KeyEvent`.
fn replace_event(event: &NSEvent, option_as_alt: OptionAsAlt) -> Id<NSEvent, Shared> {
let ev_mods = event_mods(event).state;
let ignore_alt_characters = match option_as_alt {
OptionAsAlt::OnlyLeft if event.lalt_pressed() => true,
OptionAsAlt::OnlyRight if event.ralt_pressed() => true,
OptionAsAlt::Both if ev_mods.alt_key() => true,
_ => false,
} && !ev_mods.control_key()
&& !ev_mods.super_key();
if ignore_alt_characters {
let ns_chars = event
.charactersIgnoringModifiers()
.expect("expected characters to be non-null");
NSEvent::keyEventWithType(
event.type_(),
event.locationInWindow(),
event.modifierFlags(),
event.timestamp(),
event.window_number(),
None,
&ns_chars,
&ns_chars,
event.is_a_repeat(),
event.key_code(),
)
} else {
event.copy()
}
}

View file

@ -21,7 +21,7 @@ use crate::{
error::{ExternalError, NotSupportedError, OsError as RootOsError},
event::WindowEvent,
icon::Icon,
platform::macos::WindowExtMacOS,
platform::macos::{OptionAsAlt, WindowExtMacOS},
platform_impl::platform::{
app_state::AppState,
appkit::NSWindowOrderingMode,
@ -85,6 +85,7 @@ pub struct PlatformSpecificWindowBuilderAttributes {
pub disallow_hidpi: bool,
pub has_shadow: bool,
pub accepts_first_mouse: bool,
pub option_as_alt: OptionAsAlt,
}
impl Default for PlatformSpecificWindowBuilderAttributes {
@ -100,6 +101,7 @@ impl Default for PlatformSpecificWindowBuilderAttributes {
disallow_hidpi: false,
has_shadow: true,
accepts_first_mouse: true,
option_as_alt: Default::default(),
}
}
}
@ -160,6 +162,8 @@ pub struct SharedState {
/// The current resize incerments for the window content.
pub(crate) resize_increments: NSSize,
/// The state of the `Option` as `Alt`.
pub(crate) option_as_alt: OptionAsAlt,
}
impl SharedState {
@ -369,6 +373,8 @@ impl WinitWindow {
this.center();
}
this.set_option_as_alt(pl_attrs.option_as_alt);
Id::into_shared(this)
})
})
@ -1376,6 +1382,16 @@ impl WindowExtMacOS for WinitWindow {
fn set_document_edited(&self, edited: bool) {
self.setDocumentEdited(edited)
}
fn set_option_as_alt(&self, option_as_alt: OptionAsAlt) {
let mut shared_state_lock = self.shared_state.lock().unwrap();
shared_state_lock.option_as_alt = option_as_alt;
}
fn option_as_alt(&self) -> OptionAsAlt {
let shared_state_lock = self.shared_state.lock().unwrap();
shared_state_lock.option_as_alt
}
}
pub(super) fn get_ns_theme() -> Theme {