Properties in Slint
Introduction
All elements in Slint have properties. Built-in elements come with standard properties such as color
or dimensional properties like width
and height
.
You can assign values directly or use expressions.
Example:
export component Example inherits Window {
// Simple expression (ends with semicolon)
width: 42px;
// Code block (no semicolon)
height: { 42px }
}
The default value of a property is the default value of its type:
bool
→false
int
→0
- etc.
Declaring Custom Properties
You can define extra properties by specifying the type, name, and optional default value:
export component Example {
// Integer property named my-property
property<int> my-property;
// Integer property with a default value
property<int> my-second-property: 42;
}
Property Qualifiers
You can annotate properties with qualifiers that control how they can be accessed:
- private (default): Accessible only within the component.
- in: Input property — can be set by the user of the component, but not overwritten internally.
- out: Output property — can only be set by the component, read-only for the user.
- in-out: Readable and writable by both the component and the user.
Example:
export component Button {
in property <string> text; // Set by the user
out property <bool> pressed; // Read by the user
in-out property <bool> checked; // Changed by both
private property <bool> has-mouse; // Internal only
}
Properties declared at the top level that aren’t private are accessible externally when using the component as an element, or from business logic via language bindings.
Change Callbacks
You can define a callback that is triggered when a property changes value.
import { LineEdit } from "std-widgets.slint";
export component Example inherits Window {
VerticalLayout {
LineEdit {
// Triggered when text changes
changed text => { t.text = self.text; }
}
t := Text {}
}
}
Notes on Change Callbacks:
- They are not invoked immediately — instead, they are queued and executed in the next event loop iteration.
- Invoked only if the property’s value actually changes.
- If multiple changes occur in one event loop cycle, the callback runs only once.
- If the value reverts before execution, the callback won’t be called.
Warning: Avoid Loops in Change Callbacks
Creating a callback loop can cause undefined behavior.
Example of a potential loop:
export component Example {
in-out property <int> foo;
property <int> bar: foo + 1;
changed bar => { foo += 1; } // Potential infinite loop
}
Slint will break such loops after a few iterations, which can prevent other callbacks from running.
Best Practices
- Prefer declarative bindings over change callbacks when possible.
- Avoid:
changed bar => { foo = bar + 1; }
- Instead, use:
foo: bar + 1;
Declarative bindings:
- Automatically track dependencies.
- Are lazily evaluated.
- Maintain binding purity.
- Are easier to work with in graphical editors.
Excessive use of changed
callbacks can lead to bugs, complexity, and performance issues.