gpui: Introduce PlatformKeyboardLayout
trait for human-friendly keyboard layout names (#29049)
This PR adds a new `PlatformKeyboardLayout` trait with two methods: `id(&self) -> &str` and `name(&self) -> &str`. The `id()` method returns a unique identifier for the keyboard layout, while `name()` provides a human-readable name. This distinction is especially important on Windows, where the `id` and `name` can be quite different. For example, the French layout has an `id` of `0000040C`, which is not human-readable, whereas the `name` would simply be `French`. Currently, the existing `keyboard_layout()` method returns what's essentially the same as `id()` in this new design. This PR implements the `name()` method for both Windows and macOS. On Linux, for now, `name()` still returns the same value as `id()`. Release Notes: - N/A
This commit is contained in:
parent
0454e7a22e
commit
f0ef3110d3
16 changed files with 197 additions and 68 deletions
14
Cargo.lock
generated
14
Cargo.lock
generated
|
@ -6171,6 +6171,7 @@ dependencies = [
|
|||
"windows 0.61.1",
|
||||
"windows-core 0.61.0",
|
||||
"windows-numerics",
|
||||
"windows-registry 0.5.1",
|
||||
"workspace-hack",
|
||||
"x11-clipboard",
|
||||
"x11rb",
|
||||
|
@ -11930,7 +11931,7 @@ dependencies = [
|
|||
"wasm-bindgen-futures",
|
||||
"wasm-streams",
|
||||
"web-sys",
|
||||
"windows-registry",
|
||||
"windows-registry 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -17044,6 +17045,17 @@ dependencies = [
|
|||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-registry"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad1da3e436dc7653dfdf3da67332e22bff09bb0e28b0239e1624499c7830842e"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows-result 0.3.2",
|
||||
"windows-strings 0.4.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.1.2"
|
||||
|
|
|
@ -66,7 +66,7 @@ x11 = [
|
|||
"x11-clipboard",
|
||||
"filedescriptor",
|
||||
"open",
|
||||
"scap"
|
||||
"scap",
|
||||
]
|
||||
|
||||
|
||||
|
@ -220,6 +220,7 @@ rand.workspace = true
|
|||
windows.workspace = true
|
||||
windows-core = "0.61"
|
||||
windows-numerics = "0.2"
|
||||
windows-registry = "0.5"
|
||||
|
||||
[dev-dependencies]
|
||||
backtrace = "0.3"
|
||||
|
|
|
@ -635,7 +635,7 @@ impl Render for InputExample {
|
|||
.flex()
|
||||
.flex_row()
|
||||
.justify_between()
|
||||
.child(format!("Keyboard {}", cx.keyboard_layout()))
|
||||
.child(format!("Keyboard {}", cx.keyboard_layout().name()))
|
||||
.child(
|
||||
div()
|
||||
.border_1()
|
||||
|
|
|
@ -35,10 +35,10 @@ use crate::{
|
|||
AssetSource, BackgroundExecutor, Bounds, ClipboardItem, CursorStyle, DispatchPhase, DisplayId,
|
||||
EventEmitter, FocusHandle, FocusMap, ForegroundExecutor, Global, KeyBinding, Keymap, Keystroke,
|
||||
LayoutId, Menu, MenuItem, OwnedMenu, PathPromptOptions, Pixels, Platform, PlatformDisplay,
|
||||
Point, PromptBuilder, PromptHandle, PromptLevel, Render, RenderablePromptHandle, Reservation,
|
||||
ScreenCaptureSource, SharedString, SubscriberSet, Subscription, SvgRenderer, Task, TextSystem,
|
||||
Window, WindowAppearance, WindowHandle, WindowId, WindowInvalidator, current_platform, hash,
|
||||
init_app_menus,
|
||||
PlatformKeyboardLayout, Point, PromptBuilder, PromptHandle, PromptLevel, Render,
|
||||
RenderablePromptHandle, Reservation, ScreenCaptureSource, SharedString, SubscriberSet,
|
||||
Subscription, SvgRenderer, Task, TextSystem, Window, WindowAppearance, WindowHandle, WindowId,
|
||||
WindowInvalidator, current_platform, hash, init_app_menus,
|
||||
};
|
||||
|
||||
mod async_context;
|
||||
|
@ -248,7 +248,7 @@ pub struct App {
|
|||
pub(crate) window_handles: FxHashMap<WindowId, AnyWindowHandle>,
|
||||
pub(crate) focus_handles: Arc<FocusMap>,
|
||||
pub(crate) keymap: Rc<RefCell<Keymap>>,
|
||||
pub(crate) keyboard_layout: SharedString,
|
||||
pub(crate) keyboard_layout: Box<dyn PlatformKeyboardLayout>,
|
||||
pub(crate) global_action_listeners:
|
||||
FxHashMap<TypeId, Vec<Rc<dyn Fn(&dyn Any, DispatchPhase, &mut Self)>>>,
|
||||
pending_effects: VecDeque<Effect>,
|
||||
|
@ -289,7 +289,7 @@ impl App {
|
|||
|
||||
let text_system = Arc::new(TextSystem::new(platform.text_system()));
|
||||
let entities = EntityMap::new();
|
||||
let keyboard_layout = SharedString::from(platform.keyboard_layout());
|
||||
let keyboard_layout = platform.keyboard_layout();
|
||||
|
||||
let app = Rc::new_cyclic(|this| AppCell {
|
||||
app: RefCell::new(App {
|
||||
|
@ -345,7 +345,7 @@ impl 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 = cx.platform.keyboard_layout();
|
||||
cx.keyboard_layout_observers
|
||||
.clone()
|
||||
.retain(&(), move |callback| (callback)(cx));
|
||||
|
@ -387,8 +387,8 @@ impl App {
|
|||
}
|
||||
|
||||
/// Get the id of the current keyboard layout
|
||||
pub fn keyboard_layout(&self) -> &SharedString {
|
||||
&self.keyboard_layout
|
||||
pub fn keyboard_layout(&self) -> &dyn PlatformKeyboardLayout {
|
||||
self.keyboard_layout.as_ref()
|
||||
}
|
||||
|
||||
/// Invokes a handler when the current keyboard layout changes
|
||||
|
|
|
@ -214,7 +214,7 @@ pub(crate) trait Platform: 'static {
|
|||
fn on_app_menu_action(&self, callback: Box<dyn FnMut(&dyn Action)>);
|
||||
fn on_will_open_app_menu(&self, callback: Box<dyn FnMut()>);
|
||||
fn on_validate_app_menu_command(&self, callback: Box<dyn FnMut(&dyn Action) -> bool>);
|
||||
fn keyboard_layout(&self) -> String;
|
||||
fn keyboard_layout(&self) -> Box<dyn PlatformKeyboardLayout>;
|
||||
|
||||
fn compositor_name(&self) -> &'static str {
|
||||
""
|
||||
|
@ -1634,3 +1634,11 @@ impl From<String> for ClipboardString {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait for platform-specific keyboard layouts
|
||||
pub trait PlatformKeyboardLayout {
|
||||
/// Get the keyboard layout ID, which should be unique to the layout
|
||||
fn id(&self) -> &str;
|
||||
/// Get the keyboard layout display name
|
||||
fn name(&self) -> &str;
|
||||
}
|
||||
|
|
|
@ -9,7 +9,8 @@ use util::ResultExt;
|
|||
use crate::platform::linux::LinuxClient;
|
||||
use crate::platform::{LinuxCommon, PlatformWindow};
|
||||
use crate::{
|
||||
AnyWindowHandle, CursorStyle, DisplayId, PlatformDisplay, ScreenCaptureSource, WindowParams,
|
||||
AnyWindowHandle, CursorStyle, DisplayId, LinuxKeyboardLayout, PlatformDisplay,
|
||||
PlatformKeyboardLayout, ScreenCaptureSource, WindowParams,
|
||||
};
|
||||
|
||||
pub struct HeadlessClientState {
|
||||
|
@ -50,8 +51,8 @@ impl LinuxClient for HeadlessClient {
|
|||
f(&mut self.0.borrow_mut().common)
|
||||
}
|
||||
|
||||
fn keyboard_layout(&self) -> String {
|
||||
"unknown".to_string()
|
||||
fn keyboard_layout(&self) -> Box<dyn PlatformKeyboardLayout> {
|
||||
Box::new(LinuxKeyboardLayout::new("unknown".to_string()))
|
||||
}
|
||||
|
||||
fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
|
||||
|
|
|
@ -25,8 +25,8 @@ use xkbcommon::xkb::{self, Keycode, Keysym, State};
|
|||
use crate::{
|
||||
Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId,
|
||||
ForegroundExecutor, Keymap, LinuxDispatcher, Menu, MenuItem, OwnedMenu, PathPromptOptions,
|
||||
Pixels, Platform, PlatformDisplay, PlatformTextSystem, PlatformWindow, Point, Result,
|
||||
ScreenCaptureSource, Task, WindowAppearance, WindowParams, px,
|
||||
Pixels, Platform, PlatformDisplay, PlatformKeyboardLayout, PlatformTextSystem, PlatformWindow,
|
||||
Point, Result, ScreenCaptureSource, Task, WindowAppearance, WindowParams, px,
|
||||
};
|
||||
|
||||
#[cfg(any(feature = "wayland", feature = "x11"))]
|
||||
|
@ -46,7 +46,7 @@ const FILE_PICKER_PORTAL_MISSING: &str =
|
|||
pub trait LinuxClient {
|
||||
fn compositor_name(&self) -> &'static str;
|
||||
fn with_common<R>(&self, f: impl FnOnce(&mut LinuxCommon) -> R) -> R;
|
||||
fn keyboard_layout(&self) -> String;
|
||||
fn keyboard_layout(&self) -> Box<dyn PlatformKeyboardLayout>;
|
||||
fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>>;
|
||||
#[allow(unused)]
|
||||
fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>>;
|
||||
|
@ -138,7 +138,7 @@ impl<P: LinuxClient + 'static> Platform for P {
|
|||
self.with_common(|common| common.text_system.clone())
|
||||
}
|
||||
|
||||
fn keyboard_layout(&self) -> String {
|
||||
fn keyboard_layout(&self) -> Box<dyn PlatformKeyboardLayout> {
|
||||
self.keyboard_layout()
|
||||
}
|
||||
|
||||
|
@ -858,6 +858,26 @@ impl crate::Modifiers {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) struct LinuxKeyboardLayout {
|
||||
id: String,
|
||||
}
|
||||
|
||||
impl PlatformKeyboardLayout for LinuxKeyboardLayout {
|
||||
fn id(&self) -> &str {
|
||||
&self.id
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.id
|
||||
}
|
||||
}
|
||||
|
||||
impl LinuxKeyboardLayout {
|
||||
pub(crate) fn new(id: String) -> Self {
|
||||
Self { id }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
@ -66,8 +66,10 @@ use wayland_protocols_plasma::blur::client::{org_kde_kwin_blur, org_kde_kwin_blu
|
|||
use xkbcommon::xkb::ffi::XKB_KEYMAP_FORMAT_TEXT_V1;
|
||||
use xkbcommon::xkb::{self, KEYMAP_COMPILE_NO_FLAGS, Keycode};
|
||||
|
||||
use super::display::WaylandDisplay;
|
||||
use super::window::{ImeInput, WaylandWindowStatePtr};
|
||||
use super::{
|
||||
display::WaylandDisplay,
|
||||
window::{ImeInput, WaylandWindowStatePtr},
|
||||
};
|
||||
|
||||
use crate::platform::linux::{
|
||||
LinuxClient, get_xkb_compose_state, is_within_click_distance, open_uri_internal, read_fd,
|
||||
|
@ -83,11 +85,11 @@ use crate::platform::linux::{
|
|||
use crate::platform::{PlatformWindow, blade::BladeContext};
|
||||
use crate::{
|
||||
AnyWindowHandle, Bounds, CursorStyle, DOUBLE_CLICK_INTERVAL, DevicePixels, DisplayId,
|
||||
FileDropEvent, ForegroundExecutor, KeyDownEvent, KeyUpEvent, Keystroke, LinuxCommon, Modifiers,
|
||||
ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseExitEvent, MouseMoveEvent,
|
||||
MouseUpEvent, NavigationDirection, Pixels, PlatformDisplay, PlatformInput, Point, SCROLL_LINES,
|
||||
ScaledPixels, ScreenCaptureSource, ScrollDelta, ScrollWheelEvent, Size, TouchPhase,
|
||||
WindowParams, point, px, size,
|
||||
FileDropEvent, ForegroundExecutor, KeyDownEvent, KeyUpEvent, Keystroke, LinuxCommon,
|
||||
LinuxKeyboardLayout, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent,
|
||||
MouseExitEvent, MouseMoveEvent, MouseUpEvent, NavigationDirection, Pixels, PlatformDisplay,
|
||||
PlatformInput, PlatformKeyboardLayout, Point, SCROLL_LINES, ScaledPixels, ScreenCaptureSource,
|
||||
ScrollDelta, ScrollWheelEvent, Size, TouchPhase, WindowParams, point, px, size,
|
||||
};
|
||||
|
||||
/// Used to convert evdev scancode to xkb scancode
|
||||
|
@ -587,9 +589,9 @@ impl WaylandClient {
|
|||
}
|
||||
|
||||
impl LinuxClient for WaylandClient {
|
||||
fn keyboard_layout(&self) -> String {
|
||||
fn keyboard_layout(&self) -> Box<dyn PlatformKeyboardLayout> {
|
||||
let state = self.0.borrow();
|
||||
if let Some(keymap_state) = &state.keymap_state {
|
||||
let id = if let Some(keymap_state) = &state.keymap_state {
|
||||
let layout_idx = keymap_state.serialize_layout(xkbcommon::xkb::STATE_LAYOUT_EFFECTIVE);
|
||||
keymap_state
|
||||
.get_keymap()
|
||||
|
@ -597,7 +599,8 @@ impl LinuxClient for WaylandClient {
|
|||
.to_string()
|
||||
} else {
|
||||
"unknown".to_string()
|
||||
}
|
||||
};
|
||||
Box::new(LinuxKeyboardLayout::new(id))
|
||||
}
|
||||
|
||||
fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
|
||||
|
|
|
@ -59,9 +59,10 @@ use crate::platform::{
|
|||
};
|
||||
use crate::{
|
||||
AnyWindowHandle, Bounds, ClipboardItem, CursorStyle, DisplayId, FileDropEvent, Keystroke,
|
||||
Modifiers, ModifiersChangedEvent, MouseButton, Pixels, Platform, PlatformDisplay,
|
||||
PlatformInput, Point, RequestFrameOptions, ScaledPixels, ScreenCaptureSource, ScrollDelta,
|
||||
Size, TouchPhase, WindowParams, X11Window, modifiers_from_xinput_info, point, px,
|
||||
LinuxKeyboardLayout, Modifiers, ModifiersChangedEvent, MouseButton, Pixels, Platform,
|
||||
PlatformDisplay, PlatformInput, PlatformKeyboardLayout, Point, RequestFrameOptions,
|
||||
ScaledPixels, ScreenCaptureSource, ScrollDelta, Size, TouchPhase, WindowParams, X11Window,
|
||||
modifiers_from_xinput_info, point, px,
|
||||
};
|
||||
|
||||
/// Value for DeviceId parameters which selects all devices.
|
||||
|
@ -1282,14 +1283,16 @@ impl LinuxClient for X11Client {
|
|||
f(&mut self.0.borrow_mut().common)
|
||||
}
|
||||
|
||||
fn keyboard_layout(&self) -> String {
|
||||
fn keyboard_layout(&self) -> Box<dyn PlatformKeyboardLayout> {
|
||||
let state = self.0.borrow();
|
||||
let layout_idx = state.xkb.serialize_layout(STATE_LAYOUT_EFFECTIVE);
|
||||
state
|
||||
.xkb
|
||||
.get_keymap()
|
||||
.layout_get_name(layout_idx)
|
||||
.to_string()
|
||||
Box::new(LinuxKeyboardLayout::new(
|
||||
state
|
||||
.xkb
|
||||
.get_keymap()
|
||||
.layout_get_name(layout_idx)
|
||||
.to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
|
||||
|
|
|
@ -7,9 +7,9 @@ use super::{
|
|||
use crate::{
|
||||
Action, AnyWindowHandle, BackgroundExecutor, ClipboardEntry, ClipboardItem, ClipboardString,
|
||||
CursorStyle, ForegroundExecutor, Image, ImageFormat, Keymap, MacDispatcher, MacDisplay,
|
||||
MacWindow, Menu, MenuItem, PathPromptOptions, Platform, PlatformDisplay, PlatformTextSystem,
|
||||
PlatformWindow, Result, ScreenCaptureSource, SemanticVersion, Task, WindowAppearance,
|
||||
WindowParams, hash,
|
||||
MacWindow, Menu, MenuItem, PathPromptOptions, Platform, PlatformDisplay,
|
||||
PlatformKeyboardLayout, PlatformTextSystem, PlatformWindow, Result, ScreenCaptureSource,
|
||||
SemanticVersion, Task, WindowAppearance, WindowParams, hash,
|
||||
};
|
||||
use anyhow::{Context as _, anyhow};
|
||||
use block::ConcreteBlock;
|
||||
|
@ -825,20 +825,8 @@ impl Platform for MacPlatform {
|
|||
self.0.lock().validate_menu_command = Some(callback);
|
||||
}
|
||||
|
||||
fn keyboard_layout(&self) -> String {
|
||||
unsafe {
|
||||
let current_keyboard = TISCopyCurrentKeyboardLayoutInputSource();
|
||||
|
||||
let input_source_id: *mut Object = TISGetInputSourceProperty(
|
||||
current_keyboard,
|
||||
kTISPropertyInputSourceID as *const c_void,
|
||||
);
|
||||
let input_source_id: *const std::os::raw::c_char =
|
||||
msg_send![input_source_id, UTF8String];
|
||||
let input_source_id = CStr::from_ptr(input_source_id).to_str().unwrap();
|
||||
|
||||
input_source_id.to_string()
|
||||
}
|
||||
fn keyboard_layout(&self) -> Box<dyn PlatformKeyboardLayout> {
|
||||
Box::new(MacKeyboardLayout::new())
|
||||
}
|
||||
|
||||
fn app_path(&self) -> Result<PathBuf> {
|
||||
|
@ -1501,6 +1489,7 @@ unsafe extern "C" {
|
|||
pub(super) fn LMGetKbdType() -> u16;
|
||||
pub(super) static kTISPropertyUnicodeKeyLayoutData: CFStringRef;
|
||||
pub(super) static kTISPropertyInputSourceID: CFStringRef;
|
||||
pub(super) static kTISPropertyLocalizedName: CFStringRef;
|
||||
}
|
||||
|
||||
mod security {
|
||||
|
@ -1590,6 +1579,45 @@ impl UTType {
|
|||
}
|
||||
}
|
||||
|
||||
struct MacKeyboardLayout {
|
||||
id: String,
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl PlatformKeyboardLayout for MacKeyboardLayout {
|
||||
fn id(&self) -> &str {
|
||||
&self.id
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
||||
impl MacKeyboardLayout {
|
||||
fn new() -> Self {
|
||||
unsafe {
|
||||
let current_keyboard = TISCopyCurrentKeyboardLayoutInputSource();
|
||||
|
||||
let id: *mut Object = TISGetInputSourceProperty(
|
||||
current_keyboard,
|
||||
kTISPropertyInputSourceID as *const c_void,
|
||||
);
|
||||
let id: *const std::os::raw::c_char = msg_send![id, UTF8String];
|
||||
let id = CStr::from_ptr(id).to_str().unwrap().to_string();
|
||||
|
||||
let name: *mut Object = TISGetInputSourceProperty(
|
||||
current_keyboard,
|
||||
kTISPropertyLocalizedName as *const c_void,
|
||||
);
|
||||
let name: *const std::os::raw::c_char = msg_send![name, UTF8String];
|
||||
let name = CStr::from_ptr(name).to_str().unwrap().to_string();
|
||||
|
||||
Self { id, name }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::ClipboardItem;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use crate::{
|
||||
AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DevicePixels,
|
||||
ForegroundExecutor, Keymap, NoopTextSystem, Platform, PlatformDisplay, PlatformTextSystem,
|
||||
ScreenCaptureFrame, ScreenCaptureSource, ScreenCaptureStream, Size, Task, TestDisplay,
|
||||
TestWindow, WindowAppearance, WindowParams, size,
|
||||
ForegroundExecutor, Keymap, NoopTextSystem, Platform, PlatformDisplay, PlatformKeyboardLayout,
|
||||
PlatformTextSystem, ScreenCaptureFrame, ScreenCaptureSource, ScreenCaptureStream, Size, Task,
|
||||
TestDisplay, TestWindow, WindowAppearance, WindowParams, size,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use collections::VecDeque;
|
||||
|
@ -223,8 +223,8 @@ impl Platform for TestPlatform {
|
|||
self.text_system.clone()
|
||||
}
|
||||
|
||||
fn keyboard_layout(&self) -> String {
|
||||
"zed.keyboard.example".to_string()
|
||||
fn keyboard_layout(&self) -> Box<dyn PlatformKeyboardLayout> {
|
||||
Box::new(TestKeyboardLayout)
|
||||
}
|
||||
|
||||
fn on_keyboard_layout_change(&self, _: Box<dyn FnMut()>) {}
|
||||
|
@ -431,3 +431,15 @@ impl Drop for TestPlatform {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct TestKeyboardLayout;
|
||||
|
||||
impl PlatformKeyboardLayout for TestKeyboardLayout {
|
||||
fn id(&self) -> &str {
|
||||
"zed.keyboard.example"
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
"zed.keyboard.example"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -297,8 +297,12 @@ impl Platform for WindowsPlatform {
|
|||
self.text_system.clone()
|
||||
}
|
||||
|
||||
fn keyboard_layout(&self) -> String {
|
||||
"unknown".into()
|
||||
fn keyboard_layout(&self) -> Box<dyn PlatformKeyboardLayout> {
|
||||
Box::new(
|
||||
KeyboardLayout::new()
|
||||
.log_err()
|
||||
.unwrap_or(KeyboardLayout::unknown()),
|
||||
)
|
||||
}
|
||||
|
||||
fn on_keyboard_layout_change(&self, _callback: Box<dyn FnMut()>) {
|
||||
|
@ -836,6 +840,42 @@ fn should_auto_hide_scrollbars() -> Result<bool> {
|
|||
Ok(ui_settings.AutoHideScrollBars()?)
|
||||
}
|
||||
|
||||
struct KeyboardLayout {
|
||||
id: String,
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl PlatformKeyboardLayout for KeyboardLayout {
|
||||
fn id(&self) -> &str {
|
||||
&self.id
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyboardLayout {
|
||||
fn new() -> Result<Self> {
|
||||
let mut buffer = [0u16; KL_NAMELENGTH as usize];
|
||||
unsafe { GetKeyboardLayoutNameW(&mut buffer)? };
|
||||
let id = HSTRING::from_wide(&buffer).to_string();
|
||||
let entry = windows_registry::LOCAL_MACHINE.open(format!(
|
||||
"System\\CurrentControlSet\\Control\\Keyboard Layouts\\{}",
|
||||
id
|
||||
))?;
|
||||
let name = entry.get_hstring("Layout Text")?.to_string();
|
||||
Ok(Self { id, name })
|
||||
}
|
||||
|
||||
fn unknown() -> Self {
|
||||
Self {
|
||||
id: "unknown".to_string(),
|
||||
name: "unknown".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{ClipboardItem, read_from_clipboard, write_to_clipboard};
|
||||
|
|
|
@ -173,7 +173,7 @@ impl Item for KeyContextView {
|
|||
impl Render for KeyContextView {
|
||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl ui::IntoElement {
|
||||
use itertools::Itertools;
|
||||
let key_equivalents = get_key_equivalents(cx.keyboard_layout());
|
||||
let key_equivalents = get_key_equivalents(cx.keyboard_layout().id());
|
||||
v_flex()
|
||||
.id("key-context-view")
|
||||
.overflow_scroll()
|
||||
|
|
|
@ -195,7 +195,8 @@ impl KeymapFile {
|
|||
}
|
||||
|
||||
pub fn load(content: &str, cx: &App) -> KeymapFileLoadResult {
|
||||
let key_equivalents = crate::key_equivalents::get_key_equivalents(&cx.keyboard_layout());
|
||||
let key_equivalents =
|
||||
crate::key_equivalents::get_key_equivalents(cx.keyboard_layout().id());
|
||||
|
||||
if content.is_empty() {
|
||||
return KeymapFileLoadResult::Success {
|
||||
|
|
|
@ -5439,7 +5439,7 @@ impl Render for Workspace {
|
|||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let mut context = KeyContext::new_with_defaults();
|
||||
context.add("Workspace");
|
||||
context.set("keyboard_layout", cx.keyboard_layout().clone());
|
||||
context.set("keyboard_layout", cx.keyboard_layout().name().to_string());
|
||||
let centered_layout = self.centered_layout
|
||||
&& self.center.panes().len() == 1
|
||||
&& self.active_item(cx).is_some();
|
||||
|
|
|
@ -1224,9 +1224,9 @@ pub fn handle_keymap_file_changes(
|
|||
})
|
||||
.detach();
|
||||
|
||||
let mut current_mapping = settings::get_key_equivalents(cx.keyboard_layout());
|
||||
let mut current_mapping = settings::get_key_equivalents(cx.keyboard_layout().id());
|
||||
cx.on_keyboard_layout_change(move |cx| {
|
||||
let next_mapping = settings::get_key_equivalents(cx.keyboard_layout());
|
||||
let next_mapping = settings::get_key_equivalents(cx.keyboard_layout().id());
|
||||
if next_mapping != current_mapping {
|
||||
current_mapping = next_mapping;
|
||||
keyboard_layout_tx.unbounded_send(()).ok();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue