I copied my slint checkout from one compter to another, and it ended up
in a different path. Running test-driver-nodejs on the new computer gave this error:
ERR_PNPM_ABORTED_REMOVE_MODULES_DIR_NO_TTY Aborted removal of modules directory due to no TTY
If you are running pnpm in CI, set the CI environment variable to "true".
This is because pnpm detects that the nodes-module directory isn't as
expected (wrong path in the storeDir key of .modules.yaml), and wants
to wipe it out. But first, it wants to ask for confirmation...
and it fails because there's no TTY (pnpm being launched indirectly).
This is fixed by --config.confirmModulesPurge=false
(or --force, or setting the env var CI to true)
See references in the comments
* compiler: Introduce ElementType::Interface
Add a new ElementType variant that will allow us to parse interface
declarations using the existing element parsing code.
The match arms implemented are my best guess based on the context of
the enclosing function and how a global is handled.
* compiler: `interface` is a valid top-level item keyword
A document may contain zero or more interfaces. Interfaces do not
support inheritance.
* compiler: An interface excludes the same content as a global
Verify that an interface cannot have:
- sub elements;
- repeated elements;
- property animations;
- states;
- transitions;
- an init callback declaration;
- an init callback implementation.
* compiler: An interface can have public properties
Warn if private properties are declared.
* compiler: `implements` is a valid keyword
It is treated as an alias for `inherits`, for now.
* compiler: Cannot "implement" non-interface components
The compiler produces an error when the `implements` keyword is used
before a global, component, or builtin type.
The parser doesn't know about this kind of relationship - the
"implements" and "inherits" keywords are just consumed. For now we
just go back and check what the expected relationship type was whilst
constructing an Element.
* compiler: interface properties are exposed
Attempt to verify that the interpreter can see and use properties
declared in an interface that a component implements.
This is an initial proof-of-concept, we will need to come back and add
checks and tests for mixing 'inherits' with 'implements'.
* Move existing interface syntax texts to syntax/interfaces
Group interface related tests in a subdirectory of their own.
* compiler: Add uses specified as valid new component syntax
Allow a new component to specify that it exposes one or more
interfaces via a given child element.
This commit introduces the syntax, we will add the implementation in a
future commit.
* compiler: uses statements expose interface properties from child
For each uses statement, iterate through the interface properties and
create two-way bindings to the property on the specified base
component.
The error cases will be populated in follow-up commit. For now,
demonstrate that the initial concept works.
* compiler: emit errors for invalid interfaces in uses statements
Detect and emit errors where:
- the type specified in a uses statement is not an interface;
- the type specified in a uses statement is unknown;
- the type specified in a uses statement cannot be used in this
context.
* compiler: emit an error when the child specified in a uses statement does not exist
Add a test case to verify the expected error message.
* compiler: verify that external interfaces can be implemented
Add a test to verify that interfaces from imported modules can be
used.
* compiler: verify that renamed components and interfaces can be used
Add a test case to verify that:
- we can import an interface as another name and re-use it;
- we can import a component that implements a renamed interface and
use it.
* compiler: verify that the child component implements the interface
For each property in an interface that a child element is expected to
implement, verify that the child has a property with the same name,
type and visibility.
This is easier than attempting to store and match interface names
which may change if the user renames an interface when importing.
* compiler: do not allow interface properties to be overridden
Emit a compile error if the user attempts to declare a property that
would override a property from an interface.
* compiler: do not permit interfaces to be used if they would override an inherited property
Look up the interface property on the base type before allowing a
component to implement the interface.
* compiler: emit an error if a uses statement attempts to override an existing binding
I couldn't figure out a good way to test this - everything I came up
with got caught by the prior check that the property does not already
exist.
* compiler: 'interface', 'implements' and 'uses' are experimental features
Guard these keywords behind the experimental features flag. Slint must
be compiled with the `SLINT_ENABLE_EXPERIMENTAL_FEATURES` environment
variable set to enable these keywords.
* compiler: parse_uses_identifier returns a valid SyntaxKind::UsesIdentifier
Previously we were not creating a DeclaredIdentifier if a syntax error
was encountered. This would cause a panic when
`UsesIdentifier::DeclaredIdentifier()` was called for a node with a
syntax error.
As a result, we can convert `TryFrom<&syntax_nodes::UsesIdentifier>
for UsesStatement` to `From<&syntax_nodes::UsesIdentifier> for
UsesStatement`.
* compiler: simplify `UsesSpecifier -> UsesSpecifierList -> UsesIdentifier` to `UsesSpecifier -> UsesIdentifier`
We don't need to intermediate list object because we only expect a
UsesSpecifier to contain UsesIdentifiers.
* lsp: Ensure that uses statements are not broken
Previously the space between the DeclaredIdentifier and the `from`
keyword was being removed. E.g.:
```slint
component C uses { Foo from bar } { }
```
would become:
```slint
component C uses { Foofrom bar } { }
```
This would cause a syntax error. This no longer happens.
* compiler: ':=' to declare an interface is an error
Previously this was a warning to match the global behaviour. Given
that interfaces are a new feature, make it an error to declare them
using the deprecated ':='.
* compiler: private properties in an interface are an error
It does not make sense to declare an interface with properties that
are not accessible to the user of the interface.
* Update insterface syntax tests
The syntax for declaring an expected diagnostic has changed to include
the span (#9703). Update the interface tests that were added using the
old syntax.
* compiler: check experimental features before emitting more specific interface errors
The `implements` keyword should not be supported if experimental
features are not enabled.
* compiler: Simplify UsesStatement
Only store the `syntax_nodes::UsesIdentifier` and replace the old
members with functions returning the appropriate child node of the
`UsesIdentifier`.
* compiler: clean up ElementType::Interface TODO comment
We cannot instantiate an interface element, so we should not be able
to reach one by recursing into the children of a component.
The code used to first call to_layout_cell_data in order
to turn the organized data into the old-style GridLayoutCellData
(by zipping together the row/col/span information and the constraints),
before passing that to to_layout_data just to iterate through.
We can eliminate this intermediary Vec and look things up in the
GridLayoutOrganizedData slice directly, to save memory allocations
and simplify the code.
In to_layout_data I simplified things further by iterating only
once over the cells, reducing the amount of copy/pasted code.
The goal is to be able to enable feature conditionally with `i-slint-renderer-software/?...`
NOTE: this change the implementation of
`SceneBuilder::platform_text_fill_brush/platform_text_stroke_brush`
It was warning about `non_exhaustive_omitted_patterns`.
And it changed it to always return the brush color so gradient gets
converted to plain color instead of None.
This is the behavior with the non-parley renderer
Motivation: the git pre-commit hook is confused by the mix of 2021 and
2024 in the same workspace...
The only problem this raises is the std::env::set_var("LANGUAGE", lang) in
demos/printerdemo/rust/lib.rs, pre-existing but now explicitly marked as unsafe
because it actually is. Added a TODO there.
vec![] expands to Vec::new(), but expanding macros takes a bit of
time and takes memory in rust-analyzer. Also, the code was using a mix
of vec![] and Vec::new(), so this is more consistent.
* Fix exporting builtin global (such as Palette from Qt style)
We would otherwise have errors in our generated code such as
`no 'NativePalette' in slint_generatedTestCase`
So we need to generate the public Rust/C++ API for the builtin globals
as well
So that if ever we make them public, we can still add values.
... all but the Orientation as it is often used in match and
will never get any other variant
Let's call "auto" the case where the slint file doesn't specify
the row or col for a cell in a grid layout.
When the runtime expression for row/col changes, what should happen to
the subsequent cells with 'auto' behavior for their column number
(including the case where a change of row implies restarting at
column 0) ?
Option A: they move with it, as if it had all been specified that way at
compile time (dynamic auto behavior)
Option B: they stay where they are (compile-time-only auto behavior)
Both have upsides and downsides (the risk of colliding with an existing
cell exists either way), so the current decision is: let's just forbid
it.
Note that the code currently implements option A, so if one day
it's decided in favor of that, just revert this commit...
This allows to store the result into a different cache property
than the one that comes out of solve_grid_layout, which fixes
a binding loop when the text of a widget depends on its row or col
value.
ChangeLog: the row, col, rowspan and colspan properties can now be
expressions rather than having to be constants. The layout adapts when
the value of the expression changes.
In the guide, grouped mobile platforms into their own category, added a general page that explains developing for all mobile platforms, added documentation for getting started with Android.
The generated file offers the following two pieces of functionality:
- Convenient front-end to slint.load_file() by loading the .slint file
(replaces use of auto-loader)
- More importantly: Type information for exported properties, callbacks,
functions, and globals
On loading, the previously generated API is compared to what's in the
.slint file and if the .slint file was changed in an incompatible way,
an exception is thrown.
A Python test driver is added which performs three steps with each test
case:
1. Generate python stubs and checks that they can be loaded with the
python interpreter and that `uv ty check` passes.
2. Extract expected python API out of pyi sections and compares them.
3. Appends any python code from python sections and runs them (this is
combined with step 1)
Fixes#4136
Transform or Opacity item might have children outside of their geometry.
If the item itself is clipped, we still need to apply the opacity or
transform state to the children, as the children might not be clipped.
Fixes https://github.com/slint-ui/slint/issues/10037
* Add support for CSS conic-gradient 'from <angle>' syntax
Implement rotation support for conic gradients by adding the 'from <angle>'
syntax, which rotates the entire gradient by the specified angle.
- Add `from_angle` field to ConicGradient expression
- Parse 'from <angle>' syntax in compiler (defaults to 0deg when omitted)
- Normalize angles to 0-1 range (0.0 = 0°, 1.0 = 360°)
- Add `ConicGradientBrush::rotated_stops()` method that:
* Applies rotation by adding from_angle to each stop position
* Adds boundary stops at 0.0 and 1.0 with interpolated colors
* Handles stops outside [0, 1] range for boundary interpolation
- Update all renderers (Skia, FemtoVG, Qt, Software) to use rotated_stops()
The rotation is applied at render time by the rotated_stops() method,
which ensures all renderers consistently handle the gradient rotation.
* Add screenshot to rotated conic gradient docs example
Wraps the rotated conic gradient example in CodeSnippetMD to automatically
generate and display a visual screenshot of the gradient rotation effect.
This makes it easier for users to understand how the 'from' parameter rotates
the gradient.
* Make ConicGradientBrush fields private
The from_angle and stops fields don't need to be pub since:
- Rust code in the same module can access them without pub
- C++ FFI access works through cbindgen-generated struct (C++ struct members are public by default)
* Optimize ConicGradientBrush::rotated_stops to avoid allocations
- Changed return type from Vec to SharedVector
- When from_angle is zero, returns a clone of internal SharedVector
(only increments reference count instead of allocating new Vec)
- Removed break from duplicate position separation loop to handle
all duplicate pairs, not just the first one
- Updated documentation to match actual implementation
* Remove automatic sorting in ConicGradientBrush::new() to match CSS spec
- CSS conic-gradient does not automatically sort color stops
- Stops are processed in the order specified by the user
- Changed boundary stop interpolation logic to use max_by/min_by
instead of relying on sorted order
- This allows CSS-style hard transitions when stops are out of order
* Move conic gradient rotation processing to construction time
Major changes:
- ConicGradientBrush::new() now applies rotation and boundary stop
processing immediately, instead of deferring to rotated_stops()
- Removed rotated_stops() method - backends now use stops() directly
- Changed to LinearGradientBrush pattern: store angle in first dummy stop
- Added angle() method to retrieve the stored angle
- Maintained #[repr(transparent)] by removing from_angle field
- All backends updated: rotated_stops() -> stops()
- Qt backend
- Skia renderer
- femtovg renderer
- Software renderer
C++ API changes:
- Added FFI function slint_conic_gradient_new() for C++ to call Rust's new()
- Updated make_conic_gradient() to call FFI function instead of manually
constructing SharedVector
- Ensures C++-created gradients get full rotation processing
Benefits:
- Eliminates per-frame rotation calculations
- Reduces memory usage (no from_angle field)
- Consistent with LinearGradientBrush design
- C++ and Rust APIs now produce identical results
* Change ConicGradientBrush::new() from_angle parameter to use degrees
- Changed from_angle parameter from normalized form (0.0-1.0) to degrees
- Matches LinearGradientBrush API convention (angle in degrees)
- Updated internal conversion: from_angle / 360.0 for normalization
- Stores angle as-is in degrees in the first dummy stop
- FFI function slint_conic_gradient_new() passes degrees directly
Example usage:
ConicGradientBrush::new(90.0, stops) // 90 degrees
LinearGradientBrush::new(90.0, stops) // 90 degrees (consistent)
* Fix ConicGradient color transformation methods to preserve angle
Changed brighter(), darker(), transparentize(), and with_alpha() methods
to clone and modify the gradient in-place instead of calling new().
- Clones the existing gradient (preserves angle and rotation)
- Modifies only color stops (skips first stop which contains angle)
- Avoids re-running expensive rotation processing
- Maintains the original angle information
Before: ConicGradientBrush::new(0.0, ...) // Lost angle information
After: Clone + modify colors in-place // Preserves angle
* Use premultiplied alpha interpolation for conic gradient colors
- Changed interpolate_color() to use premultiplied RGBA interpolation
- Updated signature to match Color::mix convention (&Color, factor)
- Added documentation explaining why we can't use Color::mix() here
(Sass algorithm vs CSS gradient color interpolation)
- Reference: https://www.w3.org/TR/css-images-4/#color-interpolation
This ensures correct visual interpolation of semi-transparent colors
in gradient boundaries, following CSS gradient specification.
* Run rustfmt on conic gradient code
* Fix ConicGradientBrush edge cases and add comprehensive tests
- Handle stops that are all below 0.0 or all above 1.0
- Add default transparent gradient when no valid stops remain
- Add 7 unit tests covering basic functionality and edge cases
* Apply clippy suggestion: use retain() instead of filter().collect()
* Fix radial-gradient parsing to allow empty gradients
Allow @radial-gradient(circle) without color stops, fixing syntax test
regression from commit 820ae2b04.
The previous logic required a comma after 'circle', but it should only
error if there's something that is NOT a comma.
* Fix conic-gradient syntax test error markers
Update error markers to match actual compiler error positions.
The 'from 2' case produces two errors:
- One at the @conic-gradient expression level
- One at the literal '2' position
Auto-updated using SLINT_SYNTAX_TEST_UPDATE=1.
* Refactor ConicGradientBrush epsilon adjustment and update tests
- Move epsilon adjustment for first stop into rotation block
(only needed when rotation is applied)
- Update property_view test to reflect boundary stops added by
ConicGradientBrush::new()
* Update conic-gradient screenshot reference image
Update the reference screenshot to match the current rendering output.
The small pixel differences (1% different pixels, max color diff 3.46)
are due to minor rounding differences in the conic gradient implementation.
* Fix ConicGradientBrush C++ FFI to avoid C-linkage return type error
Refactored ConicGradientBrush construction to match LinearGradientBrush
pattern, fixing macOS Clang error about returning C++ types from extern "C"
functions.
Changes:
- Rust: Split ConicGradientBrush::new into simple construction + separate
normalize_stops() and apply_rotation() methods
- Rust: Added FFI functions slint_conic_gradient_normalize_stops() and
slint_conic_gradient_apply_rotation() that take pointers (no return value)
- C++: Construct SharedVector directly in make_conic_gradient(), then call
Rust functions via pointer (matching LinearGradientBrush pattern)
- Optimized both methods to only copy when changes are needed
This resolves the macOS Clang error:
"'slint_conic_gradient_new' has C-linkage specified, but returns incomplete
type 'ConicGradientBrush' which could be incompatible with C"
The new approach maintains ABI compatibility while keeping complex gradient
processing logic in Rust.
* Fix C++ header generation to avoid GradientStop redefinition error
Resolves the macOS CI compilation error where GradientStop and
ConicGradientBrush were being defined in multiple headers
(slint_color_internal.h, slint_image_internal.h, and slint_brush_internal.h).
Changes:
- cbindgen.rs: Add ConicGradientBrush and FFI functions to slint_brush_internal.h include list
- cbindgen.rs: Add GradientStop, ConicGradientBrush, and FFI functions to exclude list for other headers
- slint_color.h: Add forward declaration for ConicGradientBrush
- slint_color.h: Add friend declaration for ConicGradientBrush to allow access to Color::inner
Root cause: After adding extern "C" functions in graphics/brush.rs,
cbindgen automatically detects and tries to include them in all headers
that use graphics/brush.rs as a source. The exclude list + filter logic
ensures these types only appear in slint_brush_internal.h.
This fixes the C++ compilation errors:
- "redefinition of 'GradientStop'"
- "ConicGradientBrush does not name a type"
- "Color::inner is private within this context"
* Prepare ConicGradientBrush FFI for Rust 2024 edition
Update FFI functions to use the new `#[unsafe(no_mangle)]` attribute
syntax and safe function signatures in preparation for Rust 2024 edition.
- Add `#![allow(unsafe_code)]` to graphics module for `#[unsafe(no_mangle)]`
- Add `#[cfg(feature = "ffi")]` to conditionally compile FFI functions
- Change from raw pointers to safe references (&mut)
- Remove manual null checks and unsafe blocks
I kept seeing distracting warnings when working with these testcases,
and it's better to have good examples when writing new testcases.
I didn't touch the issue_*.slint regression tests, so those will
still be testing the old syntax.
Rename grid to grid_simple, otherwise
cargo test -p test-driver-interpreter -- test_interpreter_layout_grid
runs multiple tests, no way to run only that one.
Same for nested_grid.slint given that there's a nested_grid_2.slint
* Change Color type to be f32 internally but no other changes
* Add missing clamp
* Add round function as we're not on MSRV 1.90+
* Prepare for being able to switch between u8s and f32s
* [autofix.ci] apply automated fixes
* Fix Display impl
* Add feature flag for 8-bit color values. Name to be bikeshed
* Fix brighter-darker test
* Update test screenshot
* Remove unused Float trait import
* Add cfg thing for not(cbindgen)
* Change Channel to float in cbindgen
* Change the cpp color type for uint8_t for now
* Opt cpp into 8-bit-color
* Switch feature around
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This line was added by mistake in a previous commit.
But the feature can't work for C++ and the interpreter for now because
they can't re-use the same binding.
It would also break for eust if there was another layer of two way
binding.
That's because `impl BindingCallable for TwoWayBindingWithMap` doesn't
(and can't) implement intercept_set_binding