# State, records and bindings

## State

All dialog, screen, and component `property` declarations are mutable state, including those with defaults. The application sets them during construction and afterwards through generated setters:

```qml
component PriceTab : Item {
    property bool selected
    Text {
        id: labelText
        color: selected ? theme.interactiveColor : theme.textColor
    }
    Rect { id: bar; visible: selected; size: labelText.width + 30, 6 }
}
```

```cpp
tab->setSelected(i == index);
```

**What follows a change:**
- Every element statement group that references the state,
- every group referring to an affected element (through its ID, `parent` or `place`); in this example, that includes `bar`'s width because it depends on `labelText`,
- component instance arguments (`TestNav { up: focus > 0 }` calls the instance's setter),
- `repeat` and `if` blocks whose list or condition references the state (rebuilding; see below),
- the layout (`Column`/`Row`/`Grid`) containing a changed element.

**Equal values:** A setter given the state's existing value does nothing (records and lists are compared with `==`). The application can therefore set system information every second, for example; only changed content is rebuilt.

**What does not follow:** One-time properties (`name`, `source`, `onTap`, `pressSink`, `spin`) and constants (`const`). A reevaluated group also resets its geometry; elements moved by the application itself (such as a scroll thumb) should therefore have no position in the `.aura` file.

## Named states (`state`) and `when`

A state with fixed values (like QML `states`), plus blocks that change properties while a condition holds:

```qml
component SaveButton : Rect {
    state mode: idle, busy, done           // der erste Wert ist der Start
    color: theme.mainColor

    Text { id: label; text: tr("ui_save", "Speichern") }
    Svg { id: spinner; source: "icon_spinner.svg"; visible: false; spin: 1000 }

    when mode == busy {
        opacity: 0.5                        // das Element mit dem when
        // ein Element darin, über seine ID
        label.text: tr("ui_saving", "Speichert …")
        spinner.visible: true
    }
    when root.pressed { color: theme.mainColorDark }
}
```

- **`state name: a, b, c`:** A `string` state containing one of these values. Conditions use values without quotes (`mode == busy`); a typo (`mode == bsy`) fails the build. An element can have several states.
- **In C++:** An `enum class` in the struct and a type-safe setter (`button->setMode(SaveButton::Mode::busy)`), plus the field `mode` containing the value's name. For dialogs and screens, the state appears at the end of Args, initialized to its first value (`args.view = "thanks"`). An instance sets it with `mode: busy`.
- **`when condition { … }`:** While the condition holds, its properties apply to the element itself and (as `id.name`) to elements inside it. The condition can be any expression: a state, `root.pressed`, an argument. If several `when` blocks affect the same property, the later one wins.
- **Values outside `when`:** A property changed by a `when` needs a value outside it; only `visible`, `opacity`, `scale` and `rotation` have defaults. `width`/`height` change the corresponding part of `size`. One-time properties (`place`, `name`, `onTap`, `source`, `pressSink`, `spin`) do not change.
- **Implementation:** The parser rewrites every property in a `when` as a conditional value (`label.text: mode == "busy" ? … : …`). Everything then uses normal bindings, identically in both modes; structural changes still use `if`.
- **Pressed:** `when root.pressed` or `when id.pressed` on the element itself changes its content (color, text, opacity), not its geometry (which would reset a `pressSink`).

## Animations (`animate`, `enter`)

Changing properties move smoothly to their new values rather than jumping; appearing elements can fade in:

```qml
Rect {
    id: panel
    opacity: 1
    // Eigenschaft, ms, Easing (ohne: outQuad)
    animate: opacity 200, color 150 linear
    when mode == busy { opacity: 0.5; color: theme.lightGrey }
}
if view == thanks {
    Rect {
        id: thanks
        enter: opacity 0 300        // beim Erscheinen von 0 zum Wert
        exit: opacity 0 300         // beim Abbau zu 0, dann weg
    }
}
```

- **`animate: property ms [easing], …`:** Every value change animates, whether from an application setter, a `when` or `root.pressed`. A new change during an animation starts from the current interpolated value. The initial value during construction is set directly.
- **`enter: property start ms [easing], …`:** The first value animates from the starting value, for example on an element just built by an `if`.
- **`exit: property end ms [easing], …`:** When an `if` or `repeat` removes the element, the property first animates to its ending value, then the element disappears. It does not accept taps meanwhile; the new element is underneath (crossfade). Applies to elements directly inside an `if`/`repeat`.
- **Without an explicit value:** If the element does not set `opacity`, `scale`, `rotation` or `unfold` itself, `enter:` animates to its own default value (`Popover { enter: unfold 0 300 linear }` unfolds it; `enter: opacity 0 300` fades it in).
- **`loop: property from to ms [easing] [pause ms], …`:** Oscillates indefinitely: from `from` to `to` in `ms`, back in the same time, then pauses for `pause` ms (default easing: `inOutQuad`). `loop: rotation -6 6 800` swings an arrow; `loop: scale 1 0.95 2000 pause 1000` pulses an icon. Without an animator, the value stays still. Endless animations (`loop`, `spin`) keep drawing active only while their element can be seen (visible and nontransparent all the way to the root).
- **`delay ms`** (at the end of an `animate`, `enter` or `exit` entry): Each change waits this long before starting; the value remains unchanged during the wait. This sequences steps even across multiple elements:

  ```qml
  Rect { id: sidebar; x: menu ? -width : 0; animate: x 250 inOutCubic }
  Rect { id: topNavigation; y: menu ? 60 : -60; animate: y 250 inOutCubic delay 250 }   // nach der Seitenleiste
  Scroll { id: list; y: menu ? 130 : 1060; animate: y 750 outBack delay 250 }            // fliegt herein, schwingt über
  ```
- **`when condition`** (at the end of an `animate` entry): A change animates only if the condition is true at that moment; otherwise the value is applied immediately. This makes a transition run in only one direction, and lets the application disable animation for a particular change:

  ```qml
  Rect { id: videoLayer; opacity: videoMode ? 1 : 0; animate: opacity 1000 inOutQuad when !videoMode }   // nur ausblenden
  Rect { id: sidebar; x: videoMode ? 0 : -width; animate: x 250 inOutCubic when animated }               // property bool animated
  ```
- **`via value ms [easing]`** (after duration and easing in an `animate` entry): A change first moves to `value` (in the entry's duration), then on to the new value. This lets an element overshoot its target and return: `animate: y 750 inOutCubic via 60 - 50 250 inOutCubic when animated && !videoMode`.
- **`onSettled: signal`** (any element): An element's animation (`animate:`) reached its value, even if it was applied immediately (not on the initial value). Use this for subsequent work, such as stopping a player after its video fades out.
- **Scrolling:** A `Scroll`'s `contentY` is a property like `y`: bound to state and combined with `animate: contentY 600 inOutQuint`, it scrolls the list to a section (the application sets the target). A user's swipe does not stop the animation, but a new animation stops the swipe. In C++, `aura::scrollTo(scroll, y, animator)` scrolls (duration based on distance, overshooting the top and returning).
- **Properties:** `x`, `y`, `opacity`, `scale`, `rotation`, `color` (Rect, Text), `unfold` (Popover), `contentY` (Scroll); `loop` does not support `color`. Easings: `linear`, `inOutQuad`, `inOutCubic`, `inOutQuint`, `outQuad`, `outBounce`, `outBack` and `inOutBack` (overshoot by about 10%, QML `Easing.OutBack`).
- **Without an animator** (screenshots, `aura-view --render`, pixel tests), the final value is applied immediately. In the VS Code preview, animations run with Play/Pause controls.
- **Implementation:** `animate`/`enter` attach transitions to the node during construction (`aura::transition()`); property assignments use `aura::animateTo()`/`animateColor()`. Identical in both modes.

## Previews

A preview shows a dialog, screen or component with preset values in the preview tools (VS Code, `aura-view`), similar to SwiftUI's `#Preview`:

```qml
preview "Busy" for SaveButton {
    mode: busy
}
preview "Zwei Produkte" for ShoppingCart {
    lines: [
        { description: "test-Notebook", quantity: 12,
          unitPrice: "0,29 €", linePrice: "3,48 €" },
        { description: "test-Pencil", quantity: 1,
          unitPrice: "9,99 €", linePrice: "9,99 €" }
    ]
}
```

- **Values:** The target's properties and states (state values without quotes). Values not specified by a preview are empty (`""`, `0`, `false`, no entries); components retain their defaults. Placeholders (`test-<name>`) appear only in “Standard”.
- **Size:** A component whose size follows its parent (`size: parent.width, …`, `height: parent.height`) receives its size in the preview through `size: width, height`, as on an instance. `background: color` draws a color behind it (e.g. `theme.mainColor` for white header buttons). Both apply only to component previews.
- **Literals:** Only previews accept records as `{ field: value, … }`; missing fields are empty. An `image` is a filename, as in `Image { source: … }`.
- **Validation:** `aurac` validates the target, names and state values; no code is generated. The application never sees previews.
- **In the preview tools:** An element's previews appear in the “Preview” dropdown; “Standard” shows it with placeholders. States and `bool` properties of the displayed element can be toggled live there.
- **Which components:** Data rows and cells (`CartRow`, `PriceRow`, `MenuTile`, …) and elements with variants (`BackButton`, `HeaderButton`, `SaveButton`, …) have previews with `test-` data. Frames and structural elements (`DialogFrame`, `Flyout`, `Ripple`) do not; they are visible in their dialogs.

## Records

A `record` describes application data made up of several values. A list of records replaces the loop in which the application previously built a component for each entry:

```qml
// cart_screen.aura
record CartLine {
    string description
    string discount
    int quantity
    string unitPrice
    string linePrice
}

Screen ShoppingCart {
    property CartLine[] lines
    signal edit(int index)
    signal remove(int index)

    Column {
        id: productRows
        repeat line, i in lines {
            CartRow {
                id: row
                index: i
                line: line
                onEdit: edit(i)
                onRemove: remove(i)
            }
        }
    }
}

component CartRow : Rect {
    property int index
    property CartLine line
    signal edit
    signal remove
    Text { text: line.description }
    if line.discount != "" { Text { text: line.discount } }
}
```

```cpp
std::vector<views::CartLine> lines{
    {"Notebook", "", 2, "3.00", "6.00"}
};
auto ui = views::ShoppingCart::build(frame, ctx, {lines});
ui->edit = [](double index) { /* handle the selected row */ };
```

- **Fields:** `real`, `int`, `bool`, `string`, `color`, `image`, `strings`, and previously defined records or `Datensatz[]`. An image in a record (`image preview`, `Image { image: line.preview }`) replaces the placeholder to which the application previously attached an image; if it arrives later (photos still being decoded), the application resets the list and only that row is rebuilt.
- **In C++:** A struct in the UI namespace (`views::CartLine`), with fields in source order, so it can be initialized with `{...}`.
- **Origin:** Records always come from the application. There are no record literals for defaults or `const` declarations.
- **In signals:** `signal chosen(CartLine line)` or `signal all(CartLine[] lines)` passes records and lists, received in C++ as `const CartLine&` or `const std::vector<CartLine>&`. In dynamic mode, `fromValue()` converts them back.
- **Per-entry behavior:** Records carry no functions. A component emits a signal with the index or record; the application decides what to do.


## Rebuilding `repeat` and `if`

If a `repeat` list or an `if` condition references state, changing that state rebuilds the block:

```cpp
ui->setLines(lines); // update the record list
```

- **In place:** An invisible anchor node (`objectName` `aura_block`) marks the position among siblings. New nodes are inserted immediately after it, preserving draw order and layout.
- **Repetition entries:** When the list changes, the block compares its entries by position with the previous list (records use `operator==`). Equal entries keep their nodes and bindings; only changed, added and removed entries are built or removed. Appending an entry therefore builds only that entry; changing a quantity rebuilds only its row. Comparison uses position because loop variables (`i` in `onTap: remove(i)`) are captured in entries. Inserting an entry at the beginning therefore rebuilds all subsequent entries.
- **State inside entries:** If state referenced by an `if` or `repeat` inside entries changes, the repetition rebuilds all entries. State in ordinary entry properties is simply reevaluated, as elsewhere.
- **IDs:** After the setter, struct fields (vectors, pointers, component structs) point to current nodes in list order. The application attaches anything needed by new nodes after the setter; retained entries keep their existing additions.
- **`ui->onRebuilt`:** Runs after a block is rebuilt or a `lazy` repetition builds or removes entries (not during initial construction). The application attaches additions to new nodes here if rebuilding was not triggered by its own setter.
- **Old nodes** are removed from the tree and deleted only on the next rebuild. A tap handler whose signal triggers rebuilding therefore does not run on deleted memory.
- **Bindings** for nodes in a block belong to their iteration (`aura::Binding` with a guard) and end with it; this also applies to `pressed` groups.
- **Nested blocks:** Inside a repetition, the outermost repetition is always rebuilt as a whole. It therefore also follows the state of lists and conditions inside it. An `if` outside repetitions rebuilds independently, including when nested inside another `if`.
- **Cost:** An `if` completely rebuilds its branch; a repetition rebuilds only changed entries (see above).

## Lifetime

Dependencies (`aura::Bindings`) are attached to the root node; the struct holds only a weak reference. A setter called after closing does nothing.
