Add Caps Lock support (#30470)

Closes #21700

Release Notes:

- Added caps lock support and show a warning if the user is entering an
SSH password with Caps Lock enabled

---------

Co-authored-by: Mikayla Maki <mikayla@zed.dev>
Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
Co-authored-by: 张小白 <364772080@qq.com>
This commit is contained in:
Maxim Zaks 2025-06-18 02:43:33 +02:00 committed by GitHub
parent e47c48fd3b
commit 90aa99bb14
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 146 additions and 31 deletions

View file

@ -1,7 +1,7 @@
use crate::{ use crate::{
Action, AnyView, AnyWindowHandle, App, AppCell, AppContext, AsyncApp, AvailableSpace, Action, AnyView, AnyWindowHandle, App, AppCell, AppContext, AsyncApp, AvailableSpace,
BackgroundExecutor, BorrowAppContext, Bounds, ClipboardItem, DrawPhase, Drawable, Element, BackgroundExecutor, BorrowAppContext, Bounds, Capslock, ClipboardItem, DrawPhase, Drawable,
Empty, EventEmitter, ForegroundExecutor, Global, InputEvent, Keystroke, Modifiers, Element, Empty, EventEmitter, ForegroundExecutor, Global, InputEvent, Keystroke, Modifiers,
ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels,
Platform, Point, Render, Result, Size, Task, TestDispatcher, TestPlatform, Platform, Point, Render, Result, Size, Task, TestDispatcher, TestPlatform,
TestScreenCaptureSource, TestWindow, TextSystem, VisualContext, Window, WindowBounds, TestScreenCaptureSource, TestWindow, TextSystem, VisualContext, Window, WindowBounds,
@ -771,7 +771,18 @@ impl VisualTestContext {
/// Simulate a modifiers changed event /// Simulate a modifiers changed event
pub fn simulate_modifiers_change(&mut self, modifiers: Modifiers) { pub fn simulate_modifiers_change(&mut self, modifiers: Modifiers) {
self.simulate_event(ModifiersChangedEvent { modifiers }) self.simulate_event(ModifiersChangedEvent {
modifiers,
capslock: Capslock { on: false },
})
}
/// Simulate a capslock changed event
pub fn simulate_capslock_change(&mut self, on: bool) {
self.simulate_event(ModifiersChangedEvent {
modifiers: Modifiers::none(),
capslock: Capslock { on },
})
} }
/// Simulates the user resizing the window to the new size. /// Simulates the user resizing the window to the new size.

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
Context, Empty, IntoElement, Keystroke, Modifiers, Pixels, Point, Render, Window, point, Capslock, Context, Empty, IntoElement, Keystroke, Modifiers, Pixels, Point, Render, Window,
seal::Sealed, point, seal::Sealed,
}; };
use smallvec::SmallVec; use smallvec::SmallVec;
use std::{any::Any, fmt::Debug, ops::Deref, path::PathBuf}; use std::{any::Any, fmt::Debug, ops::Deref, path::PathBuf};
@ -55,6 +55,8 @@ impl KeyEvent for KeyUpEvent {}
pub struct ModifiersChangedEvent { pub struct ModifiersChangedEvent {
/// The new state of the modifier keys /// The new state of the modifier keys
pub modifiers: Modifiers, pub modifiers: Modifiers,
/// The new state of the capslock key
pub capslock: Capslock,
} }
impl Sealed for ModifiersChangedEvent {} impl Sealed for ModifiersChangedEvent {}

View file

@ -415,6 +415,7 @@ pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle {
fn display(&self) -> Option<Rc<dyn PlatformDisplay>>; fn display(&self) -> Option<Rc<dyn PlatformDisplay>>;
fn mouse_position(&self) -> Point<Pixels>; fn mouse_position(&self) -> Point<Pixels>;
fn modifiers(&self) -> Modifiers; fn modifiers(&self) -> Modifiers;
fn capslock(&self) -> Capslock;
fn set_input_handler(&mut self, input_handler: PlatformInputHandler); fn set_input_handler(&mut self, input_handler: PlatformInputHandler);
fn take_input_handler(&mut self) -> Option<PlatformInputHandler>; fn take_input_handler(&mut self) -> Option<PlatformInputHandler>;
fn prompt( fn prompt(

View file

@ -538,3 +538,11 @@ impl Modifiers {
&& (other.function || !self.function) && (other.function || !self.function)
} }
} }
/// The state of the capslock key at some point in time
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default, Serialize, Deserialize, Hash, JsonSchema)]
pub struct Capslock {
/// The capslock key is on
#[serde(default)]
pub on: bool,
}

View file

@ -873,6 +873,14 @@ impl crate::Modifiers {
} }
} }
#[cfg(any(feature = "wayland", feature = "x11"))]
impl crate::Capslock {
pub(super) fn from_xkb(keymap_state: &State) -> Self {
let on = keymap_state.mod_name_is_active(xkb::MOD_NAME_CAPS, xkb::STATE_MODS_EFFECTIVE);
Self { on }
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View file

@ -73,7 +73,7 @@ use super::{
use crate::platform::{PlatformWindow, blade::BladeContext}; use crate::platform::{PlatformWindow, blade::BladeContext};
use crate::{ use crate::{
AnyWindowHandle, Bounds, CursorStyle, DOUBLE_CLICK_INTERVAL, DevicePixels, DisplayId, AnyWindowHandle, Bounds, Capslock, CursorStyle, DOUBLE_CLICK_INTERVAL, DevicePixels, DisplayId,
FileDropEvent, ForegroundExecutor, KeyDownEvent, KeyUpEvent, Keystroke, LinuxCommon, FileDropEvent, ForegroundExecutor, KeyDownEvent, KeyUpEvent, Keystroke, LinuxCommon,
LinuxKeyboardLayout, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, LinuxKeyboardLayout, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent,
MouseExitEvent, MouseMoveEvent, MouseUpEvent, NavigationDirection, Pixels, PlatformDisplay, MouseExitEvent, MouseMoveEvent, MouseUpEvent, NavigationDirection, Pixels, PlatformDisplay,
@ -217,6 +217,7 @@ pub(crate) struct WaylandClientState {
click: ClickState, click: ClickState,
repeat: KeyRepeat, repeat: KeyRepeat,
pub modifiers: Modifiers, pub modifiers: Modifiers,
pub capslock: Capslock,
axis_source: AxisSource, axis_source: AxisSource,
pub mouse_location: Option<Point<Pixels>>, pub mouse_location: Option<Point<Pixels>>,
continuous_scroll_delta: Option<Point<Pixels>>, continuous_scroll_delta: Option<Point<Pixels>>,
@ -595,6 +596,7 @@ impl WaylandClient {
function: false, function: false,
platform: false, platform: false,
}, },
capslock: Capslock { on: false },
scroll_event_received: false, scroll_event_received: false,
axis_source: AxisSource::Wheel, axis_source: AxisSource::Wheel,
mouse_location: None, mouse_location: None,
@ -1251,9 +1253,12 @@ impl Dispatch<wl_keyboard::WlKeyboard, ()> for WaylandClientStatePtr {
keymap_state.serialize_layout(xkbcommon::xkb::STATE_LAYOUT_EFFECTIVE); keymap_state.serialize_layout(xkbcommon::xkb::STATE_LAYOUT_EFFECTIVE);
keymap_state.update_mask(mods_depressed, mods_latched, mods_locked, 0, 0, group); keymap_state.update_mask(mods_depressed, mods_latched, mods_locked, 0, 0, group);
state.modifiers = Modifiers::from_xkb(keymap_state); state.modifiers = Modifiers::from_xkb(keymap_state);
let keymap_state = state.keymap_state.as_mut().unwrap();
state.capslock = Capslock::from_xkb(keymap_state);
let input = PlatformInput::ModifiersChanged(ModifiersChangedEvent { let input = PlatformInput::ModifiersChanged(ModifiersChangedEvent {
modifiers: state.modifiers, modifiers: state.modifiers,
capslock: state.capslock,
}); });
drop(state); drop(state);

View file

@ -21,11 +21,6 @@ use wayland_protocols::xdg::shell::client::xdg_surface;
use wayland_protocols::xdg::shell::client::xdg_toplevel::{self}; use wayland_protocols::xdg::shell::client::xdg_toplevel::{self};
use wayland_protocols_plasma::blur::client::org_kde_kwin_blur; use wayland_protocols_plasma::blur::client::org_kde_kwin_blur;
use crate::platform::{
PlatformAtlas, PlatformInputHandler, PlatformWindow,
blade::{BladeContext, BladeRenderer, BladeSurfaceConfig},
linux::wayland::{display::WaylandDisplay, serial::SerialKind},
};
use crate::scene::Scene; use crate::scene::Scene;
use crate::{ use crate::{
AnyWindowHandle, Bounds, Decorations, Globals, GpuSpecs, Modifiers, Output, Pixels, AnyWindowHandle, Bounds, Decorations, Globals, GpuSpecs, Modifiers, Output, Pixels,
@ -34,6 +29,14 @@ use crate::{
WindowBackgroundAppearance, WindowBounds, WindowControlArea, WindowControls, WindowDecorations, WindowBackgroundAppearance, WindowBounds, WindowControlArea, WindowControls, WindowDecorations,
WindowParams, px, size, WindowParams, px, size,
}; };
use crate::{
Capslock,
platform::{
PlatformAtlas, PlatformInputHandler, PlatformWindow,
blade::{BladeContext, BladeRenderer, BladeSurfaceConfig},
linux::wayland::{display::WaylandDisplay, serial::SerialKind},
},
};
#[derive(Default)] #[derive(Default)]
pub(crate) struct Callbacks { pub(crate) struct Callbacks {
@ -861,6 +864,10 @@ impl PlatformWindow for WaylandWindow {
self.borrow().client.get_client().borrow().modifiers self.borrow().client.get_client().borrow().modifiers
} }
fn capslock(&self) -> Capslock {
self.borrow().client.get_client().borrow().capslock
}
fn set_input_handler(&mut self, input_handler: PlatformInputHandler) { fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
self.borrow_mut().input_handler = Some(input_handler); self.borrow_mut().input_handler = Some(input_handler);
} }

View file

@ -1,3 +1,4 @@
use crate::Capslock;
use core::str; use core::str;
use std::{ use std::{
cell::RefCell, cell::RefCell,
@ -203,8 +204,11 @@ pub struct X11ClientState {
pub(crate) ximc: Option<X11rbClient<Rc<XCBConnection>>>, pub(crate) ximc: Option<X11rbClient<Rc<XCBConnection>>>,
pub(crate) xim_handler: Option<XimHandler>, pub(crate) xim_handler: Option<XimHandler>,
pub modifiers: Modifiers, pub modifiers: Modifiers,
pub capslock: Capslock,
// TODO: Can the other updates to `modifiers` be removed so that this is unnecessary? // TODO: Can the other updates to `modifiers` be removed so that this is unnecessary?
// capslock logic was done analog to modifiers
pub last_modifiers_changed_event: Modifiers, pub last_modifiers_changed_event: Modifiers,
pub last_capslock_changed_event: Capslock,
pub(crate) compose_state: Option<xkbc::compose::State>, pub(crate) compose_state: Option<xkbc::compose::State>,
pub(crate) pre_edit_text: Option<String>, pub(crate) pre_edit_text: Option<String>,
@ -473,7 +477,9 @@ impl X11Client {
X11Client(Rc::new(RefCell::new(X11ClientState { X11Client(Rc::new(RefCell::new(X11ClientState {
modifiers: Modifiers::default(), modifiers: Modifiers::default(),
capslock: Capslock::default(),
last_modifiers_changed_event: Modifiers::default(), last_modifiers_changed_event: Modifiers::default(),
last_capslock_changed_event: Capslock::default(),
event_loop: Some(event_loop), event_loop: Some(event_loop),
loop_handle: handle, loop_handle: handle,
common, common,
@ -961,17 +967,25 @@ impl X11Client {
}; };
let modifiers = Modifiers::from_xkb(&state.xkb); let modifiers = Modifiers::from_xkb(&state.xkb);
if state.last_modifiers_changed_event == modifiers { let capslock = Capslock::from_xkb(&state.xkb);
if state.last_modifiers_changed_event == modifiers
&& state.last_capslock_changed_event == capslock
{
drop(state); drop(state);
} else { } else {
let focused_window_id = state.keyboard_focused_window?; let focused_window_id = state.keyboard_focused_window?;
state.modifiers = modifiers; state.modifiers = modifiers;
state.last_modifiers_changed_event = modifiers; state.last_modifiers_changed_event = modifiers;
state.capslock = capslock;
state.last_capslock_changed_event = capslock;
drop(state); drop(state);
let focused_window = self.get_window(focused_window_id)?; let focused_window = self.get_window(focused_window_id)?;
focused_window.handle_input(PlatformInput::ModifiersChanged( focused_window.handle_input(PlatformInput::ModifiersChanged(
ModifiersChangedEvent { modifiers }, ModifiersChangedEvent {
modifiers,
capslock,
},
)); ));
} }

View file

@ -1215,6 +1215,17 @@ impl PlatformWindow for X11Window {
.unwrap_or_default() .unwrap_or_default()
} }
fn capslock(&self) -> crate::Capslock {
self.0
.state
.borrow()
.client
.0
.upgrade()
.map(|ref_cell| ref_cell.borrow().capslock)
.unwrap_or_default()
}
fn set_input_handler(&mut self, input_handler: PlatformInputHandler) { fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
self.0.state.borrow_mut().input_handler = Some(input_handler); self.0.state.borrow_mut().input_handler = Some(input_handler);
} }

View file

@ -1,5 +1,5 @@
use crate::{ use crate::{
KeyDownEvent, KeyUpEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton, Capslock, KeyDownEvent, KeyUpEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton,
MouseDownEvent, MouseExitEvent, MouseMoveEvent, MouseUpEvent, NavigationDirection, Pixels, MouseDownEvent, MouseExitEvent, MouseMoveEvent, MouseUpEvent, NavigationDirection, Pixels,
PlatformInput, ScrollDelta, ScrollWheelEvent, TouchPhase, PlatformInput, ScrollDelta, ScrollWheelEvent, TouchPhase,
platform::mac::{ platform::mac::{
@ -121,6 +121,11 @@ impl PlatformInput {
NSEventType::NSFlagsChanged => { NSEventType::NSFlagsChanged => {
Some(Self::ModifiersChanged(ModifiersChangedEvent { Some(Self::ModifiersChanged(ModifiersChangedEvent {
modifiers: read_modifiers(native_event), modifiers: read_modifiers(native_event),
capslock: Capslock {
on: native_event
.modifierFlags()
.contains(NSEventModifierFlags::NSAlphaShiftKeyMask),
},
})) }))
} }
NSEventType::NSKeyDown => Some(Self::KeyDown(KeyDownEvent { NSEventType::NSKeyDown => Some(Self::KeyDown(KeyDownEvent {

View file

@ -1,11 +1,11 @@
use super::{BoolExt, MacDisplay, NSRange, NSStringExt, ns_string, renderer}; use super::{BoolExt, MacDisplay, NSRange, NSStringExt, ns_string, renderer};
use crate::{ use crate::{
AnyWindowHandle, Bounds, DisplayLink, ExternalPaths, FileDropEvent, ForegroundExecutor, AnyWindowHandle, Bounds, Capslock, DisplayLink, ExternalPaths, FileDropEvent,
KeyDownEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, ForegroundExecutor, KeyDownEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton,
MouseMoveEvent, MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay,
PlatformWindow, Point, PromptButton, PromptLevel, RequestFrameOptions, ScaledPixels, Size, PlatformInput, PlatformWindow, Point, PromptButton, PromptLevel, RequestFrameOptions,
Timer, WindowAppearance, WindowBackgroundAppearance, WindowBounds, WindowControlArea, ScaledPixels, Size, Timer, WindowAppearance, WindowBackgroundAppearance, WindowBounds,
WindowKind, WindowParams, platform::PlatformInputHandler, point, px, size, WindowControlArea, WindowKind, WindowParams, platform::PlatformInputHandler, point, px, size,
}; };
use block::ConcreteBlock; use block::ConcreteBlock;
use cocoa::{ use cocoa::{
@ -890,6 +890,16 @@ impl PlatformWindow for MacWindow {
} }
} }
fn capslock(&self) -> Capslock {
unsafe {
let modifiers: NSEventModifierFlags = msg_send![class!(NSEvent), modifierFlags];
Capslock {
on: modifiers.contains(NSEventModifierFlags::NSAlphaShiftKeyMask),
}
}
}
fn set_input_handler(&mut self, input_handler: PlatformInputHandler) { fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
self.0.as_ref().lock().input_handler = Some(input_handler); self.0.as_ref().lock().input_handler = Some(input_handler);
} }
@ -1556,13 +1566,17 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
lock.synthetic_drag_counter += 1; lock.synthetic_drag_counter += 1;
} }
PlatformInput::ModifiersChanged(ModifiersChangedEvent { modifiers }) => { PlatformInput::ModifiersChanged(ModifiersChangedEvent {
modifiers,
capslock,
}) => {
// Only raise modifiers changed event when they have actually changed // Only raise modifiers changed event when they have actually changed
if let Some(PlatformInput::ModifiersChanged(ModifiersChangedEvent { if let Some(PlatformInput::ModifiersChanged(ModifiersChangedEvent {
modifiers: prev_modifiers, modifiers: prev_modifiers,
capslock: prev_capslock,
})) = &lock.previous_modifiers_changed_event })) = &lock.previous_modifiers_changed_event
{ {
if prev_modifiers == modifiers { if prev_modifiers == modifiers && prev_capslock == capslock {
return; return;
} }
} }

View file

@ -153,6 +153,10 @@ impl PlatformWindow for TestWindow {
crate::Modifiers::default() crate::Modifiers::default()
} }
fn capslock(&self) -> crate::Capslock {
crate::Capslock::default()
}
fn set_input_handler(&mut self, input_handler: PlatformInputHandler) { fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
self.0.lock().input_handler = Some(input_handler); self.0.lock().input_handler = Some(input_handler);
} }

View file

@ -1227,6 +1227,7 @@ where
{ {
let virtual_key = VIRTUAL_KEY(wparam.loword()); let virtual_key = VIRTUAL_KEY(wparam.loword());
let mut modifiers = current_modifiers(); let mut modifiers = current_modifiers();
let capslock = current_capslock();
match virtual_key { match virtual_key {
VK_SHIFT | VK_CONTROL | VK_MENU | VK_LWIN | VK_RWIN => { VK_SHIFT | VK_CONTROL | VK_MENU | VK_LWIN | VK_RWIN => {
@ -1239,6 +1240,7 @@ where
state.last_reported_modifiers = Some(modifiers); state.last_reported_modifiers = Some(modifiers);
Some(PlatformInput::ModifiersChanged(ModifiersChangedEvent { Some(PlatformInput::ModifiersChanged(ModifiersChangedEvent {
modifiers, modifiers,
capslock,
})) }))
} }
vkey => { vkey => {
@ -1371,6 +1373,12 @@ pub(crate) fn current_modifiers() -> Modifiers {
} }
} }
#[inline]
pub(crate) fn current_capslock() -> Capslock {
let on = unsafe { GetKeyState(VK_CAPITAL.0 as i32) & 1 } > 0;
Capslock { on: on }
}
fn get_client_area_insets( fn get_client_area_insets(
handle: HWND, handle: HWND,
is_maximized: bool, is_maximized: bool,

View file

@ -559,6 +559,10 @@ impl PlatformWindow for WindowsWindow {
current_modifiers() current_modifiers()
} }
fn capslock(&self) -> Capslock {
current_capslock()
}
fn set_input_handler(&mut self, input_handler: PlatformInputHandler) { fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
self.0.state.borrow_mut().input_handler = Some(input_handler); self.0.state.borrow_mut().input_handler = Some(input_handler);
} }

View file

@ -2,15 +2,15 @@
use crate::Inspector; use crate::Inspector;
use crate::{ use crate::{
Action, AnyDrag, AnyElement, AnyImageCache, AnyTooltip, AnyView, App, AppContext, Arena, Asset, Action, AnyDrag, AnyElement, AnyImageCache, AnyTooltip, AnyView, App, AppContext, Arena, Asset,
AsyncWindowContext, AvailableSpace, Background, BorderStyle, Bounds, BoxShadow, Context, AsyncWindowContext, AvailableSpace, Background, BorderStyle, Bounds, BoxShadow, Capslock,
Corners, CursorStyle, Decorations, DevicePixels, DispatchActionListener, DispatchNodeId, Context, Corners, CursorStyle, Decorations, DevicePixels, DispatchActionListener,
DispatchTree, DisplayId, Edges, Effect, Entity, EntityId, EventEmitter, FileDropEvent, FontId, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity, EntityId, EventEmitter,
Global, GlobalElementId, GlyphId, GpuSpecs, Hsla, InputHandler, IsZero, KeyBinding, KeyContext, FileDropEvent, FontId, Global, GlobalElementId, GlyphId, GpuSpecs, Hsla, InputHandler, IsZero,
KeyDownEvent, KeyEvent, Keystroke, KeystrokeEvent, LayoutId, LineLayoutIndex, Modifiers, KeyBinding, KeyContext, KeyDownEvent, KeyEvent, Keystroke, KeystrokeEvent, LayoutId,
ModifiersChangedEvent, MonochromeSprite, MouseButton, MouseEvent, MouseMoveEvent, MouseUpEvent, LineLayoutIndex, Modifiers, ModifiersChangedEvent, MonochromeSprite, MouseButton, MouseEvent,
Path, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput,
PlatformWindow, Point, PolychromeSprite, PromptButton, PromptLevel, Quad, Render, PlatformInputHandler, PlatformWindow, Point, PolychromeSprite, PromptButton, PromptLevel, Quad,
RenderGlyphParams, RenderImage, RenderImageParams, RenderSvgParams, Replay, ResizeEdge, Render, RenderGlyphParams, RenderImage, RenderImageParams, RenderSvgParams, Replay, ResizeEdge,
SMOOTH_SVG_SCALE_FACTOR, SUBPIXEL_VARIANTS, ScaledPixels, Scene, Shadow, SharedString, Size, SMOOTH_SVG_SCALE_FACTOR, SUBPIXEL_VARIANTS, ScaledPixels, Scene, Shadow, SharedString, Size,
StrikethroughStyle, Style, SubscriberSet, Subscription, TaffyLayoutEngine, Task, TextStyle, StrikethroughStyle, Style, SubscriberSet, Subscription, TaffyLayoutEngine, Task, TextStyle,
TextStyleRefinement, TransformationMatrix, Underline, UnderlineStyle, WindowAppearance, TextStyleRefinement, TransformationMatrix, Underline, UnderlineStyle, WindowAppearance,
@ -796,6 +796,7 @@ pub struct Window {
mouse_position: Point<Pixels>, mouse_position: Point<Pixels>,
mouse_hit_test: HitTest, mouse_hit_test: HitTest,
modifiers: Modifiers, modifiers: Modifiers,
capslock: Capslock,
scale_factor: f32, scale_factor: f32,
pub(crate) bounds_observers: SubscriberSet<(), AnyObserver>, pub(crate) bounds_observers: SubscriberSet<(), AnyObserver>,
appearance: WindowAppearance, appearance: WindowAppearance,
@ -907,6 +908,7 @@ impl Window {
let sprite_atlas = platform_window.sprite_atlas(); let sprite_atlas = platform_window.sprite_atlas();
let mouse_position = platform_window.mouse_position(); let mouse_position = platform_window.mouse_position();
let modifiers = platform_window.modifiers(); let modifiers = platform_window.modifiers();
let capslock = platform_window.capslock();
let content_size = platform_window.content_size(); let content_size = platform_window.content_size();
let scale_factor = platform_window.scale_factor(); let scale_factor = platform_window.scale_factor();
let appearance = platform_window.appearance(); let appearance = platform_window.appearance();
@ -1015,6 +1017,7 @@ impl Window {
.update(&mut cx, |_, window, cx| { .update(&mut cx, |_, window, cx| {
window.active.set(active); window.active.set(active);
window.modifiers = window.platform_window.modifiers(); window.modifiers = window.platform_window.modifiers();
window.capslock = window.platform_window.capslock();
window window
.activation_observers .activation_observers
.clone() .clone()
@ -1100,6 +1103,7 @@ impl Window {
mouse_position, mouse_position,
mouse_hit_test: HitTest::default(), mouse_hit_test: HitTest::default(),
modifiers, modifiers,
capslock,
scale_factor, scale_factor,
bounds_observers: SubscriberSet::new(), bounds_observers: SubscriberSet::new(),
appearance, appearance,
@ -1728,6 +1732,11 @@ impl Window {
self.modifiers self.modifiers
} }
/// The current state of the keyboard's capslock
pub fn capslock(&self) -> Capslock {
self.capslock
}
fn complete_frame(&self) { fn complete_frame(&self) {
self.platform_window.completed_frame(); self.platform_window.completed_frame();
} }
@ -3352,6 +3361,7 @@ impl Window {
} }
PlatformInput::ModifiersChanged(modifiers_changed) => { PlatformInput::ModifiersChanged(modifiers_changed) => {
self.modifiers = modifiers_changed.modifiers; self.modifiers = modifiers_changed.modifiers;
self.capslock = modifiers_changed.capslock;
PlatformInput::ModifiersChanged(modifiers_changed) PlatformInput::ModifiersChanged(modifiers_changed)
} }
PlatformInput::ScrollWheel(scroll_wheel) => { PlatformInput::ScrollWheel(scroll_wheel) => {

View file

@ -289,6 +289,9 @@ impl Render for SshPrompt {
.child(MarkdownElement::new(prompt.0.clone(), markdown_style)) .child(MarkdownElement::new(prompt.0.clone(), markdown_style))
.child(self.editor.clone()), .child(self.editor.clone()),
) )
.when(window.capslock().on, |el| {
el.child(Label::new("⚠️ ⇪ is on"))
})
}) })
} }
} }