git.lucas.co / cce-system-interface
system settings
git clone https://git.lucas.co/cce-system-interface.git

commitea96841b8aafc97a0f52189829e03fe81ff7b87c
parentb4eaf623f8
authorLucas Galante <lsgalante12@gmail.com>
date2026-10-05 21:39
fix(osk): keep claiming an edited field on frames that replay the layout

TextBoxes are painted only in rebuild_layout, and display_list replays the
cached frame otherwise, so a field claimed text input on rebuild frames
alone: the first replayed frame disabled it and the on-screen keyboard
went away. The rebuild now runs under cce_ui::text_input::capture and its
claim is made again every frame. Repins cce-ui to 2e573c3 for capture.

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

 Cargo.lock      | 37 ++++++++++++++++++++++++++++++++++++-
 Cargo.toml      |  2 +-
 src/main.rs     |  9 +++++++++
 src/renderer.rs |  7 +++++++
 4 files changed, 53 insertions(+), 2 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 1471bbd..8c85e4e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -425,7 +425,7 @@ dependencies = [
 [[package]]
 name = "cce-ui"
 version = "0.1.0"
-source = "git+https://github.com/lsgalante/cce-ui.git?rev=9afd4d8c634296572feaddc96ae7c7edd0582ce4#9afd4d8c634296572feaddc96ae7c7edd0582ce4"
+source = "git+https://github.com/lsgalante/cce-ui.git?rev=2e573c35b6826fff87ac216eba48b9cc3c5a3dac#2e573c35b6826fff87ac216eba48b9cc3c5a3dac"
 dependencies = [
  "ash",
  "bitflags 2.13.1",
@@ -433,12 +433,19 @@ dependencies = [
  "calloop",
  "calloop-wayland-source",
  "cosmic-text",
+ "cursor-icon",
+ "dispatch2",
  "glam",
  "gpu-allocator",
+ "js-sys",
  "kdl",
  "libc",
  "log",
  "naga",
+ "objc2",
+ "objc2-app-kit",
+ "objc2-foundation",
+ "objc2-quartz-core",
  "png",
  "raw-window-handle",
  "resvg",
@@ -446,10 +453,14 @@ dependencies = [
  "serde",
  "serde_json",
  "smithay-client-toolkit",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
  "wayland-backend",
  "wayland-client",
  "wayland-protocols 0.31.2",
  "wayland-scanner",
+ "web-sys",
+ "web-time",
  "xkeysym",
 ]
 
@@ -1672,7 +1683,9 @@ dependencies = [
  "bitflags 2.13.1",
  "block2",
  "objc2",
+ "objc2-core-foundation",
  "objc2-foundation",
+ "objc2-quartz-core",
 ]
 
 [[package]]
@@ -1703,6 +1716,18 @@ dependencies = [
  "objc2-core-foundation",
 ]
 
+[[package]]
+name = "objc2-quartz-core"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f"
+dependencies = [
+ "bitflags 2.13.1",
+ "objc2",
+ "objc2-core-foundation",
+ "objc2-foundation",
+]
+
 [[package]]
 name = "once_cell"
 version = "1.21.4"
@@ -3313,6 +3338,16 @@ dependencies = [
  "wasm-bindgen",
 ]
 
+[[package]]
+name = "web-time"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
 [[package]]
 name = "weezl"
 version = "0.1.12"
diff --git a/Cargo.toml b/Cargo.toml
index 3787e01..fcce812 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -5,7 +5,7 @@ edition = "2021"
 
 [dependencies]
 keyring = { version = "3", features = ["sync-secret-service"] }
-cce-ui = { git = "https://github.com/lsgalante/cce-ui.git", rev = "9afd4d8c634296572feaddc96ae7c7edd0582ce4" }
+cce-ui = { git = "https://github.com/lsgalante/cce-ui.git", rev = "2e573c35b6826fff87ac216eba48b9cc3c5a3dac" }
 smithay-client-toolkit = "0.19.2"
 calloop = "0.13.0"
 calloop-wayland-source = "0.3.0"
diff --git a/src/main.rs b/src/main.rs
index 121bd0f..9c3553d 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -110,6 +110,11 @@ struct SystemInterface {
     /// rebuild and the clone at the same rect takes the focus back (lit as it
     /// is collected, focused at the pass's tail).
     refocus_rect: Option<(f32, f32, f32, f32)>,
+    /// What the last rebuild's widgets claimed for text input (an editing
+    /// TextBox, `cce_ui::text_input::capture`). Claimed again every frame:
+    /// a frame that only replays the cached layout paints no widget, and
+    /// would otherwise read as the field closing.
+    text_claim: Option<[f32; 4]>,
     page_dropdown: cce_ui::widget::Adapted<cce_ui::widget::input::Dropdown>,
     // Switcher + Page DISSOLVED (Phase 6u): the current page is app.current_page, page
     // scroll is scroll_y/max_scroll_y, and the page scrollbar is this app-owned widget
@@ -249,6 +254,7 @@ impl cce_ui::engine::Application for SystemInterface {
             focused_section: None,
             laid_out_page: None,
             refocus_rect: None,
+            text_claim: None,
             page_dropdown,
             page_scroll_bar: crate::scroll_bar::ScrollBar::new(),
             content_h: 0.0,
@@ -582,6 +588,9 @@ impl cce_ui::engine::Application for SystemInterface {
                 *bounds,
             );
         }
+        if let Some([x, y, w, h]) = self.text_claim {
+            cce_ui::text_input::claim(x, y, w, h);
+        }
         Some(pc.finish())
     }
 
diff --git a/src/renderer.rs b/src/renderer.rs
index ed1a64a..894c584 100644
--- a/src/renderer.rs
+++ b/src/renderer.rs
@@ -90,7 +90,14 @@ fn collect_window_child(
 
 impl SystemInterface {
 
+    /// Lay the page out and flatten it into the cached frame, keeping what
+    /// its widgets claimed for text input (`text_claim`).
     pub(crate) fn rebuild_layout(&mut self, sw: f32, sh: f32) {
+        let ((), claim) = cce_ui::text_input::capture(|| self.rebuild_layout_inner(sw, sh));
+        self.text_claim = claim;
+    }
+
+    fn rebuild_layout_inner(&mut self, sw: f32, sh: f32) {
         if std::env::var("CCE_HOVER_DEBUG").is_ok() {
             let t = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_millis() % 100000;
             eprintln!("[hover] t={} rebuild", t);