# TUI Pages TODO ## Give form editors a dedicated page-level focus target ### Current change Dynamic form-field visibility belongs to `tui-canvas`. A `FormEditor` knows its stable logical field indexes and decides which fields are currently visible and navigable through `DataProvider::field_visibility`. The phone-number example previously duplicated that state in `tui-pages`: ```rust PageFocusBuilder::new() .canvas_field_indices(state.editor.navigable_fields()) ``` This rebuilt a second per-field list every time the page specification was refreshed. When the phone extension appeared or disappeared, the page focus targets changed. `TuiPages::sync_focus_to_spec` consequently called `FocusManager::register_page`, which resets page-level focus state. Besides the repeated visibility scan and allocation, this reconciliation caused observable input lag and made focus behavior depend on two independently maintained lists. The immediate fix replaces that dynamic list with one stable page-level target: ```rust PageFocusBuilder::new().canvas_form_editor() ``` Internal field navigation and visibility are now handled only by `tui-canvas`. `tui-pages` only needs to know whether focus is inside the form or outside it. ### Concern with the immediate API `canvas_form_editor()` currently represents the complete form using `FocusTarget::CanvasField(0)`. The zero is a marker, not the editor's current field. This is correct for the current example because it contains one form editor and the form-editor key hook only checks `FocusTarget::is_canvas()`. However, overloading `CanvasField(0)` is ambiguous and should not become the finished public design: - Two form editors on the same page cannot be identified independently. - A form editor and a standalone Canvas widget using field index `0` have the same page-level identity. - Every form-editor hook currently accepts any Canvas focus target, so multiple registered form-editor hooks could compete for the same key event. - Application code inspecting `FocusTarget::CanvasField(index)` may reasonably assume that `index` is a real field index. For a form editor it is now only a sentinel. - Directly setting page focus cannot express which form editor should receive focus. - Future focus restoration cannot reliably distinguish multiple editors. ### Recommended design Add a dedicated focus-target variant carrying the form-editor ID: ```rust pub enum FocusTarget { CanvasFormEditor(usize), CanvasField(usize), InternalCanvasField(usize), // ... } ``` Change the builder API to require the same ID used when registering the Canvas hook: ```rust PageFocusBuilder::new().canvas_form_editor(0) ``` The responsibilities would then be explicit: - `CanvasFormEditor(id)` identifies one complete form at the page level. - `CanvasField(index)` remains for independently focusable Canvas widgets. - `InternalCanvasField(index)` retains its existing internal/non-top-level meaning where it is still needed. - `FormEditor` remains the sole owner of its current field, internal navigation, hidden fields, and computed/non-navigable fields. - `tui-pages` owns navigation between the form and other page components such as buttons, sections, and other form editors. The form-editor key hook must match its registered editor ID instead of merely checking `FocusTarget::is_canvas()`: ```rust matches!( ctx.focus.as_ref(), Some(FocusTarget::CanvasFormEditor(focused_id)) if focused_id == id ) ``` Cursor behavior and input-layer selection should use the same ID-aware check. `FocusTarget::is_canvas()` should include the new variant so generic canvas boundary behavior continues to work. ### Migration work 1. Add `FocusTarget::CanvasFormEditor(usize)`. 2. Change `PageFocusBuilder::canvas_form_editor()` to `canvas_form_editor(editor_id)`. 3. Update form-editor key dispatch, paste dispatch, cursor behavior, and input context selection to require the matching editor ID. 4. Keep standalone text inputs and text areas matched through their existing `CanvasField(index)` identities. 5. Update examples to pass the same ID to the page focus builder and `TuiPagesBuilder::canvas_form_editor(id)`. 6. Search applications for per-field form focus lists and replace those lists with one `CanvasFormEditor(id)` target at the correct position among the other page targets. Do not retain `CanvasField(0)` as a compatibility sentinel unless backwards compatibility is explicitly requested. ### Required tests - One form editor keeps the same page focus target while internal field visibility changes. - Showing and hiding a dependent field does not call page focus registration or reset the current page-level target. - A page containing two form editors routes keys only to the focused editor ID. - A form editor and standalone Canvas text input can coexist without identity collisions. - Exiting the first or last navigable form field moves to the correct adjacent page component. - Returning focus to a form preserves or deliberately restores the editor's internal current field according to the chosen policy. - Paste and cursor-style routing use the same editor-ID matching as key input. ### Performance acceptance criteria - Page refresh must not enumerate a form editor's visible or navigable fields. - Changing internal field visibility must not change the page-level focus target vector. - `sync_focus_to_spec` must not call `register_page` merely because a form row appeared or disappeared. - Typing into a visibility-controlling field must not allocate a second list of form-field focus targets in `tui-pages`.