History

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 ---------

Maxim Zaks committed 1 year ago

Commit 90aa99b1 parent e47c48f16 files · +146 −31Browse files

Unified · Split

16 files
  1. crates/gpui/src/app/test_context.rs +14 −3
  2. crates/gpui/src/interactive.rs +4 −2
  3. crates/gpui/src/platform.rs +1 −0
  4. crates/gpui/src/platform/keystroke.rs +8 −0
  5. crates/gpui/src/platform/linux/platform.rs +8 −0
  6. crates/gpui/src/platform/linux/wayland/client.rs +6 −1
  7. crates/gpui/src/platform/linux/wayland/window.rs +12 −5
  8. crates/gpui/src/platform/linux/x11/client.rs +16 −2
  9. crates/gpui/src/platform/linux/x11/window.rs +11 −0
  10. crates/gpui/src/platform/mac/events.rs +6 −1
  11. crates/gpui/src/platform/mac/window.rs +22 −8
  12. crates/gpui/src/platform/test/window.rs +4 −0
  13. crates/gpui/src/platform/windows/events.rs +8 −0
  14. crates/gpui/src/platform/windows/window.rs +4 −0
  15. crates/gpui/src/window.rs +19 −9
  16. crates/recent_projects/src/ssh_connections.rs +3 −0
crates/gpui/src/app/test_context.rs+14−3
@@ -1,7 +1,7 @@
11 use crate::{
22 Action, AnyView, AnyWindowHandle, App, AppCell, AppContext, AsyncApp, AvailableSpace,
3− BackgroundExecutor, BorrowAppContext, Bounds, ClipboardItem, DrawPhase, Drawable, Element,
4− Empty, EventEmitter, ForegroundExecutor, Global, InputEvent, Keystroke, Modifiers,
3+ BackgroundExecutor, BorrowAppContext, Bounds, Capslock, ClipboardItem, DrawPhase, Drawable,
4+ Element, Empty, EventEmitter, ForegroundExecutor, Global, InputEvent, Keystroke, Modifiers,
55 ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels,
66 Platform, Point, Render, Result, Size, Task, TestDispatcher, TestPlatform,
77 TestScreenCaptureSource, TestWindow, TextSystem, VisualContext, Window, WindowBounds,
@@ -771,7 +771,18 @@ impl VisualTestContext { · 763 unmodified lines
771771
772772 /// Simulate a modifiers changed event
773773 pub fn simulate_modifiers_change(&mut self, modifiers: Modifiers) {
774− self.simulate_event(ModifiersChangedEvent { modifiers })
774+ self.simulate_event(ModifiersChangedEvent {
775+ modifiers,
776+ capslock: Capslock { on: false },
777+ })
778+ }
779+
780+ /// Simulate a capslock changed event
781+ pub fn simulate_capslock_change(&mut self, on: bool) {
782+ self.simulate_event(ModifiersChangedEvent {
783+ modifiers: Modifiers::none(),
784+ capslock: Capslock { on },
785+ })
775786 }
776787
777788 /// Simulates the user resizing the window to the new size.
crates/gpui/src/interactive.rs+4−2
@@ -1,6 +1,6 @@
11 use crate::{
2− Context, Empty, IntoElement, Keystroke, Modifiers, Pixels, Point, Render, Window, point,
3− seal::Sealed,
2+ Capslock, Context, Empty, IntoElement, Keystroke, Modifiers, Pixels, Point, Render, Window,
3+ point, seal::Sealed,
44 };
55 use smallvec::SmallVec;
66 use std::{any::Any, fmt::Debug, ops::Deref, path::PathBuf};
@@ -55,6 +55,8 @@ impl KeyEvent for KeyUpEvent {} · 48 unmodified lines
5555 pub struct ModifiersChangedEvent {
5656 /// The new state of the modifier keys
5757 pub modifiers: Modifiers,
58+ /// The new state of the capslock key
59+ pub capslock: Capslock,
5860 }
5961
6062 impl Sealed for ModifiersChangedEvent {}
crates/gpui/src/platform.rs+1−0
@@ -415,6 +415,7 @@ pub(crate) trait PlatformWindow: HasWindowHandle + HasDisplayHandle { · 414 unmodified lines
415415 fn display(&self) -> Option<Rc<dyn PlatformDisplay>>;
416416 fn mouse_position(&self) -> Point<Pixels>;
417417 fn modifiers(&self) -> Modifiers;
418+ fn capslock(&self) -> Capslock;
418419 fn set_input_handler(&mut self, input_handler: PlatformInputHandler);
419420 fn take_input_handler(&mut self) -> Option<PlatformInputHandler>;
420421 fn prompt(
crates/gpui/src/platform/keystroke.rs+8−0
@@ -538,3 +538,11 @@ impl Modifiers { · 537 unmodified lines
538538 && (other.function || !self.function)
539539 }
540540 }
541+
542+/// The state of the capslock key at some point in time
543+#[derive(Copy, Clone, Debug, Eq, PartialEq, Default, Serialize, Deserialize, Hash, JsonSchema)]
544+pub struct Capslock {
545+ /// The capslock key is on
546+ #[serde(default)]
547+ pub on: bool,
548+}
crates/gpui/src/platform/linux/platform.rs+8−0
@@ -873,6 +873,14 @@ impl crate::Modifiers { · 872 unmodified lines
873873 }
874874 }
875875
876+#[cfg(any(feature = "wayland", feature = "x11"))]
877+impl crate::Capslock {
878+ pub(super) fn from_xkb(keymap_state: &State) -> Self {
879+ let on = keymap_state.mod_name_is_active(xkb::MOD_NAME_CAPS, xkb::STATE_MODS_EFFECTIVE);
880+ Self { on }
881+ }
882+}
883+
876884 #[cfg(test)]
877885 mod tests {
878886 use super::*;
crates/gpui/src/platform/linux/wayland/client.rs+6−1
@@ -73,7 +73,7 @@ use super::{ · 72 unmodified lines
7373
7474 use crate::platform::{PlatformWindow, blade::BladeContext};
7575 use crate::{
76− AnyWindowHandle, Bounds, CursorStyle, DOUBLE_CLICK_INTERVAL, DevicePixels, DisplayId,
76+ AnyWindowHandle, Bounds, Capslock, CursorStyle, DOUBLE_CLICK_INTERVAL, DevicePixels, DisplayId,
7777 FileDropEvent, ForegroundExecutor, KeyDownEvent, KeyUpEvent, Keystroke, LinuxCommon,
7878 LinuxKeyboardLayout, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent,
7979 MouseExitEvent, MouseMoveEvent, MouseUpEvent, NavigationDirection, Pixels, PlatformDisplay,
@@ -217,6 +217,7 @@ pub(crate) struct WaylandClientState { · 137 unmodified lines
217217 click: ClickState,
218218 repeat: KeyRepeat,
219219 pub modifiers: Modifiers,
220+ pub capslock: Capslock,
220221 axis_source: AxisSource,
221222 pub mouse_location: Option<Point<Pixels>>,
222223 continuous_scroll_delta: Option<Point<Pixels>>,
@@ -595,6 +596,7 @@ impl WaylandClient { · 372 unmodified lines
595596 function: false,
596597 platform: false,
597598 },
599+ capslock: Capslock { on: false },
598600 scroll_event_received: false,
599601 axis_source: AxisSource::Wheel,
600602 mouse_location: None,
@@ -1251,9 +1253,12 @@ impl Dispatch<wl_keyboard::WlKeyboard, ()> for WaylandClientStatePtr { · 650 unmodified lines
12511253 keymap_state.serialize_layout(xkbcommon::xkb::STATE_LAYOUT_EFFECTIVE);
12521254 keymap_state.update_mask(mods_depressed, mods_latched, mods_locked, 0, 0, group);
12531255 state.modifiers = Modifiers::from_xkb(keymap_state);
1256+ let keymap_state = state.keymap_state.as_mut().unwrap();
1257+ state.capslock = Capslock::from_xkb(keymap_state);
12541258
12551259 let input = PlatformInput::ModifiersChanged(ModifiersChangedEvent {
12561260 modifiers: state.modifiers,
1261+ capslock: state.capslock,
12571262 });
12581263 drop(state);
12591264
crates/gpui/src/platform/linux/wayland/window.rs+12−5
@@ -21,11 +21,6 @@ use wayland_protocols::xdg::shell::client::xdg_surface; · 20 unmodified lines
2121 use wayland_protocols::xdg::shell::client::xdg_toplevel::{self};
2222 use wayland_protocols_plasma::blur::client::org_kde_kwin_blur;
2323
24−use crate::platform::{
25− PlatformAtlas, PlatformInputHandler, PlatformWindow,
26− blade::{BladeContext, BladeRenderer, BladeSurfaceConfig},
27− linux::wayland::{display::WaylandDisplay, serial::SerialKind},
28−};
2924 use crate::scene::Scene;
3025 use crate::{
3126 AnyWindowHandle, Bounds, Decorations, Globals, GpuSpecs, Modifiers, Output, Pixels,
@@ -34,6 +29,14 @@ use crate::{ · 2 unmodified lines
3429 WindowBackgroundAppearance, WindowBounds, WindowControlArea, WindowControls, WindowDecorations,
3530 WindowParams, px, size,
3631 };
32+use crate::{
33+ Capslock,
34+ platform::{
35+ PlatformAtlas, PlatformInputHandler, PlatformWindow,
36+ blade::{BladeContext, BladeRenderer, BladeSurfaceConfig},
37+ linux::wayland::{display::WaylandDisplay, serial::SerialKind},
38+ },
39+};
3740
3841 #[derive(Default)]
3942 pub(crate) struct Callbacks {
@@ -861,6 +864,10 @@ impl PlatformWindow for WaylandWindow { · 821 unmodified lines
861864 self.borrow().client.get_client().borrow().modifiers
862865 }
863866
867+ fn capslock(&self) -> Capslock {
868+ self.borrow().client.get_client().borrow().capslock
869+ }
870+
864871 fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
865872 self.borrow_mut().input_handler = Some(input_handler);
866873 }
crates/gpui/src/platform/linux/x11/client.rs+16−2
@@ -1,3 +1,4 @@
1+use crate::Capslock;
12 use core::str;
23 use std::{
34 cell::RefCell,
@@ -203,8 +204,11 @@ pub struct X11ClientState { · 199 unmodified lines
203204 pub(crate) ximc: Option<X11rbClient<Rc<XCBConnection>>>,
204205 pub(crate) xim_handler: Option<XimHandler>,
205206 pub modifiers: Modifiers,
207+ pub capslock: Capslock,
206208 // TODO: Can the other updates to `modifiers` be removed so that this is unnecessary?
209+ // capslock logic was done analog to modifiers
207210 pub last_modifiers_changed_event: Modifiers,
211+ pub last_capslock_changed_event: Capslock,
208212
209213 pub(crate) compose_state: Option<xkbc::compose::State>,
210214 pub(crate) pre_edit_text: Option<String>,
@@ -473,7 +477,9 @@ impl X11Client { · 262 unmodified lines
473477
474478 X11Client(Rc::new(RefCell::new(X11ClientState {
475479 modifiers: Modifiers::default(),
480+ capslock: Capslock::default(),
476481 last_modifiers_changed_event: Modifiers::default(),
482+ last_capslock_changed_event: Capslock::default(),
477483 event_loop: Some(event_loop),
478484 loop_handle: handle,
479485 common,
@@ -961,17 +967,25 @@ impl X11Client { · 481 unmodified lines
961967 };
962968
963969 let modifiers = Modifiers::from_xkb(&state.xkb);
964− if state.last_modifiers_changed_event == modifiers {
970+ let capslock = Capslock::from_xkb(&state.xkb);
971+ if state.last_modifiers_changed_event == modifiers
972+ && state.last_capslock_changed_event == capslock
973+ {
965974 drop(state);
966975 } else {
967976 let focused_window_id = state.keyboard_focused_window?;
968977 state.modifiers = modifiers;
969978 state.last_modifiers_changed_event = modifiers;
979+ state.capslock = capslock;
980+ state.last_capslock_changed_event = capslock;
970981 drop(state);
971982
972983 let focused_window = self.get_window(focused_window_id)?;
973984 focused_window.handle_input(PlatformInput::ModifiersChanged(
974− ModifiersChangedEvent { modifiers },
985+ ModifiersChangedEvent {
986+ modifiers,
987+ capslock,
988+ },
975989 ));
976990 }
977991
crates/gpui/src/platform/linux/x11/window.rs+11−0
@@ -1215,6 +1215,17 @@ impl PlatformWindow for X11Window { · 1214 unmodified lines
12151215 .unwrap_or_default()
12161216 }
12171217
1218+ fn capslock(&self) -> crate::Capslock {
1219+ self.0
1220+ .state
1221+ .borrow()
1222+ .client
1223+ .0
1224+ .upgrade()
1225+ .map(|ref_cell| ref_cell.borrow().capslock)
1226+ .unwrap_or_default()
1227+ }
1228+
12181229 fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
12191230 self.0.state.borrow_mut().input_handler = Some(input_handler);
12201231 }
crates/gpui/src/platform/mac/events.rs+6−1
@@ -1,5 +1,5 @@
11 use crate::{
2− KeyDownEvent, KeyUpEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton,
2+ Capslock, KeyDownEvent, KeyUpEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton,
33 MouseDownEvent, MouseExitEvent, MouseMoveEvent, MouseUpEvent, NavigationDirection, Pixels,
44 PlatformInput, ScrollDelta, ScrollWheelEvent, TouchPhase,
55 platform::mac::{
@@ -121,6 +121,11 @@ impl PlatformInput { · 115 unmodified lines
121121 NSEventType::NSFlagsChanged => {
122122 Some(Self::ModifiersChanged(ModifiersChangedEvent {
123123 modifiers: read_modifiers(native_event),
124+ capslock: Capslock {
125+ on: native_event
126+ .modifierFlags()
127+ .contains(NSEventModifierFlags::NSAlphaShiftKeyMask),
128+ },
124129 }))
125130 }
126131 NSEventType::NSKeyDown => Some(Self::KeyDown(KeyDownEvent {
crates/gpui/src/platform/mac/window.rs+22−8
@@ -1,11 +1,11 @@
11 use super::{BoolExt, MacDisplay, NSRange, NSStringExt, ns_string, renderer};
22 use crate::{
3− AnyWindowHandle, Bounds, DisplayLink, ExternalPaths, FileDropEvent, ForegroundExecutor,
4− KeyDownEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent,
5− MouseMoveEvent, MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput,
6− PlatformWindow, Point, PromptButton, PromptLevel, RequestFrameOptions, ScaledPixels, Size,
7− Timer, WindowAppearance, WindowBackgroundAppearance, WindowBounds, WindowControlArea,
8− WindowKind, WindowParams, platform::PlatformInputHandler, point, px, size,
3+ AnyWindowHandle, Bounds, Capslock, DisplayLink, ExternalPaths, FileDropEvent,
4+ ForegroundExecutor, KeyDownEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton,
5+ MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay,
6+ PlatformInput, PlatformWindow, Point, PromptButton, PromptLevel, RequestFrameOptions,
7+ ScaledPixels, Size, Timer, WindowAppearance, WindowBackgroundAppearance, WindowBounds,
8+ WindowControlArea, WindowKind, WindowParams, platform::PlatformInputHandler, point, px, size,
99 };
1010 use block::ConcreteBlock;
1111 use cocoa::{
@@ -890,6 +890,16 @@ impl PlatformWindow for MacWindow { · 878 unmodified lines
890890 }
891891 }
892892
893+ fn capslock(&self) -> Capslock {
894+ unsafe {
895+ let modifiers: NSEventModifierFlags = msg_send![class!(NSEvent), modifierFlags];
896+
897+ Capslock {
898+ on: modifiers.contains(NSEventModifierFlags::NSAlphaShiftKeyMask),
899+ }
900+ }
901+ }
902+
893903 fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
894904 self.0.as_ref().lock().input_handler = Some(input_handler);
895905 }
@@ -1556,13 +1566,17 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) { · 660 unmodified lines
15561566 lock.synthetic_drag_counter += 1;
15571567 }
15581568
1559− PlatformInput::ModifiersChanged(ModifiersChangedEvent { modifiers }) => {
1569+ PlatformInput::ModifiersChanged(ModifiersChangedEvent {
1570+ modifiers,
1571+ capslock,
1572+ }) => {
15601573 // Only raise modifiers changed event when they have actually changed
15611574 if let Some(PlatformInput::ModifiersChanged(ModifiersChangedEvent {
15621575 modifiers: prev_modifiers,
1576+ capslock: prev_capslock,
15631577 })) = &lock.previous_modifiers_changed_event
15641578 {
1565− if prev_modifiers == modifiers {
1579+ if prev_modifiers == modifiers && prev_capslock == capslock {
15661580 return;
15671581 }
15681582 }
crates/gpui/src/platform/test/window.rs+4−0
@@ -153,6 +153,10 @@ impl PlatformWindow for TestWindow { · 152 unmodified lines
153153 crate::Modifiers::default()
154154 }
155155
156+ fn capslock(&self) -> crate::Capslock {
157+ crate::Capslock::default()
158+ }
159+
156160 fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
157161 self.0.lock().input_handler = Some(input_handler);
158162 }
crates/gpui/src/platform/windows/events.rs+8−0
@@ -1227,6 +1227,7 @@ where · 1226 unmodified lines
12271227 {
12281228 let virtual_key = VIRTUAL_KEY(wparam.loword());
12291229 let mut modifiers = current_modifiers();
1230+ let capslock = current_capslock();
12301231
12311232 match virtual_key {
12321233 VK_SHIFT | VK_CONTROL | VK_MENU | VK_LWIN | VK_RWIN => {
@@ -1239,6 +1240,7 @@ where · 6 unmodified lines
12391240 state.last_reported_modifiers = Some(modifiers);
12401241 Some(PlatformInput::ModifiersChanged(ModifiersChangedEvent {
12411242 modifiers,
1243+ capslock,
12421244 }))
12431245 }
12441246 vkey => {
@@ -1371,6 +1373,12 @@ pub(crate) fn current_modifiers() -> Modifiers { · 126 unmodified lines
13711373 }
13721374 }
13731375
1376+#[inline]
1377+pub(crate) fn current_capslock() -> Capslock {
1378+ let on = unsafe { GetKeyState(VK_CAPITAL.0 as i32) & 1 } > 0;
1379+ Capslock { on: on }
1380+}
1381+
13741382 fn get_client_area_insets(
13751383 handle: HWND,
13761384 is_maximized: bool,
crates/gpui/src/platform/windows/window.rs+4−0
@@ -559,6 +559,10 @@ impl PlatformWindow for WindowsWindow { · 558 unmodified lines
559559 current_modifiers()
560560 }
561561
562+ fn capslock(&self) -> Capslock {
563+ current_capslock()
564+ }
565+
562566 fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
563567 self.0.state.borrow_mut().input_handler = Some(input_handler);
564568 }
crates/gpui/src/window.rs+19−9
@@ -2,15 +2,15 @@ · 1 unmodified line
22 use crate::Inspector;
33 use crate::{
44 Action, AnyDrag, AnyElement, AnyImageCache, AnyTooltip, AnyView, App, AppContext, Arena, Asset,
5− AsyncWindowContext, AvailableSpace, Background, BorderStyle, Bounds, BoxShadow, Context,
6− Corners, CursorStyle, Decorations, DevicePixels, DispatchActionListener, DispatchNodeId,
7− DispatchTree, DisplayId, Edges, Effect, Entity, EntityId, EventEmitter, FileDropEvent, FontId,
8− Global, GlobalElementId, GlyphId, GpuSpecs, Hsla, InputHandler, IsZero, KeyBinding, KeyContext,
9− KeyDownEvent, KeyEvent, Keystroke, KeystrokeEvent, LayoutId, LineLayoutIndex, Modifiers,
10− ModifiersChangedEvent, MonochromeSprite, MouseButton, MouseEvent, MouseMoveEvent, MouseUpEvent,
11− Path, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler,
12− PlatformWindow, Point, PolychromeSprite, PromptButton, PromptLevel, Quad, Render,
13− RenderGlyphParams, RenderImage, RenderImageParams, RenderSvgParams, Replay, ResizeEdge,
5+ AsyncWindowContext, AvailableSpace, Background, BorderStyle, Bounds, BoxShadow, Capslock,
6+ Context, Corners, CursorStyle, Decorations, DevicePixels, DispatchActionListener,
7+ DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity, EntityId, EventEmitter,
8+ FileDropEvent, FontId, Global, GlobalElementId, GlyphId, GpuSpecs, Hsla, InputHandler, IsZero,
9+ KeyBinding, KeyContext, KeyDownEvent, KeyEvent, Keystroke, KeystrokeEvent, LayoutId,
10+ LineLayoutIndex, Modifiers, ModifiersChangedEvent, MonochromeSprite, MouseButton, MouseEvent,
11+ MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput,
12+ PlatformInputHandler, PlatformWindow, Point, PolychromeSprite, PromptButton, PromptLevel, Quad,
13+ Render, RenderGlyphParams, RenderImage, RenderImageParams, RenderSvgParams, Replay, ResizeEdge,
1414 SMOOTH_SVG_SCALE_FACTOR, SUBPIXEL_VARIANTS, ScaledPixels, Scene, Shadow, SharedString, Size,
1515 StrikethroughStyle, Style, SubscriberSet, Subscription, TaffyLayoutEngine, Task, TextStyle,
1616 TextStyleRefinement, TransformationMatrix, Underline, UnderlineStyle, WindowAppearance,
@@ -796,6 +796,7 @@ pub struct Window { · 779 unmodified lines
796796 mouse_position: Point<Pixels>,
797797 mouse_hit_test: HitTest,
798798 modifiers: Modifiers,
799+ capslock: Capslock,
799800 scale_factor: f32,
800801 pub(crate) bounds_observers: SubscriberSet<(), AnyObserver>,
801802 appearance: WindowAppearance,
@@ -907,6 +908,7 @@ impl Window { · 105 unmodified lines
907908 let sprite_atlas = platform_window.sprite_atlas();
908909 let mouse_position = platform_window.mouse_position();
909910 let modifiers = platform_window.modifiers();
911+ let capslock = platform_window.capslock();
910912 let content_size = platform_window.content_size();
911913 let scale_factor = platform_window.scale_factor();
912914 let appearance = platform_window.appearance();
@@ -1015,6 +1017,7 @@ impl Window { · 102 unmodified lines
10151017 .update(&mut cx, |_, window, cx| {
10161018 window.active.set(active);
10171019 window.modifiers = window.platform_window.modifiers();
1020+ window.capslock = window.platform_window.capslock();
10181021 window
10191022 .activation_observers
10201023 .clone()
@@ -1100,6 +1103,7 @@ impl Window { · 79 unmodified lines
11001103 mouse_position,
11011104 mouse_hit_test: HitTest::default(),
11021105 modifiers,
1106+ capslock,
11031107 scale_factor,
11041108 bounds_observers: SubscriberSet::new(),
11051109 appearance,
@@ -1728,6 +1732,11 @@ impl Window { · 622 unmodified lines
17281732 self.modifiers
17291733 }
17301734
1735+ /// The current state of the keyboard's capslock
1736+ pub fn capslock(&self) -> Capslock {
1737+ self.capslock
1738+ }
1739+
17311740 fn complete_frame(&self) {
17321741 self.platform_window.completed_frame();
17331742 }
@@ -3352,6 +3361,7 @@ impl Window { · 1618 unmodified lines
33523361 }
33533362 PlatformInput::ModifiersChanged(modifiers_changed) => {
33543363 self.modifiers = modifiers_changed.modifiers;
3364+ self.capslock = modifiers_changed.capslock;
33553365 PlatformInput::ModifiersChanged(modifiers_changed)
33563366 }
33573367 PlatformInput::ScrollWheel(scroll_wheel) => {
crates/recent_projects/src/ssh_connections.rs+3−0
@@ -289,6 +289,9 @@ impl Render for SshPrompt { · 288 unmodified lines
289289 .child(MarkdownElement::new(prompt.0.clone(), markdown_style))
290290 .child(self.editor.clone()),
291291 )
292+ .when(window.capslock().on, |el| {
293+ el.child(Label::new("⚠️ ⇪ is on"))
294+ })
292295 })
293296 }
294297 }