This commit is contained in:
142
todo.md
Normal file
142
todo.md
Normal file
@@ -0,0 +1,142 @@
|
||||
# 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<O = ()> {
|
||||
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`.
|
||||
Reference in New Issue
Block a user