Commit f24ad34
authored
Add support for CSS conic-gradient 'from <angle>' syntax (slint-ui#9830)
* 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 820ae2b.
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 blocks1 parent be0aea7 commit f24ad34
File tree
23 files changed
+614
-122
lines changed- api/cpp
- include
- docs/astro/src/content/docs/reference
- internal
- compiler
- generator
- llr
- passes
- resolving
- tests/syntax/basic
- core
- graphics
- interpreter
- renderers/femtovg
- tests/screenshots
- cases/software/basic
- references/software/basic
- tools/lsp/preview
- ui
23 files changed
+614
-122
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
518 | 518 | | |
519 | 519 | | |
520 | 520 | | |
521 | | - | |
| 521 | + | |
| 522 | + | |
522 | 523 | | |
523 | 524 | | |
524 | 525 | | |
| |||
578 | 579 | | |
579 | 580 | | |
580 | 581 | | |
| 582 | + | |
| 583 | + | |
| 584 | + | |
| 585 | + | |
581 | 586 | | |
582 | 587 | | |
583 | 588 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
105 | 105 | | |
106 | 106 | | |
107 | 107 | | |
108 | | - | |
109 | | - | |
110 | | - | |
111 | | - | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
112 | 113 | | |
113 | 114 | | |
114 | 115 | | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
115 | 119 | | |
116 | | - | |
| 120 | + | |
117 | 121 | | |
118 | 122 | | |
119 | | - | |
| 123 | + | |
120 | 124 | | |
121 | 125 | | |
122 | 126 | | |
| |||
127 | 131 | | |
128 | 132 | | |
129 | 133 | | |
130 | | - | |
| 134 | + | |
131 | 135 | | |
132 | 136 | | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
133 | 140 | | |
134 | 141 | | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
135 | 150 | | |
136 | 151 | | |
137 | 152 | | |
| |||
223 | 238 | | |
224 | 239 | | |
225 | 240 | | |
226 | | - | |
227 | | - | |
| 241 | + | |
| 242 | + | |
228 | 243 | | |
229 | 244 | | |
230 | 245 | | |
| |||
252 | 267 | | |
253 | 268 | | |
254 | 269 | | |
255 | | - | |
| 270 | + | |
256 | 271 | | |
257 | 272 | | |
258 | 273 | | |
| |||
282 | 297 | | |
283 | 298 | | |
284 | 299 | | |
285 | | - | |
| 300 | + | |
286 | 301 | | |
287 | 302 | | |
288 | 303 | | |
| |||
314 | 329 | | |
315 | 330 | | |
316 | 331 | | |
317 | | - | |
| 332 | + | |
318 | 333 | | |
319 | 334 | | |
320 | 335 | | |
| |||
347 | 362 | | |
348 | 363 | | |
349 | 364 | | |
350 | | - | |
| 365 | + | |
351 | 366 | | |
352 | 367 | | |
353 | 368 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
| 12 | + | |
12 | 13 | | |
13 | 14 | | |
14 | 15 | | |
| |||
214 | 215 | | |
215 | 216 | | |
216 | 217 | | |
| 218 | + | |
217 | 219 | | |
218 | 220 | | |
219 | 221 | | |
| |||
Lines changed: 21 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
167 | 167 | | |
168 | 168 | | |
169 | 169 | | |
170 | | - | |
| 170 | + | |
171 | 171 | | |
172 | 172 | | |
173 | 173 | | |
174 | 174 | | |
175 | 175 | | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
176 | 180 | | |
177 | 181 | | |
178 | 182 | | |
| |||
189 | 193 | | |
190 | 194 | | |
191 | 195 | | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
192 | 212 | | |
193 | 213 | | |
194 | 214 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
739 | 739 | | |
740 | 740 | | |
741 | 741 | | |
| 742 | + | |
| 743 | + | |
742 | 744 | | |
743 | 745 | | |
744 | 746 | | |
| |||
974 | 976 | | |
975 | 977 | | |
976 | 978 | | |
977 | | - | |
| 979 | + | |
| 980 | + | |
978 | 981 | | |
979 | 982 | | |
980 | 983 | | |
| |||
1077 | 1080 | | |
1078 | 1081 | | |
1079 | 1082 | | |
1080 | | - | |
| 1083 | + | |
| 1084 | + | |
1081 | 1085 | | |
1082 | 1086 | | |
1083 | 1087 | | |
| |||
1174 | 1178 | | |
1175 | 1179 | | |
1176 | 1180 | | |
1177 | | - | |
1178 | | - | |
| 1181 | + | |
| 1182 | + | |
| 1183 | + | |
1179 | 1184 | | |
1180 | 1185 | | |
1181 | 1186 | | |
| |||
1827 | 1832 | | |
1828 | 1833 | | |
1829 | 1834 | | |
1830 | | - | |
1831 | | - | |
1832 | | - | |
| 1835 | + | |
| 1836 | + | |
| 1837 | + | |
1833 | 1838 | | |
1834 | | - | |
1835 | | - | |
1836 | | - | |
1837 | | - | |
| 1839 | + | |
1838 | 1840 | | |
1839 | 1841 | | |
1840 | 1842 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3508 | 3508 | | |
3509 | 3509 | | |
3510 | 3510 | | |
3511 | | - | |
| 3511 | + | |
3512 | 3512 | | |
3513 | 3513 | | |
3514 | 3514 | | |
| |||
3519 | 3519 | | |
3520 | 3520 | | |
3521 | 3521 | | |
3522 | | - | |
| 3522 | + | |
| 3523 | + | |
3523 | 3524 | | |
3524 | 3525 | | |
3525 | 3526 | | |
3526 | 3527 | | |
3527 | 3528 | | |
3528 | 3529 | | |
3529 | | - | |
3530 | | - | |
| 3530 | + | |
| 3531 | + | |
3531 | 3532 | | |
3532 | 3533 | | |
3533 | 3534 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2671 | 2671 | | |
2672 | 2672 | | |
2673 | 2673 | | |
2674 | | - | |
| 2674 | + | |
| 2675 | + | |
2675 | 2676 | | |
2676 | 2677 | | |
2677 | 2678 | | |
2678 | 2679 | | |
2679 | 2680 | | |
2680 | 2681 | | |
2681 | | - | |
| 2682 | + | |
2682 | 2683 | | |
2683 | 2684 | | |
2684 | 2685 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
159 | 159 | | |
160 | 160 | | |
161 | 161 | | |
| 162 | + | |
| 163 | + | |
162 | 164 | | |
163 | 165 | | |
164 | 166 | | |
| |||
390 | 392 | | |
391 | 393 | | |
392 | 394 | | |
393 | | - | |
| 395 | + | |
| 396 | + | |
394 | 397 | | |
395 | 398 | | |
396 | 399 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
228 | 228 | | |
229 | 229 | | |
230 | 230 | | |
231 | | - | |
| 231 | + | |
| 232 | + | |
232 | 233 | | |
233 | 234 | | |
234 | 235 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
385 | 385 | | |
386 | 386 | | |
387 | 387 | | |
388 | | - | |
| 388 | + | |
389 | 389 | | |
390 | | - | |
| 390 | + | |
| 391 | + | |
391 | 392 | | |
392 | 393 | | |
393 | 394 | | |
| |||
0 commit comments