system settings
git clone https://git.lucas.co/cce-system-interface.git
fix: a Tab step's focus survives the rebuild it triggers
The page's buttons are per-rebuild clones, so the focused id died with the
view pass the step's rebuild ran, and every Tab restarted at the first stop.
focus_stepped records the focused widget's rect; the view pass lights the
clone at that rect as it collects its plate, and its tail hands that clone
the focus.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
src/main.rs | 15 ++++++++++++++-
src/renderer.rs | 30 +++++++++++++++++++++++++++++-
2 files changed, 43 insertions(+), 2 deletions(-)
diff --git a/src/main.rs b/src/main.rs
index f8843a5..8b55126 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -105,6 +105,11 @@ struct SystemInterface {
// `section_widgets()` ids are not registered and every event to them is dropped with
// a router warning. `dispatch_page_event` suppresses dispatch across that gap.
laid_out_page: Option<Page>,
+ /// The rect of the widget a Tab step focused, carried across the rebuild
+ /// the step triggers: the page's buttons are per-rebuild clones, so the
+ /// focused id dies with the view pass and the clone at the same rect takes
+ /// the focus back (see `focus_stepped` / the view pass tail).
+ refocus_rect: Option<(f32, f32, f32, f32)>,
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
@@ -243,6 +248,7 @@ impl cce_ui::engine::Application for SystemInterface {
last_scroll_y: 0.0,
focused_section: None,
laid_out_page: None,
+ refocus_rect: None,
page_dropdown,
page_scroll_bar: crate::scroll_bar::ScrollBar::new(),
content_h: 0.0,
@@ -619,8 +625,15 @@ impl cce_ui::engine::Application for SystemInterface {
true
}
- /// The geometry is cached until the next rebuild — a moved focus ring needs one.
+ /// The geometry is cached until the next rebuild — a moved focus ring needs
+ /// one — and that rebuild clones the page's buttons, so remember where the
+ /// focus went and hand it to the clone at that rect after the view pass.
fn focus_stepped(&mut self) {
+ self.refocus_rect = self
+ .ui_context
+ .focused_widget
+ .and_then(|id| self.ui_context.tree.get_ptr(id))
+ .map(|ptr| unsafe { (*ptr).rect() });
self.needs_rebuild = true;
}
diff --git a/src/renderer.rs b/src/renderer.rs
index bbaf759..b94fe7a 100644
--- a/src/renderer.rs
+++ b/src/renderer.rs
@@ -504,7 +504,18 @@ impl SystemInterface {
// coords, pre-scroll, like everything else in this list.
if cce_ui::layout::control_relief() {
let rect = cce_ui::scene::layout::Rect { x: base.x, y: base.y, width: base.w, height: base.h };
- if let Some(plate) = btn.plate(rect) {
+ if let Some(mut plate) = btn.plate(rect) {
+ // This pass's clone of the button a Tab step focused (its
+ // registered rect, window coords, matches `refocus_rect`;
+ // the view-pass tail hands it the focus): light its rim
+ // now, since the carve is collected here, before that.
+ let near = |a: f32, b: f32| (a - b).abs() < 0.5;
+ let refocused = self
+ .refocus_rect
+ .is_some_and(|(fx, fy, fw, fh)| near(wx, fx) && near(wy, fy) && near(ww, fw) && near(wh, fh));
+ if refocused {
+ plate.tint = Some(cce_ui::widget::ControlPlate::focus_tint());
+ }
self.page_control_reliefs.push(ControlCarve::Plate {
x: plate.rect.x,
y: plate.rect.y,
@@ -739,6 +750,23 @@ impl SystemInterface {
self.ui_context.register_widget(id, ptr);
}
}
+ // A Tab step's focus, handed to the fresh clone at the same rect (the
+ // page buttons above are per-rebuild allocations; see `focus_stepped`).
+ if let Some((fx, fy, fw, fh)) = self.refocus_rect.take() {
+ let near = |a: f32, b: f32| (a - b).abs() < 0.5;
+ let heir = self.ui_context.tree.iter_registered().find_map(|(id, ptr)| {
+ if ptr.is_null() {
+ return None;
+ }
+ let w = unsafe { &*ptr };
+ let (x, y, ww, hh) = w.rect();
+ (w.focus_role() != cce_ui::widget::FocusRole::None && near(x, fx) && near(y, fy) && near(ww, fw) && near(hh, fh))
+ .then_some(id)
+ });
+ if let Some(id) = heir {
+ self.ui_context.set_focused_id(id);
+ }
+ }
self.needs_rebuild = false;
self.laid_out_page = Some(self.app.current_page);
self.last_scroll_y = self.scroll_y;