zaxis 0.1.0

IDs and retained state

Stable identity for windows, changing captions, repeated controls, and dynamic lists.

Identity rules

ElementDefault identity
WindowHash of ("window", full_title)
ButtonUI scope, widget kind, and full caption
CheckboxUI scope, widget kind, and full caption
SwitchUI scope, widget kind, and full caption; optional id_source override
DragSource, DropTargetUI scope, kind and the application Id passed in; results report that Id unchanged
SliderUI scope, construction call site, and occurrence at that site; optional id_source override
Text, Separator, shapes, rowsUI scope, element kind, and allocation sequence
Ui::interact regionUI scope and the id_source you pass; Response::id is that id
FocusGroupUI scope and its id_source; the closure runs in a scope of that id, so the same captions in two groups differ

An ID identifies retained focus, pointer capture, and cached geometry. Window IDs also retain position, size, and layer order. IDs are hashes for runtime identity; do not use Id::value() as a persistent file-format identifier.

Hidden suffixes

Window, Button, and Checkbox hide everything from the first ## in the rendered caption. The complete string still contributes to the default ID.

ui.button("Delete##first");
ui.button("Delete##second");

Both buttons display Delete. Their IDs differ. A suffix does not stabilize a changing visible prefix: Run 1##run and Run 2##run still have different default IDs. Text displays ## literally.

Slider::text hides a ## suffix but derives identity from the construction call site rather than the caption. Changing its displayed value or caption preserves ID. Repeated sliders at one call site have distinct occurrence IDs; use model-key push_id scopes when inserting or reordering rows built by that same call site.

Changing captions

Use an explicit identity when the visible text changes:

use zaxis::{Button, Id, Window};

Window::new(format!("Job {job_name}"))
    .id(Id::new("job-panel"))
    .show(context, |ui| {
        ui.add(Button::new(format!("Runs: {runs}")).id_source("run-counter"));
    });

Button::id_source, Checkbox::id_source, and Slider::id_source hash any Hash value and combine it with their UI scope. Window::id accepts a complete Id.

Repeated controls

for item in items {
    ui.push_id(item.id, |ui| {
        if ui.button("Delete").clicked() {
            pending_delete = Some(item.id);
        }
    });
}

Use a stable model key for push_id, especially if rows can be sorted or removed. An array index changes identity when preceding rows move. push_id restores the previous scope and allocation sequence after its closure; its return value is the closure's return value.

Explicit id_source still inherits the surrounding scope. Moving a control to a different window, row, or push_id scope changes its final ID.

Keys and focus groups are bound to ids

A key claim (Ui::keys) belongs to the Response::id it was made for, and the events it produces are addressed to that id: two widgets with the same claims and different scoped ids never get each other's keys, and a widget whose id changes loses the events queued under the old one. A focus group remembers the member that had focus by its id, so reordering, relabelling and rebuilding in another order keep the stop, while a member whose id changes is a new member. Give members of a group stable ids as for any retained state, and scope repeated controls with Ui::push_id. Two groups with one id in a pass are reported as an IdCollision.

Id methods

MethodResult
Id::new(value: impl Hash)Hash a new root identity
id.with(value: impl Hash)Derive a child identity from a parent
id.value() -> u64Read the underlying hash

Removal and duplicates

An omitted widget loses cached paint geometry at the end of that pass. Focus and capture are cleared when their matching interactive hit region disappears or changes kind. Key claims, queued key events and a focus group's memory of its last member belong to the pass that built them: a widget or group that is not built in a pass loses them at its end. Omitted window geometry remains in the context; reopening the same window ID restores its position and size.

Painting the same ID twice in one pass does not panic: both elements are drawn (the second under a derived ID) and the pass reports an IdCollision in Context::diagnostics(); the debug overlay frames the colliding widget. Use separate scopes or explicit IDs. This includes duplicate window titles and duplicate same-kind captions inside a single scope.

Edit on GitHub

On this page