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

commit1b417ae7eb9b70e8ec775b3563e44ed68303b57d
parentf9ccb5f149
authorLucas Galante <lsgalante12@gmail.com>
date2026-09-25 15:51
fix(text_box): measure the caret in the font the text is drawn in; F1-F12 keys

TextBox measured its glyph positions (caret, selection, click-to-column)
by shaping font_family, or monospace for a password, while the text is
drawn through Adapted's own-labels bridge in Paint::text_font -- the
configured control font string. Two faces, so each column was measured
a little off and the caret drifted with every character: two dots short
after twelve in the login greeter's password box. value_font() is now
the one answer, text_font(), and both prepare_text and paint use it.

NamedKey gains F1-F4 and F6-F12 beside F5, mapped from their keysyms in
the runner, for the greeter's F1 power off and F2 reboot. NamedKey is
matched exhaustively in cce-browser's and cce-mail's WPE key tables,
which gain the arms in the same change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

 src/backend/window_runner.rs | 11 +++++++++++
 src/widget/input/text_box.rs | 34 +++++++++++++++++++++++++---------
 src/widget/mod.rs            | 14 ++++++++++++++
 3 files changed, 50 insertions(+), 9 deletions(-)

diff --git a/src/backend/window_runner.rs b/src/backend/window_runner.rs
index 797d3ab..d6f6043 100644
--- a/src/backend/window_runner.rs
+++ b/src/backend/window_runner.rs
@@ -5260,7 +5260,18 @@ impl<A: Application> EngineState<A> {
             xkeysym::Keysym::Alt_L | xkeysym::Keysym::Alt_R => Key::Named(NamedKey::Alt),
             xkeysym::Keysym::Control_L | xkeysym::Keysym::Control_R => Key::Named(NamedKey::Control),
             xkeysym::Keysym::Shift_L | xkeysym::Keysym::Shift_R => Key::Named(NamedKey::Shift),
+            xkeysym::Keysym::F1 => Key::Named(NamedKey::F1),
+            xkeysym::Keysym::F2 => Key::Named(NamedKey::F2),
+            xkeysym::Keysym::F3 => Key::Named(NamedKey::F3),
+            xkeysym::Keysym::F4 => Key::Named(NamedKey::F4),
             xkeysym::Keysym::F5 => Key::Named(NamedKey::F5),
+            xkeysym::Keysym::F6 => Key::Named(NamedKey::F6),
+            xkeysym::Keysym::F7 => Key::Named(NamedKey::F7),
+            xkeysym::Keysym::F8 => Key::Named(NamedKey::F8),
+            xkeysym::Keysym::F9 => Key::Named(NamedKey::F9),
+            xkeysym::Keysym::F10 => Key::Named(NamedKey::F10),
+            xkeysym::Keysym::F11 => Key::Named(NamedKey::F11),
+            xkeysym::Keysym::F12 => Key::Named(NamedKey::F12),
             _ => {
                 // With Ctrl held, xkb's utf8 goes through the legacy control-character
                 // transformation (ctrl+j = "\n", ctrl+a = 0x01, ...); the keysym is
diff --git a/src/widget/input/text_box.rs b/src/widget/input/text_box.rs
index 72146ed..a18e8d9 100644
--- a/src/widget/input/text_box.rs
+++ b/src/widget/input/text_box.rs
@@ -221,6 +221,23 @@ impl TextBox {
         closest
     }
 
+    /// The font the value text is drawn in — AND measured in: `prepare_text`
+    /// (whose per-glyph positions place the caret, the selection and
+    /// click-to-column) shapes with exactly this, and `paint` emits it. It is
+    /// [`Paint::text_font`], because that is what actually draws the text:
+    /// `Adapted::paint_self` drops the Text prims `paint` emits and redraws
+    /// the value through its own-labels bridge in `text_font()` — the box's
+    /// family when customized (cce-text-editor's monospace editor), else the
+    /// configured control font string.
+    ///
+    /// Until 2026-09-25 the measurement shaped `font_family` instead (or
+    /// `monospace` for a password), a different face from the drawn one, so
+    /// the caret drifted off the text a little more with every character —
+    /// two dots short after twelve in the login greeter's password box.
+    pub fn value_font(&self) -> Option<String> {
+        <Self as Paint>::text_font(self)
+    }
+
     pub fn char_width(&self) -> f32 {
         if self.shaped_char_advance > 0.0 {
             self.shaped_char_advance
@@ -1385,11 +1402,9 @@ impl Paint for TextBox {
             text_src.as_str()
         };
 
-        let font_fam = if self.is_password {
-            "monospace"
-        } else {
-            self.font_family.as_str()
-        };
+        // Measured in the family the value text is DRAWN in — see `value_font`.
+        let font_fam = self.value_font();
+        let font_fam = font_fam.as_deref();
 
         let render_text = if self.is_password {
             "•".repeat(display_text.chars().count())
@@ -1397,7 +1412,7 @@ impl Paint for TextBox {
             display_text.to_string()
         };
 
-        let buffer = crate::widget::display::text_label::make_widget_text_buffer(fs, &render_text, self.font_size, font_fam);
+        let buffer = crate::backend::window_runner::get_text_buffer(fs, &render_text, self.font_size, font_fam);
 
         let char_count = render_text.chars().count();
         let mut x_offsets = vec![0.0; char_count + 1];
@@ -1406,7 +1421,7 @@ impl Paint for TextBox {
 
         // One column's advance, from the same shaping path as the labels (buffer-cached,
         // so this is a lookup after the first frame per family/size).
-        let probe = crate::widget::display::text_label::make_widget_text_buffer(fs, "MMMMMMMM", self.font_size, font_fam);
+        let probe = crate::backend::window_runner::get_text_buffer(fs, "MMMMMMMM", self.font_size, font_fam);
         self.shaped_char_advance = probe
             .layout_runs()
             .next()
@@ -1455,7 +1470,7 @@ impl Paint for TextBox {
             };
             let (lines, _) = self.wrap_text(max_chars);
             for line in &lines {
-                let line_buffer = crate::widget::display::text_label::make_widget_text_buffer(fs, line, self.font_size, font_fam);
+                let line_buffer = crate::backend::window_runner::get_text_buffer(fs, line, self.font_size, font_fam);
                 let n = line.chars().count();
                 let mut offs = vec![0.0f32; n + 1];
                 let mut line_total: f32 = 0.0;
@@ -1591,8 +1606,9 @@ impl Paint for TextBox {
             self.rect.x + self.rect.width,
             self.rect.y + self.rect.height,
         ]);
+        let font = self.value_font();
         for tl in self.value_labels() {
-            ctx.text_with(tl.text, tl.x, tl.y, tl.font_size, tl.color, None, well);
+            ctx.text_with(tl.text, tl.x, tl.y, tl.font_size, tl.color, font.clone(), well);
         }
     }
 }
diff --git a/src/widget/mod.rs b/src/widget/mod.rs
index eda59f8..09655bb 100644
--- a/src/widget/mod.rs
+++ b/src/widget/mod.rs
@@ -71,7 +71,21 @@ pub enum NamedKey {
     Shift,
     Alt,
     Super,
+    // The function keys. F5 came first (the login greeter's restart); the
+    // rest arrived together on 2026-09-25 for the greeter's F1 power off and
+    // F2 reboot.
+    F1,
+    F2,
+    F3,
+    F4,
     F5,
+    F6,
+    F7,
+    F8,
+    F9,
+    F10,
+    F11,
+    F12,
 }
 
 #[derive(Debug, Clone, PartialEq, Eq)]