git.lucas.co / cce-ui
GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git

commit2adaf63902ac7fbf1dc93735d776a4433b2fbafe
parentaa0d8327a8
authorLucas Galante <[email protected]>
date2026-08-10 19:01
KeyEvent: carry the alt modifier

The engine has tracked alt_pressed from the Wayland modifiers event all
along but never surfaced it — apps (cce-terminal's alt-as-ESC keys and
alt-carrying mouse reports) need it on the event. Adds `alt: bool` to
KeyEvent, populated on both the direct and key-repeat paths, cleared on
keyboard leave alongside ctrl/shift (that reset previously missed alt);
widget-internal synthetic events pass alt: false.

Co-Authored-By: Claude Fable 5 <[email protected]>

 src/backend/window_runner.rs        |  3 +++
 src/widget/container/scroll_box.rs  |  6 ++++++
 src/widget/container/spreadsheet.rs |  1 +
 src/widget/input/color_selector.rs  | 12 ++++++++++++
 src/widget/input/dropdown.rs        |  1 +
 src/widget/input/text_box.rs        |  7 +++++++
 src/widget/mod.rs                   |  1 +
 7 files changed, 31 insertions(+)

diff --git a/src/backend/window_runner.rs b/src/backend/window_runner.rs
index 3857147..7d52c52 100644
--- a/src/backend/window_runner.rs
+++ b/src/backend/window_runner.rs
@@ -3830,6 +3830,7 @@ impl<A: Application> KeyboardHandler for EngineState<A> {
         self.pressed_key = None;
         self.ctrl_pressed = false;
         self.shift_pressed = false;
+        self.alt_pressed = false;
         let mut rebuild = false;
         self.inner.as_mut().unwrap().handle_focus_change(false, &mut rebuild);
         if rebuild {
@@ -3952,6 +3953,7 @@ impl<A: Application> EngineState<A> {
             repeat: false,
             ctrl: self.ctrl_pressed,
             shift: self.shift_pressed,
+            alt: self.alt_pressed,
         };
 
         if state == ElementState::Pressed {
@@ -4386,6 +4388,7 @@ pub fn run<A: Application>() {
                         repeat: true,
                         ctrl: engine_state.ctrl_pressed,
                         shift: engine_state.shift_pressed,
+                        alt: engine_state.alt_pressed,
                     };
 
                     if let Some(ctx) = engine_state.inner.as_mut().unwrap().ui_context_mut() {
diff --git a/src/widget/container/scroll_box.rs b/src/widget/container/scroll_box.rs
index c2f61e8..d90f93a 100644
--- a/src/widget/container/scroll_box.rs
+++ b/src/widget/container/scroll_box.rs
@@ -509,6 +509,7 @@ mod tests {
             repeat: false,
             ctrl: false,
             shift: false,
+            alt: false,
         };
         assert!(sb.keyboard_input(&event_down, &mut ctx));
         assert_eq!(sb.scroll_y, 24.0);
@@ -521,6 +522,7 @@ mod tests {
             repeat: false,
             ctrl: false,
             shift: false,
+            alt: false,
         };
         assert!(sb.keyboard_input(&event_pgdown, &mut ctx));
         assert_eq!(sb.scroll_y, 124.0);
@@ -533,6 +535,7 @@ mod tests {
             repeat: false,
             ctrl: false,
             shift: false,
+            alt: false,
         };
         assert!(sb.keyboard_input(&event_end, &mut ctx));
         assert_eq!(sb.scroll_y, 200.0); // clamps at max_scroll = 200.0
@@ -545,6 +548,7 @@ mod tests {
             repeat: false,
             ctrl: false,
             shift: false,
+            alt: false,
         };
         assert!(sb.keyboard_input(&event_pgup, &mut ctx));
         assert_eq!(sb.scroll_y, 100.0);
@@ -557,6 +561,7 @@ mod tests {
             repeat: false,
             ctrl: false,
             shift: false,
+            alt: false,
         };
         assert!(sb.keyboard_input(&event_home, &mut ctx));
         assert_eq!(sb.scroll_y, 0.0);
@@ -577,6 +582,7 @@ mod tests {
             repeat: false,
             ctrl: false,
             shift: false,
+            alt: false,
         };
 
         // Cursor away from the box, nothing focused: keys are ignored.
diff --git a/src/widget/container/spreadsheet.rs b/src/widget/container/spreadsheet.rs
index 19ec45f..2e70f65 100644
--- a/src/widget/container/spreadsheet.rs
+++ b/src/widget/container/spreadsheet.rs
@@ -453,6 +453,7 @@ mod tests {
             repeat: false,
             ctrl: false,
             shift: false,
+            alt: false,
         };
         assert!(s.keyboard_input(&end, &mut ctx));
         let g = s.inner().geom(rect).unwrap();
diff --git a/src/widget/input/color_selector.rs b/src/widget/input/color_selector.rs
index 5428aaa..16311d6 100644
--- a/src/widget/input/color_selector.rs
+++ b/src/widget/input/color_selector.rs
@@ -662,6 +662,7 @@ mod tests {
             repeat: false,
             ctrl: false,
             shift: false,
+            alt: false,
         };
         let handled = cs.keyboard_input(&backspace_ev, &mut dummy);
         assert!(handled);
@@ -676,6 +677,7 @@ mod tests {
             repeat: false,
             ctrl: false,
             shift: false,
+            alt: false,
         };
         let handled = cs.keyboard_input(&left_ev, &mut dummy);
         assert!(handled);
@@ -695,6 +697,7 @@ mod tests {
             repeat: false,
             ctrl: false,
             shift: false,
+            alt: false,
         };
         let handled = cs.keyboard_input(&right_ev, &mut dummy);
         assert!(handled);
@@ -708,6 +711,7 @@ mod tests {
             repeat: false,
             ctrl: false,
             shift: false,
+            alt: false,
         };
         // Move cursor to index 3
         let handled = cs.keyboard_input(&left_ev, &mut dummy); // 4
@@ -729,6 +733,7 @@ mod tests {
             repeat: false,
             ctrl: false,
             shift: false,
+            alt: false,
         };
         let handled = cs.keyboard_input(&type_b_ev, &mut dummy);
         assert!(handled);
@@ -748,6 +753,7 @@ mod tests {
             repeat: false,
             ctrl: false,
             shift: false,
+            alt: false,
         };
         let handled = cs.keyboard_input(&type_c_ev, &mut dummy);
         assert!(handled);
@@ -762,6 +768,7 @@ mod tests {
             repeat: false,
             ctrl: false,
             shift: false,
+            alt: false,
         };
         // Move to index 5
         for _ in 0..4 {
@@ -779,6 +786,7 @@ mod tests {
             repeat: false,
             ctrl: false,
             shift: false,
+            alt: false,
         };
         let handled = cs.keyboard_input(&enter_ev, &mut dummy);
         assert!(handled);
@@ -807,6 +815,7 @@ mod tests {
             repeat: false,
             ctrl: false,
             shift: false,
+            alt: false,
         };
         let backspace_ev = KeyEvent {
             state: ElementState::Pressed,
@@ -815,6 +824,7 @@ mod tests {
             repeat: false,
             ctrl: false,
             shift: false,
+            alt: false,
         };
         cs.keyboard_input(&backspace_ev, &mut dummy);
         cs.keyboard_input(&backspace_ev, &mut dummy);
@@ -827,6 +837,7 @@ mod tests {
             repeat: false,
             ctrl: false,
             shift: false,
+            alt: false,
         };
         cs.keyboard_input(&type_a_ev, &mut dummy);
         cs.keyboard_input(&type_b_ev, &mut dummy);
@@ -839,6 +850,7 @@ mod tests {
             repeat: false,
             ctrl: false,
             shift: false,
+            alt: false,
         };
         cs.keyboard_input(&enter_ev, &mut dummy);
         assert!(!cs.editing);
diff --git a/src/widget/input/dropdown.rs b/src/widget/input/dropdown.rs
index 7988e0e..3a66c99 100644
--- a/src/widget/input/dropdown.rs
+++ b/src/widget/input/dropdown.rs
@@ -1350,6 +1350,7 @@ mod tests {
             repeat: false,
             ctrl: false,
             shift: false,
+            alt: false,
         };
         dd.keyboard_input(&key_up, &mut dummy);
         assert_eq!(dd.hovered_item, Some(0));
diff --git a/src/widget/input/text_box.rs b/src/widget/input/text_box.rs
index 28bf28c..65f5e25 100644
--- a/src/widget/input/text_box.rs
+++ b/src/widget/input/text_box.rs
@@ -1564,6 +1564,7 @@ mod tests {
             repeat: false,
             ctrl: false,
             shift: false,
+            alt: false,
         };
         let handled = tb.keyboard_input(&key_ev, &mut dummy);
         assert!(handled);
@@ -1578,6 +1579,7 @@ mod tests {
             repeat: false,
             ctrl: false,
             shift: false,
+            alt: false,
         };
         let handled_enter = tb.keyboard_input(&enter_ev, &mut dummy);
         assert!(handled_enter);
@@ -1606,6 +1608,7 @@ mod tests {
             repeat: false,
             ctrl: false,
             shift: false,
+            alt: false,
         };
         let handled = tb.keyboard_input(&key_ev, &mut dummy);
         assert!(handled);
@@ -1655,6 +1658,7 @@ mod tests {
             repeat: false,
             ctrl: false,
             shift: true,
+            alt: false,
         };
         let handled = tb.keyboard_input(&left_shift_ev, &mut dummy);
         assert!(handled);
@@ -1669,6 +1673,7 @@ mod tests {
             repeat: false,
             ctrl: false,
             shift: false,
+            alt: false,
         };
         let handled = tb.keyboard_input(&left_ev, &mut dummy);
         assert!(handled);
@@ -1683,6 +1688,7 @@ mod tests {
             repeat: false,
             ctrl: false,
             shift: true,
+            alt: false,
         };
         let handled = tb.keyboard_input(&up_shift_ev, &mut dummy);
         assert!(handled);
@@ -1697,6 +1703,7 @@ mod tests {
             repeat: false,
             ctrl: false,
             shift: false,
+            alt: false,
         };
         let handled = tb.keyboard_input(&rust_ev, &mut dummy);
         assert!(handled);
diff --git a/src/widget/mod.rs b/src/widget/mod.rs
index 58cf458..478b1d8 100644
--- a/src/widget/mod.rs
+++ b/src/widget/mod.rs
@@ -82,6 +82,7 @@ pub struct KeyEvent {
     pub repeat: bool,
     pub ctrl: bool,
     pub shift: bool,
+    pub alt: bool,
 }
 
 /// Text justification for widget labels/content (shared by Button, cce-files' row