diff --git a/AGENTS.md b/AGENTS.md
index df3a400..82b0c80 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -62,15 +62,16 @@ my_project/
```
-### The three sigils
+### The sigils
| Prefix | Means | Example |
| --- | --- | --- |
| `$name` | An `` property of this element | `` |
| `#name` | A constant from `` or `globals.xml` | `pad="#space_md"` |
| `{ ... }` | An expression, evaluated once at creation | `hidden="{!icon}"` |
+| `@{ ... }` | The same expression as a binding: re-evaluated whenever a subject or variant in it changes | `hidden="@{subject_count == 0}"` |
-Inside `{ }` you write bare identifiers, no `$` or `#`.
+Inside `{ }` and `@{ }` you write bare identifiers, no `$` or `#`.
### `view` and `extends`
@@ -123,6 +124,12 @@ Three ways, in order of preference:
+
+
+
+
+
+
```
Prefix style names with `style_`. Selectors combine parts and states with `|`.
@@ -140,6 +147,19 @@ But constants can be used:
Pass the property to a *local* style property instead: ``.
+A `` child animates a style's properties on state changes. It animates *into* the state of the style holding it, so for both directions add one to the default style too:
+
+```xml
+
+
+```
+
+One transition per style, numeric and color properties only, and `` never animates.
+
## Data binding
Subjects are the interface between the UI and the application. Define them in `globals.xml`:
@@ -161,6 +181,10 @@ Only `int`, `string` and `float` are supported.
+
+
+
+
```
`bind_flag_*` takes a `flag`, `bind_state_*` takes a `state`. Both come in `_eq`, `_not_eq`, `_gt`, `_ge`, `_lt`, `_le`. The `lv_obj-` prefix is optional.
@@ -168,8 +192,35 @@ Only `int`, `string` and `float` are supported.
States: `default`, `checked`, `focused`, `focus_key`, `edited`, `hovered`, `pressed`, `scrolled`, `disabled`.
Common flags: `hidden`, `clickable`, `checkable`, `scrollable`, `floating`, `ignore_layout`.
+`@{ }` is `{ }` that re-runs whenever a referenced subject or variant changes. It works on **widget** attributes (including `style_*` locals) and on a component instance's **variant** attributes. Not in `` (initialized once) and not on a component's own props or slots. It must reference at least one subject or variant, and inside it only `type="subject"` props may appear; other props are an error. A failed re-evaluation (e.g. `/0`) keeps the previous value.
+
+To give each instance its own data, declare `` and pass a subject name at the call site: ``.
+
**Binding beats callbacks.** A radio group, a theme switch, or a value readout needs no C at all: write the subject with `subject_set_int_event`, read it with `bind_state_if_eq`.
+## Variants
+
+A component's named visual states, declared in ``. Per-instance and reactive, so they are the component-scoped counterpart of global subjects.
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+Read a variant with `` (preferred for anything visual) or in `@{ }`, where the variant name is the current option and an option name is a constant.
+
+Pick an option on the instance, ``, or from C with the exported `my_badge_set_size(obj, MY_BADGE_SIZE_LARGE)` (`lv_xml_set_variant(obj, "size", "large")` at runtime). An unknown option on the instance falls back to `default` with a warning; in `lv_xml_set_variant()` it's refused and the option is left unchanged. Option names must be unique across a component's variants, a variant name shadows a same-named prop/const/subject, and reordering `options` breaks already exported C.
+
## Events
All are children of a widget, all take `trigger` (`clicked`, `long_pressed`, `value_changed`, ...):
@@ -196,14 +247,16 @@ Evaluated **once at creation**, not reactive. For anything that changes at runti
```
-`.` concatenates. Strings use single quotes. There is no `&&` or `||`, comparisons cannot be chained, and ternaries cannot be nested.
+`.` concatenates. Strings use single quotes. Comparisons cannot be chained (`a < b < c`) and ternaries cannot be nested.
+
+`&&` and `||` exist, but `&` and `<` must be XML-escaped in an attribute value, so prefer the `and` / `or` keywords: `hidden="{a > 10 and a <= 30}"`. Both sides are always evaluated, there is no short-circuiting.
## Animations
```xml
-
+
@@ -212,6 +265,8 @@ Evaluated **once at creation**, not reactive. For anything that changes at runti
`target="self"` is the `view`; anything else is matched against a child's `name`. Play with ``.
+`easing` (on `` and ``) is `linear` (default), `ease_in`, `ease_out`, `ease_in_out`, `overshoot`, `bounce`, `step`, `bezier(x1 y1 x2 y2)` with `x` in `0..1`, or a callback registered with `lv_xml_register_easing_cb()`.
+
## Slots
Expose an internal object as a place where the caller can add children:
@@ -237,7 +292,8 @@ The slot target is ``, and you can set normal object p
- Inventing an attribute instead of reading `lvgl_widgets_xml/`.
- Putting `$prop` into a `
@@ -98,6 +99,46 @@ It's also possible to change the basic `lv_obj` properties on the slots, such as
```
+### Variants
+
+Besides ``s, a Component can declare **variants**: named lists of options that can be changed after creation. Unlike properties, a variant keeps its value per instance, so `` and [data bindings](./data-binding) can follow it at runtime.
+
+`badge.xml`
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+`screen1.xml`
+
+```xml
+
+
+
+
+
+
+
+```
+
+Learn more about variants on their [dedicated page](./variants).
+
### Limitations
Component APIs support only simple properties that are forwarded. The following Widget API features cannot be used for Components:
@@ -244,7 +285,7 @@ Used in a view:
-
+
```
LVGL's UI Editor generates:
@@ -270,11 +311,11 @@ Used for internal or implicit elements:
Used in a view:
```xml
-
+
-
+
```
LVGL's UI Editor generates:
@@ -301,11 +342,11 @@ Used for indexed access, like setting values in a table:
Used in a view:
```xml
-
+
-
+
```
LVGL's UI Editor generates:
@@ -330,15 +371,15 @@ Used to describe custom API functions with a custom name. Custom elements can ha
Used in a view:
```xml
-
+
-
+
```
LVGL's UI Editor generates:
```c
void my_widget_bind_color(lv_obj_t * parent, lv_subject_t * subject, lv_color_t color, int32_t ref_value);
-```
\ No newline at end of file
+```
diff --git a/docs/syntax/components.mdx b/docs/syntax/components.mdx
index c9a418a..b1d5de3 100644
--- a/docs/syntax/components.mdx
+++ b/docs/syntax/components.mdx
@@ -99,7 +99,7 @@ However, you can also define custom properties in the [`api` tag](./api). These
-
+
```
@@ -160,7 +160,7 @@ The following examples demonstrate parameter passing, styles, and constants in C
-
+
```
\ No newline at end of file
diff --git a/docs/syntax/constants.mdx b/docs/syntax/constants.mdx
index 3bff151..2af20ae 100644
--- a/docs/syntax/constants.mdx
+++ b/docs/syntax/constants.mdx
@@ -42,7 +42,7 @@ Constants can be used in:
- Widget properties
- Component properties
-Constant values are referenced using the `#` symbol followed by the constant name. For example:
+A constant is referenced by its name in `{ }`, where it can also be part of a larger [expression](./evaluate). This works in the `view` and in the ``, ``, ``, `` and `` sections alike:
```xml
@@ -50,15 +50,20 @@ Constant values are referenced using the `#` symbol followed by the constant nam
+
+
+
-
+
-
+
-
+
-
+
-```
\ No newline at end of file
+```
+
+`#const_name` is the legacy syntax for referencing a constant. It takes a single name only, so `{ }` is the recommended form.
\ No newline at end of file
diff --git a/docs/syntax/data-binding.mdx b/docs/syntax/data-binding.mdx
index bef1184..e726d7d 100644
--- a/docs/syntax/data-binding.mdx
+++ b/docs/syntax/data-binding.mdx
@@ -6,12 +6,12 @@ faqs:
answer: "Integer, string, and float types are supported for subjects. You define them in globals.xml using , , or tags with a name and initial value."
- question: "How do I update a subject value from my C code?"
answer: "Use the generated setter functions in your application code. For a subject named 'battery_power', call set_battery_power(value) to update it. All widgets bound to that subject will automatically update."
- - question: "What's the difference between simple and complex binding?"
- answer: "Simple binding directly binds a subject to a widget property using attributes like bind_text or bind_value. Complex binding uses child elements to add conditional logic with multiple subjects and parameters, like bind_flag_if_eq or bind_state_if_gt."
+ - question: "What's the difference between a generic and a widget specific binding?"
+ answer: "A generic binding wraps an expression in @{ } and works on any widget property, with any number of subjects and variants. A widget specific binding is a dedicated bind_* property, like bind_text or bind_value, that takes a single subject without an expression and is often two-way."
- question: "Can multiple widgets bind to the same subject?"
answer: "Yes! Multiple widgets can bind to the same subject. When the subject's value changes (either from code or user interaction), all bound widgets automatically update to reflect the new value."
- question: "How do I use data binding to show or hide a widget?"
- answer: "Use complex binding with the 'hidden' flag. For example: will hide the widget when some_value equals 0."
+ answer: "Bind the 'hidden' property to a condition. For example: will hide the widget when some_subject equals 0."
---
## Overview
@@ -23,7 +23,7 @@ application or the UI, and whose value can be bound to widget
properties.
-Designing in Figma? You can bind layers to subjects directly in the plugin, no code required. See [Subjects & Data Binding in Figma Flow](../figma/subjects).
+Designing in Figma? You can bind layers to subjects directly in the Figma plugin, no code required. See [Subjects & Data Binding in Figma Flow](../figma/subjects).
For example, a `room1_temperature` subject's value can be set in the
@@ -31,9 +31,12 @@ application when the temperature is measured, and can be bound to a
label like this:
```xml
-
+
+
```
+Some widgets also define dedicated `bind_*` properties, for example ``. They take a single subject without an expression, and most of them are two way. See [Widget specific bindings](#widget-specific-bindings).
+
## Defining subjects
Subjects can be created in `globals.xml` like this:
@@ -51,156 +54,159 @@ Subjects can be created in `globals.xml` like this:
As the example shows, a subject consists of a type, name, and initial
value. Integer, string, and float types are supported.
+The initial value can be a constant or an [expression](./evaluate#outside-the-view) of constants, e.g. ``.
+
Float subjects require `LV_USE_FLOAT` to be enabled in `lv_conf.h`. Without it, the generated code fails to compile.
-## Simple binding
+## Binding widget properties
-Some widgets (e.g., label, slider) support binding the subject's value
-directly to the widget. These bindings use attributes that start with
-`bind_*` and reference a subject.
+### Generic bindings with expressions
+
+The simplest way to create bindings is to wrap subjects and optional expressions into `@{ }`. For example:
```xml
-
-
+
```
-Once a binding is created, if the subject's value changes (e.g., by
-adjusting the slider), all bound widgets will be updated automatically.
+These expressions support
-## Complex binding
+- multiple subjects
+- variants
+- basic arithmetic, ternaries, and logic operators
-In more complex cases---when a binding requires multiple
-parameters---the binding can be added as a child element of a widget.
-This allows binding multiple subjects with different parameters. For
-example:
+Inside `@{ }` names are written as **bare identifiers** (without `$` or `#`). The following symbols can be used:
+
+- **Subjects** - defined in `globals.xml`.
+- **Variants** - the Component's own reactive state. A variant name reads the currently selected option, and the option names can be used as constants. See [Variants](./variants).
+- **Constants** - they are inlined as literals.
+- **`type="subject"` properties** - see [Per-instance subjects](#per-instance-subjects) below.
+
+Properties (``) of types other than subject cannot be used, because they exist only while the element is being created. Such a binding is skipped with a warning, but the widget is still created normally.
+
+`@{ }` works on the attributes of **widgets**: on the `` itself, on any widget inside it, and on local style properties (`style_bg_color`, `style_pad_all-pressed`, ...). On a Component instance only its [variant](./variants) attributes can be bound.
+
+It can't be used in the `` section, as [styles](./styles) are initialized only once, and not on the attributes of a Component instance either:
```xml
-
-
-
-
-```
+
+
+
+
+
-Explanation of complex bindings:
+
+
-| Binding Type | Condition | Description |
-| --- | --- | --- |
-| `bind_flag_if_eq` | Equals | Set a flag if the subject's value equals the reference value |
-| `bind_flag_if_not_eq` | Not equals | Set a flag if the subject's value does not equal the reference value |
-| `bind_flag_if_gt` | Greater than | Set a flag if the subject's value is greater than the reference value |
-| `bind_flag_if_ge` | Greater or equal | Set a flag if the subject's value is greater than or equal to the reference value |
-| `bind_flag_if_lt` | Less than | Set a flag if the subject's value is less than the reference value |
-| `bind_flag_if_le` | Less or equal | Set a flag if the subject's value is less than or equal to the reference value |
-| `bind_state_if_eq` | Equals | Set a state if the subject's value equals the reference value |
-| `bind_state_if_not_eq` | Not equals | Set a state if the subject's value does not equal the reference value |
-| `bind_state_if_gt` | Greater than | Set a state if the subject's value is greater than the reference value |
-| `bind_state_if_ge` | Greater or equal | Set a state if the subject's value is greater than or equal to the reference value |
-| `bind_state_if_lt` | Less than | Set a state if the subject's value is less than the reference value |
-| `bind_state_if_le` | Less or equal | Set a state if the subject's value is less than or equal to the reference value |
+
+
-Note: The `lv_obj-` prefix can be omitted. For example, you can simply
-write `bind_state_if_gt` instead.
+
+
+
+
+
+
+
+
+```
-### Choosing the right attribute
+Learn more about the details on the [Evaluate expressions](./evaluate) page.
-`bind_flag_*` and `bind_state_*` look almost identical but expect a different attribute and a different set of values:
+### Per-instance subjects
-- `bind_flag_*` toggles a widget **flag** — use the `flag` attribute.
-- `bind_state_*` toggles a widget **state** — use the `state` attribute.
+Referencing a subject by name in the component makes every instance follow the *same* subject. To let each instance follow a *different* one, declare a `type="subject"` property and pass the subject's name where the Component is used:
```xml
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
```
-### Valid state values
+```xml
+
+
+
+```
-These match LVGL's `LV_STATE_*` constants. The same names are used as style selectors and in `bind_state_*` bindings.
+The value has to name an existing subject. If it doesn't, the binding is skipped with a warning.
-| Value | Meaning |
-|---|---|
-| `default` | Widget is in its base state |
-| `checked` | Widget is toggled on (e.g. a checkable button) |
-| `focused` | Widget has focus from any input device |
-| `focus_key` | Widget has focus from a keypad/encoder |
-| `edited` | Widget is being edited (e.g. encoder edit mode) |
-| `hovered` | Pointer is over the widget |
-| `pressed` | Widget is currently pressed |
-| `scrolled` | Widget is being scrolled |
-| `disabled` | Widget rejects input |
+### Widget specific bindings
-### Valid flag values
+Some widgets (e.g., label, slider) support binding the subject's value
+directly to the widget. These bindings use attributes that start with
+`bind_*` and reference a subject.
-The most commonly bound `LV_OBJ_FLAG_*` flags, exposed by the same names without the prefix:
+```xml
+
+
+```
-| Value | Effect when set |
-|---|---|
-| `hidden` | Widget is not drawn and ignores input |
-| `clickable` | Widget can be pressed/clicked |
-| `checkable` | Click toggles the `checked` state |
-| `scrollable` | Widget can be scrolled |
-| `scroll_on_focus` | Parent scrolls so the focused child is visible |
-| `floating` | Excluded from the parent's layout |
-| `ignore_layout` | Ignored by the parent's layout |
+Most of these are two way bindings: the widget can set the subject and the subject can set the widget too. If the subject's value changes (for example because the user dragged the slider), every widget bound to it is updated automatically.
-For the full list of states and flags see LVGL's [Object basics](https://lvgl.io/docs/open/intro/basics) and [Style states](https://lvgl.io/docs/open/main-modules/style#states) reference.
+`lv_label` also accepts a format string, which `@{ }` cannot do, so `bind_text` is the way to control the number of digits or decimals.
-## Selection groups via a shared subject
+### Old syntax
-A common pattern is to express "exactly one of these widgets is active" with a single integer subject and one `ref_value` per option. Combining `subject_set_int_event` (writes the subject on click) with `bind_state_if_eq` (reads it to drive the `checked` state) produces a runtime-driven radio group with no application code.
+Before `@{ }` existed, flags and states were bound like this:
```xml
-
-
-
-
+
+
+
+
```
+`bind_flag_*` takes a `flag`, `bind_state_*` takes a `state`, and both come in `_eq`, `_not_eq`, `_gt`, `_ge`, `_lt`, and `_le` variants. The `lv_obj-` prefix is optional.
+
+These still work, but `@{ }` is more powerful and replaces all of them, so it's recommended to migrate:
+
```xml
-
-
-
-
-
-
+
+```
-
-
+## Variants
-
-
+A subject is global, but a Component often needs reactive state of its own, for example whether a badge is `normal` or `danger`. That is what a [variant](./variants) is: a named list of options declared in the Component's ``, with its own value in every instance.
-
-
+Variants are used in bindings exactly like subjects:
-
-
-
-
-
+```xml
+
+
+
```
```xml
-
-
-
-
+
+
```
-When the user taps a cell, `subject_lamp` is written to that cell's `ref_value`; every other cell's `bind_state_if_eq` re-evaluates and drops out of the `checked` state automatically. The same subject can also be set from C (`lv_subject_set_int(&subject_lamp, …)`) to drive the selection from application code.
+Inside an expression a variant name reads the currently selected option, and an option name acts as a constant. See [Variants](./variants) for declaring them and for setting an option from C.
+
+## Bind styles
+
+To switch a whole style at runtime, use [``](./styles#style-binding), which takes the same kind of expression in its `if` attribute:
+
+```xml
+
+```
-## Subject Related Events
+## Subject related events
Besides binding properties to subjects, it's also possible to add
events that change the value of a subject on pressed, release, etc.
-Learn more about these in the [events documentation](./events).
\ No newline at end of file
+Learn more about these in the [events documentation](./events).
diff --git a/docs/syntax/evaluate.mdx b/docs/syntax/evaluate.mdx
index a664d74..8627c16 100644
--- a/docs/syntax/evaluate.mdx
+++ b/docs/syntax/evaluate.mdx
@@ -7,7 +7,7 @@ description: Compute attribute values from arithmetic, comparisons, ternaries, a
This feature requires [LVGL Pro Editor v2.0](https://github.com/lvgl/lvgl_pro/releases) or higher.
-Any XML attribute value can be an **expression** wrapped in `{ }`. Instead of writing a fixed value, you let the Editor compute it from numbers, colors, strings, constants, and parameters.
+Any XML attribute value can be an **expression** wrapped in `{ }`. Instead of writing a fixed value, you let the Editor compute it from numbers, colors, strings, constants, and parameters **when the instance is created**.
```xml
@@ -15,33 +15,51 @@ Any XML attribute value can be an **expression** wrapped in `{ }`. Instead of wr
```
+Wrapping the same expression in `@{ }` creates a dynamic data binding instead, **re-evaluated whenever a subject or variant in it changes**.
+
+```xml
+
+
+
+```
+
## Overview
Expressions are useful whenever a value is *derived* from other values: a width that is twice a parameter, a label that joins a prefix with a name, a flag that depends on a count, and so on. Keeping the calculation in the XML means you don't have to duplicate or pre-compute values by hand.
A few important properties to keep in mind:
-- **Creation-time only.** An expression is evaluated **once**, when the UI element is created. The result is then handed to the normal attribute parser exactly as if you had typed the computed value yourself.
-- **Not data binding.** Expressions do *not* react to changes. If you need a value that updates at runtime when a subject changes, use [data binding](./data-binding) instead.
-- **The result type is inferred from the terms.** You never declare the type — it follows from the literals and variables you use (see [Value types](#value-types)).
+- **`{ }` is creation-time only.** An expression is evaluated **once**, when the UI element is created. The result is then handed to the normal attribute parser exactly as if you had typed the computed value yourself.
+- **`@{ }` is reactive.** An `@{ }` expression is re-evaluated as the connected subjects or variants change.
+- **The result type is inferred from the terms.** You never declare the type, it follows from the literals and variables you use (see [Value types](#value-types)).
- Expressions are exported into the generated C code, not pre-computed to a fixed literal. They are re-evaluated every time the element is created — including when you create a component manually from C — using the parameter values passed at that moment.
+ Expressions are exported into the generated C code, not pre-computed to a fixed literal. They are re-evaluated every time the element is created, including when you create a component manually from C, using the parameter values passed at that moment.
-## Writing expressions
+The same expression engine is used in `` too. There no `@{ }` wrapping is needed, as that attribute is always treated as a binding.
+
+## Basic syntax
-An expression is the whole attribute value, wrapped in a single pair of braces:
+An expression is the whole attribute value, wrapped into `{ }` (one time evaluated) or `@{ }` (reactive binding):
```xml
```
-Inside the braces you can use literals, operators, and **variable names**. A variable name is written as a **bare identifier** — without the `$` or `#` prefixes used elsewhere in XML. The name is resolved, in order, against:
+Inside the braces you can use the following data:
+
+- In a `{ }` one time evaluation: literals, operators, constants, API ``s, subjects, and variants. Subjects and variants are read once, so their later changes are ignored.
+- In an `@{ }` binding: the same, except that API ``s cannot be used. They are not stored per instance, they only exist during creation. The one exception is a `type="subject"` ``, which names a subject the binding can follow.
-1. **Parameters** declared in the element's [``](./api) — the value comes from the instance (or the parameter's `default`), and the type from the declared `type`.
-2. **Constants** from [``](./constants) — the type is inferred from the constant's value.
-3. **Other named items** the XML processor resolves later (a font, image, subject, or screen name). These are passed through as plain strings.
+Names are written as **bare identifiers**, without the `$` or `#` prefixes used elsewhere in XML. They are resolved in this order:
+
+1. **Variants** declared in the element's [``](./api), the value is the currently selected option of that [variant](./variants).
+2. **Parameters** declared in the element's [``](./api) as ``.
+3. **Constants** from [``](./constants): the type is inferred from the constant's value.
+4. **Variant option names**: an integer constant, the option's index in its variant.
+5. **Subjects** from `globals.xml`.
+6. **Other named items** the XML processor resolves later (a font, image, or screen name).
```xml
@@ -59,7 +77,45 @@ Inside the braces you can use literals, operators, and **variable names**. A var
```
+
Note that `$` and `#` are not used in expressions to reference properties and constants.
+
+
+`{ }` is the general way to reference anything by name. `$param_name` and `#const_name` are the legacy syntax for the same thing: they reference a single name, so they can't be part of a larger expression.
+
+### Outside the view
+
+``, ``, ``, `` and `` are processed once, when the component is registered, before any instance of it exists. Expressions work there too, but they can only use **constants and literals**, with every operator, including `and` / `or` and the ternary:
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+A **property**, **subject** or **variant** has no value at that point: a property has no instance to take it from, and a subject or variant would have to be read again later, which these sections can't do. Using one is reported.
+
+An `if_target` block is filtered before its content is processed, so an expression there sees the constants of the current target.
## Value types
@@ -78,8 +134,8 @@ Each term has a type, inferred from how it is written:
Notes:
- **Colors** accept both the full `0xRRGGBB` form and the `0xRGB` shorthand, which expands like CSS (`0xf00` → `0xff0000`).
-- **Strings** use **single quotes** (`'…'`), since double quotes already delimit the XML attribute.
-- **Floats** require `LV_USE_FLOAT`. Without it, a float degrades to its integer part. Float results are serialized with two decimals (e.g. `3.50`).
+- **Strings** use **single quotes** (`'...'`), since double quotes already delimit the XML attribute.
+- **Floats** require `LV_USE_FLOAT`. Without it, a float degrades to its integer part. Float results are serialized with two decimals in texts (e.g. `3.50`).
## Operators
@@ -98,10 +154,10 @@ Notes:
```xml
-
+
```
-Only **one** comparison is allowed per expression — comparisons cannot be chained (`a < b < c` is invalid).
+Comparisons cannot be **chained**: `a < b < c` is invalid. Each operand of a logical `and`/`or` may contain one comparison, so a range check is written as `a > 10 and a <= 30`.
### Logical not
@@ -111,18 +167,29 @@ The unary `!` accepts a number, bool, or string and returns a bool. An empty or
```
-There are intentionally **no** `&&`, `||`, or `^^` operators.
+### Logical and / or
-### Ternary
+`&&` and `||` combine two conditions and return a bool. Their operands can be anything that has a truthiness: a bool, a number (non-zero is true), or a string (non-empty is true). A color has none, so it's a type error.
-`cond ? a : b` chooses between two values. The condition must be a bool, a number, or a string (a non-empty string is truthy); it **cannot** be a color. Both branches must have the same type (int and float mix freely).
+Because `&` must be escaped in an XML attribute value, both operators also have a keyword spelling, `and` and `or`, which is usually the readable choice:
```xml
-
-
+
+
+
```
-Ternaries **cannot be nested**, but each branch may itself be a string concatenation.
+`&&` binds tighter than `||`, and both bind tighter than concatenation and the ternary. So a combined condition can be written directly as a ternary condition, without parentheses:
+
+```xml
+
+```
+
+
+Both sides are **always evaluated**. There is no short-circuiting, just like with the two ternary branches. So `count != 0 and total / count > 5` still fails on a division by zero when `count` is 0.
+
+
+`and`/`or` are keywords, so a constant, property, or subject cannot be named `and` or `or`. There is no `^^` operator.
### String concatenation
@@ -133,6 +200,17 @@ The `.` operator joins values into a single string. Each operand is automaticall
```
+### Ternary
+
+`cond ? a : b` chooses between two values. The condition must be a bool, a number, or a string (a non-empty string is truthy); it **cannot** be a color. Both branches must have the same type (int and float mix freely).
+
+```xml
+
+
+```
+
+Ternaries **cannot be nested**, but each branch may itself be a string concatenation.
+
## Precedence and grouping
From highest to lowest:
@@ -141,25 +219,39 @@ From highest to lowest:
2. `*`, `/`, `%`
3. `+`, `-`
4. Comparisons (`==`, `!=`, `<`, `<=`, `>`, `>=`)
-5. Concatenation (`.`)
-6. Ternary (`? :`)
+5. `&&` (`and`)
+6. `||` (`or`)
+7. Concatenation (`.`)
+8. Ternary (`? :`)
-Parentheses `( )` group **arithmetic and comparison** sub-expressions. They cannot contain a concatenation or a ternary.
+Parentheses `( )` group **arithmetic, comparison, and logical** sub-expressions. They cannot contain a concatenation or a ternary.
```xml
+
```
-## Error handling
-If an expression is malformed — an unknown identifier, a type mismatch, a missing `)` or `:`, division by zero, and so on — evaluation fails. A warning describing the exact problem and the offending expression is logged, and the attribute is left unset.
+## XML escaping
+
+An expression lives in an XML attribute value, where a few characters are reserved. This is a property of XML itself, not of the expression syntax:
-Common messages include:
+| Character | Write it as | Note |
+|---|---|---|
+| `<` | `<` | Never allowed in an attribute value |
+| `&` | `&` | So `&&` becomes `&&`. Or use `and` |
+| `'` | `'` | Only if the attribute value itself is delimited by `'` |
+| `>` | `>` | Allowed as-is |
+
+```xml
+
+
-- *unknown identifier* — the name is not a const, or prop.
-- *the two ternary branches must have the same type*.
-- *`< <= > >=` need numeric operands* — use `==`/`!=` for strings, colors, and bools.
-- *arithmetic `+ - * / %` needs numeric operands*.
-- *unexpected character (note: use `==` to compare, `.` to concatenate)*.
+
+
+
+
+
+```
## Examples
@@ -174,8 +266,8 @@ Common messages include:
-
+
-
+
```
diff --git a/docs/syntax/fonts.mdx b/docs/syntax/fonts.mdx
index a3873a1..44003ea 100644
--- a/docs/syntax/fonts.mdx
+++ b/docs/syntax/fonts.mdx
@@ -86,7 +86,7 @@ even `api` properties:
-
+
diff --git a/docs/syntax/images.mdx b/docs/syntax/images.mdx
index 048c2f8..f46d1d3 100644
--- a/docs/syntax/images.mdx
+++ b/docs/syntax/images.mdx
@@ -151,7 +151,7 @@ Black is the practical choice: the alpha channel carries the shape, so a single
asset can be tinted anywhere it's used and one icon set serves every theme.
```xml
-
+
```
## Using the Images
@@ -178,7 +178,7 @@ In custom properties:
-
+
```
diff --git a/docs/syntax/index.mdx b/docs/syntax/index.mdx
index 429e524..513271a 100644
--- a/docs/syntax/index.mdx
+++ b/docs/syntax/index.mdx
@@ -116,6 +116,12 @@ This section covers everything from basic building blocks to advanced styling an
Define custom APIs for widgets and components with properties, parameters, enums, and elements.
+ }>
+ **Variants**
+
+ Declare a Component's named visual states, like size or tone, and switch between them at runtime.
+
+
}>
**Events in XML**
diff --git a/docs/syntax/meta.json b/docs/syntax/meta.json
index 3b7aec0..7fef27c 100644
--- a/docs/syntax/meta.json
+++ b/docs/syntax/meta.json
@@ -23,6 +23,7 @@
"---Interaction & Configuration---",
"api",
+ "variants",
"events",
"data-binding",
"evaluate",
diff --git a/docs/syntax/overview.mdx b/docs/syntax/overview.mdx
index 97a5135..e3b6211 100644
--- a/docs/syntax/overview.mdx
+++ b/docs/syntax/overview.mdx
@@ -15,9 +15,26 @@ LVGL Pro Editor uses standard XML syntax with special conventions for property n
- Hex without prefix: `112233`
- Short hex: `0x123`, `#123`, or `123` (all equivalent to `#112233`)
- **LVGL API alignment**: Follow LVGL naming conventions (e.g., `align="center"`, `bg_color="0xff0000"`)
-- **Property references**: Use `$` prefix (e.g., `$param_name`)
-- **Constant references**: Use `#` prefix (e.g., `#const_name`)
-- [**Expressions**](./evaluate): Use `{ }`. When referencing properties or constants inside, no `$` or `#` is needed
+- **References**: Use `{ }` to reference anything by name: a ``, a constant, a subject, or a variant (e.g. `width="{card_width}"`). Inside the braces the name is written plain, and it can be part of a larger [expression](./evaluate)
+- **Legacy references**: `$param_name` for a `` and `#const_name` for a constant. Each takes a single name, so it can't be part of an expression
+
+## Names and Scopes
+
+Styles, constants, subjects, and animation timelines are all referenced **by name**. Every Component, Widget, and Screen has its own **local scope**, and `globals.xml` holds the **global scope**.
+
+A name is looked up in the local scope first, and in the global scope only after that. Two consequences:
+
+- Two Components can use the same local name freely. Their scopes never see each other.
+- A local name **hides** a global one. If a Component defines `style_card` and `globals.xml` defines `style_card` too, that Component always gets its own.
+
+Keeping names unique **within one scope is your responsibility**. Nothing reports a collision, so give every style, constant, subject, timeline, and widget `name="..."` a distinct name. Two cases make a collision easy to miss:
+
+- In an [expression](./evaluate) a bare name is resolved in a fixed order: variant, property, constant, variant option, subject. The first match wins, so a constant named `temperature` hides a subject of the same name.
+- A `name="..."` is found by searching the widget tree and the first match is used, so two widgets with the same name make animation targets and `lv_obj_find_by_name()` ambiguous.
+
+A prefix per kind keeps them apart: `style_card`, `subject_temperature`, `timeline_load`.
+
+Component, Widget, and Screen names come from their file names and are kept separately, so `h3.xml` never collides with a style, constant, or subject named `h3`.
## Property Names
@@ -84,7 +101,28 @@ The same names can also be used as `` types so a Component can accept an a
| **subject** | A subject registered under `` | `` |
| **style** | A style defined in `` or `globals.xml` | `` |
-These props are typically forwarded into a child element with the `$` prefix, e.g. `` or ``.
+These props are typically forwarded into a child element by name, e.g. `` or ``.
+
+A **style** prop names a style of the Component that passes it, and it is resolved there:
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+
+A `name` is always a literal or a single `$param_name` / `#const_name` reference, never an expression. The Editor has to resolve it to check that the style, font, image, and so on exists.
+
**Automatic binding** occurs for:
- Styles, fonts, images, animations
diff --git a/docs/syntax/screens.mdx b/docs/syntax/screens.mdx
index 8bbd739..249e398 100644
--- a/docs/syntax/screens.mdx
+++ b/docs/syntax/screens.mdx
@@ -37,7 +37,7 @@ This example illustrates a screen in XML using `my_header` and `my_main_cont` co
-
+
diff --git a/docs/syntax/styles.mdx b/docs/syntax/styles.mdx
index caa3173..35bed4a 100644
--- a/docs/syntax/styles.mdx
+++ b/docs/syntax/styles.mdx
@@ -56,6 +56,49 @@ As shown in the example, parts and states can be set using `selector`.
It's recommended to prefix style names with `style_` to avoid name collisions.
+### Transitions
+
+By default the properties of a style are applied immediately when the widget's state changes. A `` child of a `
+
+
+
+
+
+
+
+
+```
+
+`` supports these properties:
+
+- `props` - Space separated list of the style properties to animate. Mandatory. Use `remove` to delete a previously set transition.
+- `duration` - Duration of the animation in milliseconds. Mandatory.
+- `delay` - Delay before the animation starts in milliseconds. Default is `0`.
+- `easing` - The animation path, e.g. `ease_out`. See [Easing](./animations#easing). Default is `linear`.
+
+A transition describes how to animate **into** the state of the style that contains it. In the above example pressing the button uses the 80 ms transition of `style_button_pressed`, and releasing it uses the 300 ms transition of `style_button`.
+
+This means that to animate in both directions, a `` needs to be added to the default state's style too. If only the pressed style has one, the button animates when it's pressed, but snaps back when it's released.
+
+
+Only state changes (`pressed`, `checked`, `disabled`, `focused`, ...) start a transition. `` enables and disables styles instead of changing the state, so its styles are applied without animation.
+
+
+Keep these limitations in mind:
+
+- A style can store only one transition. To use different durations for different properties, split them into two styles.
+- Only numeric (including opacity) and color properties can be animated. Properties referencing other data (`text_font`, `bg_image_src`, `bg_grad`, ...) and most enums and booleans (`flex_flow`, `text_align`, `clip_corner`, ...) are ignored with a warning.
+- The composite names are expanded, so e.g. `props="pad_all"` transitions all the four paddings.
+- Like in any other style property, constants and `{ }` expressions of them can be used, but API ``s can't.
+
## Style Binding
Instead of directly adding styles to UI elements, you can add them conditionally when a subject's value equals a reference value. This enables dynamic styling at runtime, allowing you to change appearance or layout based on conditions.
@@ -85,6 +128,35 @@ Here is an example:
```
+`` accepts the same `selector` as `