macOS: Add key equivalents for non-Latin layouts (#20401)
Closes #16343
Closes #10972
Release Notes:
- (breaking change) On macOS when using a keyboard that supports an
extended Latin character set (e.g. French, German, ...) keyboard
shortcuts are automatically updated so that they can be typed without
`option`. This fixes several long-standing problems where some keyboards
could not type some shortcuts.
- This mapping works the same way as
[macOS](https://developer.apple.com/documentation/swiftui/view/keyboardshortcut(_:modifiers:localization:)).
For example on a German keyboard shortcuts like `cmd->` become `cmd-:`,
`cmd-[` and `cmd-]` become `cmd-ö` and `cmd-ä`. This mapping happens at
the time keyboard layout files are read so the keybindings are visible
in the command palette. To opt out of this behavior for your custom
keyboard shortcuts, set `"use_layout_keys": true` in your binding
section. For the mappings used for each layout [see
here](a890df1863/crates/settings/src/key_equivalents.rs (L7)
).
---------
Co-authored-by: Will <will@zed.dev>
This commit is contained in:
parent
07821083df
commit
ff4f67993b
16 changed files with 435 additions and 10 deletions
|
@ -243,6 +243,7 @@ pub struct AppContext {
|
|||
pub(crate) windows: SlotMap<WindowId, Option<Window>>,
|
||||
pub(crate) window_handles: FxHashMap<WindowId, AnyWindowHandle>,
|
||||
pub(crate) keymap: Rc<RefCell<Keymap>>,
|
||||
pub(crate) keyboard_layout: SharedString,
|
||||
pub(crate) global_action_listeners:
|
||||
FxHashMap<TypeId, Vec<Rc<dyn Fn(&dyn Any, DispatchPhase, &mut Self)>>>,
|
||||
pending_effects: VecDeque<Effect>,
|
||||
|
@ -252,6 +253,7 @@ pub struct AppContext {
|
|||
// TypeId is the type of the event that the listener callback expects
|
||||
pub(crate) event_listeners: SubscriberSet<EntityId, (TypeId, Listener)>,
|
||||
pub(crate) keystroke_observers: SubscriberSet<(), KeystrokeObserver>,
|
||||
pub(crate) keyboard_layout_observers: SubscriberSet<(), Handler>,
|
||||
pub(crate) release_listeners: SubscriberSet<EntityId, ReleaseListener>,
|
||||
pub(crate) global_observers: SubscriberSet<TypeId, Handler>,
|
||||
pub(crate) quit_observers: SubscriberSet<(), QuitHandler>,
|
||||
|
@ -279,6 +281,7 @@ impl AppContext {
|
|||
|
||||
let text_system = Arc::new(TextSystem::new(platform.text_system()));
|
||||
let entities = EntityMap::new();
|
||||
let keyboard_layout = SharedString::from(platform.keyboard_layout());
|
||||
|
||||
let app = Rc::new_cyclic(|this| AppCell {
|
||||
app: RefCell::new(AppContext {
|
||||
|
@ -302,6 +305,7 @@ impl AppContext {
|
|||
window_handles: FxHashMap::default(),
|
||||
windows: SlotMap::with_key(),
|
||||
keymap: Rc::new(RefCell::new(Keymap::default())),
|
||||
keyboard_layout,
|
||||
global_action_listeners: FxHashMap::default(),
|
||||
pending_effects: VecDeque::new(),
|
||||
pending_notifications: FxHashSet::default(),
|
||||
|
@ -310,6 +314,7 @@ impl AppContext {
|
|||
event_listeners: SubscriberSet::new(),
|
||||
release_listeners: SubscriberSet::new(),
|
||||
keystroke_observers: SubscriberSet::new(),
|
||||
keyboard_layout_observers: SubscriberSet::new(),
|
||||
global_observers: SubscriberSet::new(),
|
||||
quit_observers: SubscriberSet::new(),
|
||||
layout_id_buffer: Default::default(),
|
||||
|
@ -323,6 +328,19 @@ impl AppContext {
|
|||
|
||||
init_app_menus(platform.as_ref(), &mut app.borrow_mut());
|
||||
|
||||
platform.on_keyboard_layout_change(Box::new({
|
||||
let app = Rc::downgrade(&app);
|
||||
move || {
|
||||
if let Some(app) = app.upgrade() {
|
||||
let cx = &mut app.borrow_mut();
|
||||
cx.keyboard_layout = SharedString::from(cx.platform.keyboard_layout());
|
||||
cx.keyboard_layout_observers
|
||||
.clone()
|
||||
.retain(&(), move |callback| (callback)(cx));
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
platform.on_quit(Box::new({
|
||||
let cx = app.clone();
|
||||
move || {
|
||||
|
@ -356,6 +374,27 @@ impl AppContext {
|
|||
}
|
||||
}
|
||||
|
||||
/// Get the id of the current keyboard layout
|
||||
pub fn keyboard_layout(&self) -> &SharedString {
|
||||
&self.keyboard_layout
|
||||
}
|
||||
|
||||
/// Invokes a handler when the current keyboard layout changes
|
||||
pub fn on_keyboard_layout_change<F>(&self, mut callback: F) -> Subscription
|
||||
where
|
||||
F: 'static + FnMut(&mut AppContext),
|
||||
{
|
||||
let (subscription, activate) = self.keyboard_layout_observers.insert(
|
||||
(),
|
||||
Box::new(move |cx| {
|
||||
callback(cx);
|
||||
true
|
||||
}),
|
||||
);
|
||||
activate();
|
||||
subscription
|
||||
}
|
||||
|
||||
/// Gracefully quit the application via the platform's standard routine.
|
||||
pub fn quit(&self) {
|
||||
self.platform.quit();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue