1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4use i_slint_core::{graphics::GradientStop, Brush, Color};
5use napi::{bindgen_prelude::External, Error, Result};
6
7/// RgbaColor represents a color in the Slint run-time, represented using 8-bit channels for red, green, blue and the alpha (opacity).
8#[napi(object)]
9pub struct RgbaColor {
10 /// Represents the red channel of the color as u8 in the range 0..255.
11 pub red: f64,
12
13 /// Represents the green channel of the color as u8 in the range 0..255.
14 pub green: f64,
15
16 /// Represents the blue channel of the color as u8 in the range 0..255.
17 pub blue: f64,
18
19 /// Represents the alpha channel of the color as u8 in the range 0..255.
20 pub alpha: Option<f64>,
21}
22
23impl Default for RgbaColor {
24 fn default() -> Self {
25 Self { red: 0., green: 0., blue: 0., alpha: None }
26 }
27}
28
29// no public api only available internal because in js/ts it's exported as interface
30impl RgbaColor {
31 pub fn red(&self) -> f64 {
32 self.red
33 }
34
35 pub fn green(&self) -> f64 {
36 self.green
37 }
38
39 pub fn blue(&self) -> f64 {
40 self.blue
41 }
42
43 pub fn alpha(&self) -> f64 {
44 self.alpha.unwrap_or(default:255.)
45 }
46}
47
48/// SlintRgbaColor implements {@link RgbaColor}.
49#[napi]
50pub struct SlintRgbaColor {
51 inner: Color,
52}
53
54impl From<Color> for SlintRgbaColor {
55 fn from(color: Color) -> Self {
56 Self { inner: color }
57 }
58}
59
60impl From<SlintRgbaColor> for RgbaColor {
61 fn from(color: SlintRgbaColor) -> Self {
62 Self {
63 red: color.red() as f64,
64 green: color.green() as f64,
65 blue: color.blue() as f64,
66 alpha: Some(color.alpha() as f64),
67 }
68 }
69}
70
71#[napi]
72impl SlintRgbaColor {
73 /// Creates a new transparent color.
74 #[napi(constructor)]
75 pub fn new() -> Self {
76 Self { inner: Color::default() }
77 }
78
79 /// Construct a color from the red, green and blue color channel parameters. The alpha
80 /// channel will have the value 255.
81 #[napi(factory)]
82 pub fn from_rgb(red: u8, green: u8, blue: u8) -> Self {
83 Self { inner: Color::from_rgb_u8(red, green, blue) }
84 }
85
86 /// Construct a color from the alpha, red, green and blue color channel parameters.
87 #[napi(factory)]
88 pub fn from_argb(alpha: u8, red: u8, green: u8, blue: u8) -> Self {
89 Self { inner: Color::from_argb_u8(alpha, red, green, blue) }
90 }
91
92 /// Returns the red channel of the color as number in the range 0..255.
93 #[napi(getter)]
94 pub fn red(&self) -> u8 {
95 self.inner.red()
96 }
97
98 /// Returns the green channel of the color as number in the range 0..255.
99 #[napi(getter)]
100 pub fn green(&self) -> u8 {
101 self.inner.green()
102 }
103
104 /// Returns the blue channel of the color as number in the range 0..255.
105 #[napi(getter)]
106 pub fn blue(&self) -> u8 {
107 self.inner.blue()
108 }
109
110 /// Returns the alpha channel of the color as number in the range 0..255.
111 #[napi(getter)]
112 pub fn alpha(&self) -> u8 {
113 self.inner.alpha()
114 }
115
116 // Returns a new version of this color that has the brightness increased
117 /// by the specified factor. This is done by converting the color to the HSV
118 /// color space and multiplying the brightness (value) with (1 + factor).
119 /// The result is converted back to RGB and the alpha channel is unchanged.
120 /// So for example `brighter(0.2)` will increase the brightness by 20%, and
121 /// calling `brighter(-0.5)` will return a color that's 50% darker.
122 #[napi]
123 pub fn brighter(&self, factor: f64) -> SlintRgbaColor {
124 SlintRgbaColor::from(self.inner.brighter(factor as f32))
125 }
126
127 /// Returns a new version of this color that has the brightness decreased
128 /// by the specified factor. This is done by converting the color to the HSV
129 /// color space and dividing the brightness (value) by (1 + factor). The
130 /// result is converted back to RGB and the alpha channel is unchanged.
131 /// So for example `darker(0.3)` will decrease the brightness by 30%.
132 #[napi]
133 pub fn darker(&self, factor: f64) -> SlintRgbaColor {
134 SlintRgbaColor::from(self.inner.darker(factor as f32))
135 }
136
137 /// Returns a new version of this color with the opacity decreased by `factor`.
138 ///
139 /// The transparency is obtained by multiplying the alpha channel by `(1 - factor)`.
140 #[napi]
141 pub fn transparentize(&self, amount: f64) -> SlintRgbaColor {
142 SlintRgbaColor::from(self.inner.transparentize(amount as f32))
143 }
144
145 /// Returns a new color that is a mix of `this` color and `other`. The specified factor is
146 /// clamped to be between `0.0` and `1.0` and then applied to `this` color, while `1.0 - factor`
147 ///is applied to `other`.
148 #[napi]
149 pub fn mix(&self, other: &SlintRgbaColor, factor: f64) -> SlintRgbaColor {
150 SlintRgbaColor::from(self.inner.mix(&other.inner, factor as f32))
151 }
152
153 /// Returns a new version of this color with the opacity set to `alpha`.
154 #[napi]
155 pub fn with_alpha(&self, alpha: f64) -> SlintRgbaColor {
156 SlintRgbaColor::from(self.inner.with_alpha(alpha as f32))
157 }
158
159 /// Returns the color as string in hex representation e.g. `#000000` for black.
160 #[napi]
161 pub fn to_string(&self) -> String {
162 format!("#{:02x}{:02x}{:02x}{:02x}", self.red(), self.green(), self.blue(), self.alpha())
163 }
164}
165
166/// A brush is a data structure that is used to describe how
167/// a shape, such as a rectangle, path or even text, shall be filled.
168/// A brush can also be applied to the outline of a shape, that means
169/// the fill of the outline itself.
170#[napi(object, js_name = "Brush")]
171pub struct JsBrush {
172 /// Defines a solid color brush from rgba.
173 ///
174 /// If no color is set it defaults to transparent.
175 pub color: Option<RgbaColor>,
176}
177
178/// SlintBrush implements {@link Brush}.
179#[napi]
180pub struct SlintBrush {
181 inner: Brush,
182}
183
184impl From<Brush> for SlintBrush {
185 fn from(brush: Brush) -> Self {
186 Self { inner: brush }
187 }
188}
189
190impl From<SlintRgbaColor> for SlintBrush {
191 fn from(color: SlintRgbaColor) -> Self {
192 Self::from(Brush::from(color.inner))
193 }
194}
195
196#[napi]
197impl SlintBrush {
198 #[napi(constructor)]
199 pub fn new_with_color(color: RgbaColor) -> Result<Self> {
200 if color.red() < 0. || color.green() < 0. || color.blue() < 0. || color.alpha() < 0. {
201 return Err(Error::from_reason("A channel of Color cannot be negative"));
202 }
203
204 Ok(Self {
205 inner: Brush::SolidColor(Color::from_argb_u8(
206 color.alpha().floor() as u8,
207 color.red().floor() as u8,
208 color.green().floor() as u8,
209 color.blue().floor() as u8,
210 )),
211 })
212 }
213
214 #[napi(factory)]
215 pub fn from_brush(brush: JsBrush) -> Result<Self> {
216 SlintBrush::new_with_color(brush.color.unwrap_or_default())
217 }
218
219 /// Creates a brush form a `Color`.
220 pub fn from_slint_color(color: &SlintRgbaColor) -> Self {
221 Self { inner: Brush::SolidColor(color.inner) }
222 }
223
224 #[napi(getter)]
225 pub fn color(&self) -> RgbaColor {
226 self.slint_color().into()
227 }
228
229 /// @hidden
230 #[napi(getter)]
231 pub fn slint_color(&self) -> SlintRgbaColor {
232 self.inner.color().into()
233 }
234
235 /// Returns true if this brush contains a fully transparent color (alpha value is zero)
236 #[napi(getter)]
237 pub fn is_transparent(&self) -> bool {
238 self.inner.is_transparent()
239 }
240
241 /// Returns true if this brush is fully opaque.
242 #[napi(getter)]
243 pub fn is_opaque(&self) -> bool {
244 self.inner.is_opaque()
245 }
246
247 /// Returns a new version of this brush that has the brightness increased
248 /// by the specified factor. This is done by calling [`Color::brighter`] on
249 /// all the colors of this brush.
250 #[napi]
251 pub fn brighter(&self, factor: f64) -> SlintBrush {
252 SlintBrush::from(self.inner.brighter(factor as f32))
253 }
254
255 /// Returns a new version of this brush that has the brightness decreased
256 /// by the specified factor. This is done by calling [`Color::darker`] on
257 /// all the color of this brush.
258 #[napi]
259 pub fn darker(&self, factor: f64) -> SlintBrush {
260 SlintBrush::from(self.inner.darker(factor as f32))
261 }
262
263 /// Returns a new version of this brush with the opacity decreased by `factor`.
264 ///
265 /// The transparency is obtained by multiplying the alpha channel by `(1 - factor)`.
266 #[napi]
267 pub fn transparentize(&self, amount: f64) -> SlintBrush {
268 SlintBrush::from(self.inner.transparentize(amount as f32))
269 }
270
271 /// Returns a new version of this brush with the related color's opacities
272 /// set to `alpha`.
273 #[napi]
274 pub fn with_alpha(&self, alpha: f64) -> SlintBrush {
275 SlintBrush::from(self.inner.with_alpha(alpha as f32))
276 }
277
278 /// @hidden
279 #[napi(getter)]
280 pub fn brush(&self) -> External<Brush> {
281 External::new(self.inner.clone())
282 }
283
284 /// Returns the color as string in hex representation e.g. `#000000` for black.
285 /// It is only implemented for solid color brushes.
286 #[napi]
287 pub fn to_string(&self) -> String {
288 match &self.inner {
289 Brush::SolidColor(_) => self.slint_color().to_string(),
290 Brush::LinearGradient(gradient) => {
291 format!(
292 "linear-gradient({}deg, {})",
293 gradient.angle(),
294 gradient_stops_to_string(gradient.stops())
295 )
296 }
297 Brush::RadialGradient(gradient) => {
298 format!("radial-gradient(circle, {})", gradient_stops_to_string(gradient.stops()))
299 }
300 _ => String::default(),
301 }
302 }
303}
304
305fn gradient_stops_to_string<'a>(stops: impl Iterator<Item = &'a GradientStop>) -> String {
306 let stops: Vec<String> = stops
307 .map(|s| {
308 format!(
309 "rgba({}, {}, {}, {}) {}%",
310 s.color.red(),
311 s.color.green(),
312 s.color.blue(),
313 s.color.alpha(),
314 s.position * 100.
315 )
316 })
317 .collect();
318
319 let mut stops_string = String::default();
320 let len = stops.len();
321
322 for i in 0..len {
323 stops_string.push_str(stops[i].as_str());
324
325 if i < len - 1 {
326 stops_string.push_str(", ");
327 }
328 }
329
330 stops_string
331}
332

Provided by KDAB

Privacy Policy
Learn Rust with the experts
Find out more