From 90aa99bb14d2fcbfd6bbbc6de0fd0de812cdbecf Mon Sep 17 00:00:00 2001 From: Maxim Zaks Date: Wed, 18 Jun 2025 02:43:33 +0200 Subject: [PATCH] Add Caps Lock support (#30470) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Co-authored-by: Mikayla Maki Co-authored-by: 张小白 <364772080@qq.com> --- crates/gpui/src/app/test_context.rs | 17 +++++++++-- crates/gpui/src/interactive.rs | 6 ++-- crates/gpui/src/platform.rs | 1 + crates/gpui/src/platform/keystroke.rs | 8 +++++ crates/gpui/src/platform/linux/platform.rs | 8 +++++ .../gpui/src/platform/linux/wayland/client.rs | 7 ++++- .../gpui/src/platform/linux/wayland/window.rs | 17 +++++++---- crates/gpui/src/platform/linux/x11/client.rs | 18 +++++++++-- crates/gpui/src/platform/linux/x11/window.rs | 11 +++++++ crates/gpui/src/platform/mac/events.rs | 7 ++++- crates/gpui/src/platform/mac/window.rs | 30 ++++++++++++++----- crates/gpui/src/platform/test/window.rs | 4 +++ crates/gpui/src/platform/windows/events.rs | 8 +++++ crates/gpui/src/platform/windows/window.rs | 4 +++ crates/gpui/src/window.rs | 28 +++++++++++------ crates/recent_projects/src/ssh_connections.rs | 3 ++ 16 files changed, 146 insertions(+), 31 deletions(-) diff --git a/crates/gpui/src/app/test_context.rs b/crates/gpui/src/app/test_context.rs index a861b82ff6..dfc7af0d9c 100644 --- a/crates/gpui/src/app/test_context.rs +++ b/crates/gpui/src/app/test_context.rs @@ -1,7 +1,7 @@ use crate::{ Action, AnyView, AnyWindowHandle, App, AppCell, AppContext, AsyncApp, AvailableSpace, - BackgroundExecutor, BorrowAppContext, Bounds, ClipboardItem, DrawPhase, Drawable, Element, - Empty, EventEmitter, ForegroundExecutor, Global, InputEvent, Keystroke, Modifiers, + BackgroundExecutor, BorrowAppContext, Bounds, Capslock, ClipboardItem, DrawPhase, Drawable, + Element, Empty, EventEmitter, ForegroundExecutor, Global, InputEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, Platform, Point, Render, Result, Size, Task, TestDispatcher, TestPlatform, TestScreenCaptureSource, TestWindow, TextSystem, VisualContext, Window, WindowBounds, @@ -771,7 +771,18 @@ impl VisualTestContext { /// Simulate a modifiers changed event 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. diff --git a/crates/gpui/src/interactive.rs b/crates/gpui/src/interactive.rs index 02d0aaac38..16cd8381cd 100644 --- a/crates/gpui/src/interactive.rs +++ b/crates/gpui/src/interactive.rs @@ -1,6 +1,6 @@ use crate::{ - Context, Empty, IntoElement, Keystroke, Modifiers, Pixels, Point, Render, Window, point, - seal::Sealed, + Capslock, Context, Empty, IntoElement, Keystroke, Modifiers, Pixels, Point, Render, Window, + point, seal::Sealed, }; use smallvec::SmallVec; use std::{any::Any, fmt::Debug, ops::Deref, path::PathBuf}; @@ -55,6 +55,8 @@ impl KeyEvent for KeyUpEvent {} pub struct ModifiersChangedEvent { /// The new state of the modifier keys pub modifiers: Modifiers, + /// The new state of the capslock key + pub capslock: Capslock, } impl Sealed for ModifiersChangedEvent {} diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index 8bc5bf0862..66068db560 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -415,6 +415,7 @@ pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle { fn display(&self) -> Option>; fn mouse_position(&self) -> Point; fn modifiers(&self) -> Modifiers; + fn capslock(&self) -> Capslock; fn set_input_handler(&mut self, input_handler: PlatformInputHandler); fn take_input_handler(&mut self) -> Option; fn prompt( diff --git a/crates/gpui/src/platform/keystroke.rs b/crates/gpui/src/platform/keystroke.rs index 3a7070da7f..18adc1af10 100644 --- a/crates/gpui/src/platform/keystroke.rs +++ b/crates/gpui/src/platform/keystroke.rs @@ -538,3 +538,11 @@ impl Modifiers { && (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, +} diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index aee05706d9..ddb7f7918e 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -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)] mod tests { use super::*; diff --git a/crates/gpui/src/platform/linux/wayland/client.rs b/crates/gpui/src/platform/linux/wayland/client.rs index 77b8cf5cca..2cf6d35f30 100644 --- a/crates/gpui/src/platform/linux/wayland/client.rs +++ b/crates/gpui/src/platform/linux/wayland/client.rs @@ -73,7 +73,7 @@ use super::{ use crate::platform::{PlatformWindow, blade::BladeContext}; 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, LinuxKeyboardLayout, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseExitEvent, MouseMoveEvent, MouseUpEvent, NavigationDirection, Pixels, PlatformDisplay, @@ -217,6 +217,7 @@ pub(crate) struct WaylandClientState { click: ClickState, repeat: KeyRepeat, pub modifiers: Modifiers, + pub capslock: Capslock, axis_source: AxisSource, pub mouse_location: Option>, continuous_scroll_delta: Option>, @@ -595,6 +596,7 @@ impl WaylandClient { function: false, platform: false, }, + capslock: Capslock { on: false }, scroll_event_received: false, axis_source: AxisSource::Wheel, mouse_location: None, @@ -1251,9 +1253,12 @@ impl Dispatch for WaylandClientStatePtr { keymap_state.serialize_layout(xkbcommon::xkb::STATE_LAYOUT_EFFECTIVE); keymap_state.update_mask(mods_depressed, mods_latched, mods_locked, 0, 0, group); 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 { modifiers: state.modifiers, + capslock: state.capslock, }); drop(state); diff --git a/crates/gpui/src/platform/linux/wayland/window.rs b/crates/gpui/src/platform/linux/wayland/window.rs index 0b98b8bd1d..4507101eed 100644 --- a/crates/gpui/src/platform/linux/wayland/window.rs +++ b/crates/gpui/src/platform/linux/wayland/window.rs @@ -21,11 +21,6 @@ use wayland_protocols::xdg::shell::client::xdg_surface; use wayland_protocols::xdg::shell::client::xdg_toplevel::{self}; 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::{ AnyWindowHandle, Bounds, Decorations, Globals, GpuSpecs, Modifiers, Output, Pixels, @@ -34,6 +29,14 @@ use crate::{ WindowBackgroundAppearance, WindowBounds, WindowControlArea, WindowControls, WindowDecorations, WindowParams, px, size, }; +use crate::{ + Capslock, + platform::{ + PlatformAtlas, PlatformInputHandler, PlatformWindow, + blade::{BladeContext, BladeRenderer, BladeSurfaceConfig}, + linux::wayland::{display::WaylandDisplay, serial::SerialKind}, + }, +}; #[derive(Default)] pub(crate) struct Callbacks { @@ -861,6 +864,10 @@ impl PlatformWindow for WaylandWindow { 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) { self.borrow_mut().input_handler = Some(input_handler); } diff --git a/crates/gpui/src/platform/linux/x11/client.rs b/crates/gpui/src/platform/linux/x11/client.rs index 2b99277ce9..d2884f37c7 100644 --- a/crates/gpui/src/platform/linux/x11/client.rs +++ b/crates/gpui/src/platform/linux/x11/client.rs @@ -1,3 +1,4 @@ +use crate::Capslock; use core::str; use std::{ cell::RefCell, @@ -203,8 +204,11 @@ pub struct X11ClientState { pub(crate) ximc: Option>>, pub(crate) xim_handler: Option, pub modifiers: Modifiers, + pub capslock: Capslock, // 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_capslock_changed_event: Capslock, pub(crate) compose_state: Option, pub(crate) pre_edit_text: Option, @@ -473,7 +477,9 @@ impl X11Client { X11Client(Rc::new(RefCell::new(X11ClientState { modifiers: Modifiers::default(), + capslock: Capslock::default(), last_modifiers_changed_event: Modifiers::default(), + last_capslock_changed_event: Capslock::default(), event_loop: Some(event_loop), loop_handle: handle, common, @@ -961,17 +967,25 @@ impl X11Client { }; 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); } else { let focused_window_id = state.keyboard_focused_window?; state.modifiers = modifiers; state.last_modifiers_changed_event = modifiers; + state.capslock = capslock; + state.last_capslock_changed_event = capslock; drop(state); let focused_window = self.get_window(focused_window_id)?; focused_window.handle_input(PlatformInput::ModifiersChanged( - ModifiersChangedEvent { modifiers }, + ModifiersChangedEvent { + modifiers, + capslock, + }, )); } diff --git a/crates/gpui/src/platform/linux/x11/window.rs b/crates/gpui/src/platform/linux/x11/window.rs index 1e3b6c3c72..7eedf5d4e6 100644 --- a/crates/gpui/src/platform/linux/x11/window.rs +++ b/crates/gpui/src/platform/linux/x11/window.rs @@ -1215,6 +1215,17 @@ impl PlatformWindow for X11Window { .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) { self.0.state.borrow_mut().input_handler = Some(input_handler); } diff --git a/crates/gpui/src/platform/mac/events.rs b/crates/gpui/src/platform/mac/events.rs index 32ec4b89ab..0dc361b9dc 100644 --- a/crates/gpui/src/platform/mac/events.rs +++ b/crates/gpui/src/platform/mac/events.rs @@ -1,5 +1,5 @@ use crate::{ - KeyDownEvent, KeyUpEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton, + Capslock, KeyDownEvent, KeyUpEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseExitEvent, MouseMoveEvent, MouseUpEvent, NavigationDirection, Pixels, PlatformInput, ScrollDelta, ScrollWheelEvent, TouchPhase, platform::mac::{ @@ -121,6 +121,11 @@ impl PlatformInput { NSEventType::NSFlagsChanged => { Some(Self::ModifiersChanged(ModifiersChangedEvent { modifiers: read_modifiers(native_event), + capslock: Capslock { + on: native_event + .modifierFlags() + .contains(NSEventModifierFlags::NSAlphaShiftKeyMask), + }, })) } NSEventType::NSKeyDown => Some(Self::KeyDown(KeyDownEvent { diff --git a/crates/gpui/src/platform/mac/window.rs b/crates/gpui/src/platform/mac/window.rs index 3c6f12b779..82a43eb760 100644 --- a/crates/gpui/src/platform/mac/window.rs +++ b/crates/gpui/src/platform/mac/window.rs @@ -1,11 +1,11 @@ use super::{BoolExt, MacDisplay, NSRange, NSStringExt, ns_string, renderer}; use crate::{ - AnyWindowHandle, Bounds, DisplayLink, ExternalPaths, FileDropEvent, ForegroundExecutor, - KeyDownEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, - MouseMoveEvent, MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, - PlatformWindow, Point, PromptButton, PromptLevel, RequestFrameOptions, ScaledPixels, Size, - Timer, WindowAppearance, WindowBackgroundAppearance, WindowBounds, WindowControlArea, - WindowKind, WindowParams, platform::PlatformInputHandler, point, px, size, + AnyWindowHandle, Bounds, Capslock, DisplayLink, ExternalPaths, FileDropEvent, + ForegroundExecutor, KeyDownEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton, + MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay, + PlatformInput, PlatformWindow, Point, PromptButton, PromptLevel, RequestFrameOptions, + ScaledPixels, Size, Timer, WindowAppearance, WindowBackgroundAppearance, WindowBounds, + WindowControlArea, WindowKind, WindowParams, platform::PlatformInputHandler, point, px, size, }; use block::ConcreteBlock; 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) { 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; } - PlatformInput::ModifiersChanged(ModifiersChangedEvent { modifiers }) => { + PlatformInput::ModifiersChanged(ModifiersChangedEvent { + modifiers, + capslock, + }) => { // Only raise modifiers changed event when they have actually changed if let Some(PlatformInput::ModifiersChanged(ModifiersChangedEvent { modifiers: prev_modifiers, + capslock: prev_capslock, })) = &lock.previous_modifiers_changed_event { - if prev_modifiers == modifiers { + if prev_modifiers == modifiers && prev_capslock == capslock { return; } } diff --git a/crates/gpui/src/platform/test/window.rs b/crates/gpui/src/platform/test/window.rs index d29fcca882..1b88415d3b 100644 --- a/crates/gpui/src/platform/test/window.rs +++ b/crates/gpui/src/platform/test/window.rs @@ -153,6 +153,10 @@ impl PlatformWindow for TestWindow { crate::Modifiers::default() } + fn capslock(&self) -> crate::Capslock { + crate::Capslock::default() + } + fn set_input_handler(&mut self, input_handler: PlatformInputHandler) { self.0.lock().input_handler = Some(input_handler); } diff --git a/crates/gpui/src/platform/windows/events.rs b/crates/gpui/src/platform/windows/events.rs index 2f6d8a8aad..5075997f77 100644 --- a/crates/gpui/src/platform/windows/events.rs +++ b/crates/gpui/src/platform/windows/events.rs @@ -1227,6 +1227,7 @@ where { let virtual_key = VIRTUAL_KEY(wparam.loword()); let mut modifiers = current_modifiers(); + let capslock = current_capslock(); match virtual_key { VK_SHIFT | VK_CONTROL | VK_MENU | VK_LWIN | VK_RWIN => { @@ -1239,6 +1240,7 @@ where state.last_reported_modifiers = Some(modifiers); Some(PlatformInput::ModifiersChanged(ModifiersChangedEvent { modifiers, + capslock, })) } 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( handle: HWND, is_maximized: bool, diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index d79edd6783..b682b88e72 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -559,6 +559,10 @@ impl PlatformWindow for WindowsWindow { current_modifiers() } + fn capslock(&self) -> Capslock { + current_capslock() + } + fn set_input_handler(&mut self, input_handler: PlatformInputHandler) { self.0.state.borrow_mut().input_handler = Some(input_handler); } diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index 924692ea91..c87a65573f 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -2,15 +2,15 @@ use crate::Inspector; use crate::{ Action, AnyDrag, AnyElement, AnyImageCache, AnyTooltip, AnyView, App, AppContext, Arena, Asset, - AsyncWindowContext, AvailableSpace, Background, BorderStyle, Bounds, BoxShadow, Context, - Corners, CursorStyle, Decorations, DevicePixels, DispatchActionListener, DispatchNodeId, - DispatchTree, DisplayId, Edges, Effect, Entity, EntityId, EventEmitter, FileDropEvent, FontId, - Global, GlobalElementId, GlyphId, GpuSpecs, Hsla, InputHandler, IsZero, KeyBinding, KeyContext, - KeyDownEvent, KeyEvent, Keystroke, KeystrokeEvent, LayoutId, LineLayoutIndex, Modifiers, - ModifiersChangedEvent, MonochromeSprite, MouseButton, MouseEvent, MouseMoveEvent, MouseUpEvent, - Path, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, - PlatformWindow, Point, PolychromeSprite, PromptButton, PromptLevel, Quad, Render, - RenderGlyphParams, RenderImage, RenderImageParams, RenderSvgParams, Replay, ResizeEdge, + AsyncWindowContext, AvailableSpace, Background, BorderStyle, Bounds, BoxShadow, Capslock, + Context, Corners, CursorStyle, Decorations, DevicePixels, DispatchActionListener, + DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity, EntityId, EventEmitter, + FileDropEvent, FontId, Global, GlobalElementId, GlyphId, GpuSpecs, Hsla, InputHandler, IsZero, + KeyBinding, KeyContext, KeyDownEvent, KeyEvent, Keystroke, KeystrokeEvent, LayoutId, + LineLayoutIndex, Modifiers, ModifiersChangedEvent, MonochromeSprite, MouseButton, MouseEvent, + MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, + PlatformInputHandler, PlatformWindow, Point, PolychromeSprite, PromptButton, PromptLevel, Quad, + Render, RenderGlyphParams, RenderImage, RenderImageParams, RenderSvgParams, Replay, ResizeEdge, SMOOTH_SVG_SCALE_FACTOR, SUBPIXEL_VARIANTS, ScaledPixels, Scene, Shadow, SharedString, Size, StrikethroughStyle, Style, SubscriberSet, Subscription, TaffyLayoutEngine, Task, TextStyle, TextStyleRefinement, TransformationMatrix, Underline, UnderlineStyle, WindowAppearance, @@ -796,6 +796,7 @@ pub struct Window { mouse_position: Point, mouse_hit_test: HitTest, modifiers: Modifiers, + capslock: Capslock, scale_factor: f32, pub(crate) bounds_observers: SubscriberSet<(), AnyObserver>, appearance: WindowAppearance, @@ -907,6 +908,7 @@ impl Window { let sprite_atlas = platform_window.sprite_atlas(); let mouse_position = platform_window.mouse_position(); let modifiers = platform_window.modifiers(); + let capslock = platform_window.capslock(); let content_size = platform_window.content_size(); let scale_factor = platform_window.scale_factor(); let appearance = platform_window.appearance(); @@ -1015,6 +1017,7 @@ impl Window { .update(&mut cx, |_, window, cx| { window.active.set(active); window.modifiers = window.platform_window.modifiers(); + window.capslock = window.platform_window.capslock(); window .activation_observers .clone() @@ -1100,6 +1103,7 @@ impl Window { mouse_position, mouse_hit_test: HitTest::default(), modifiers, + capslock, scale_factor, bounds_observers: SubscriberSet::new(), appearance, @@ -1728,6 +1732,11 @@ impl Window { self.modifiers } + /// The current state of the keyboard's capslock + pub fn capslock(&self) -> Capslock { + self.capslock + } + fn complete_frame(&self) { self.platform_window.completed_frame(); } @@ -3352,6 +3361,7 @@ impl Window { } PlatformInput::ModifiersChanged(modifiers_changed) => { self.modifiers = modifiers_changed.modifiers; + self.capslock = modifiers_changed.capslock; PlatformInput::ModifiersChanged(modifiers_changed) } PlatformInput::ScrollWheel(scroll_wheel) => { diff --git a/crates/recent_projects/src/ssh_connections.rs b/crates/recent_projects/src/ssh_connections.rs index f48ff43b7f..070d8dc4e3 100644 --- a/crates/recent_projects/src/ssh_connections.rs +++ b/crates/recent_projects/src/ssh_connections.rs @@ -289,6 +289,9 @@ impl Render for SshPrompt { .child(MarkdownElement::new(prompt.0.clone(), markdown_style)) .child(self.editor.clone()), ) + .when(window.capslock().on, |el| { + el.child(Label::new("⚠️ ⇪ is on")) + }) }) } }