guido 0.12.0

A reactive Rust GUI library using wgpu for Wayland layer shell widgets
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
//! Draw command definitions for the render tree.

use super::text_measurer::LineFit;
use super::types::{Gradient, Shadow};
use crate::image_decode::DecodedImage;
use crate::widgets::font::{FontFamily, FontWeight, LineHeight};
use crate::widgets::image::{ContentFit, ImageSource};
use crate::widgets::text_style::TextStroke;
use crate::widgets::{Color, Rect, TextAlign};

/// Border definition for shapes.
#[derive(Debug, Clone, Copy)]
pub struct Border {
    /// Border width in logical pixels
    pub width: f32,
    /// Border color
    pub color: Color,
}

impl Border {
    /// Create a new border.
    pub fn new(width: f32, color: Color) -> Self {
        Self { width, color }
    }
}

/// Per-corner radii for rounded rectangles (logical pixels).
///
/// Most shapes use a uniform radius — `f32` converts directly. Per-corner
/// values enable accordion-style lists where the first row rounds only its
/// top corners and the last only its bottom ones.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct CornerRadii {
    pub top_left: f32,
    pub top_right: f32,
    pub bottom_right: f32,
    pub bottom_left: f32,
}

impl CornerRadii {
    /// These radii after `permutation` has moved each corner.
    ///
    /// `permutation[i]` says where corner `i` lands, so the radius that was at
    /// `i` is the radius at `permutation[i]` afterwards — see
    /// [`Transform::corner_permutation`](crate::transform::Transform::corner_permutation),
    /// which is the only thing that produces one.
    ///
    /// Four numbers in a fixed order is what made a quarter turn between two
    /// clips unanswerable until #397: the rect survived the turn and the
    /// corners had nowhere to go.
    pub fn permuted(&self, permutation: [usize; 4]) -> Self {
        let from = self.to_array();
        let mut to = [0.0; 4];
        for (i, &dest) in permutation.iter().enumerate() {
            to[dest] = from[i];
        }
        Self {
            top_left: to[0],
            top_right: to[1],
            bottom_right: to[2],
            bottom_left: to[3],
        }
    }

    /// The same radius on all four corners.
    pub fn uniform(radius: f32) -> Self {
        Self {
            top_left: radius,
            top_right: radius,
            bottom_right: radius,
            bottom_left: radius,
        }
    }

    /// `radius` on the top corners, zero on the bottom ones.
    pub fn top(radius: f32) -> Self {
        Self {
            top_left: radius,
            top_right: radius,
            bottom_right: 0.0,
            bottom_left: 0.0,
        }
    }

    /// `radius` on the bottom corners, zero on the top ones.
    pub fn bottom(radius: f32) -> Self {
        Self {
            top_left: 0.0,
            top_right: 0.0,
            bottom_right: radius,
            bottom_left: radius,
        }
    }

    /// The largest of the four radii.
    pub fn max(&self) -> f32 {
        self.top_left
            .max(self.top_right)
            .max(self.bottom_right)
            .max(self.bottom_left)
    }

    /// As `[top_left, top_right, bottom_right, bottom_left]`.
    pub fn to_array(self) -> [f32; 4] {
        [
            self.top_left,
            self.top_right,
            self.bottom_right,
            self.bottom_left,
        ]
    }

    /// Multiply every radius (logical → physical pixels).
    pub fn scaled(self, factor: f32) -> Self {
        Self {
            top_left: self.top_left * factor,
            top_right: self.top_right * factor,
            bottom_right: self.bottom_right * factor,
            bottom_left: self.bottom_left * factor,
        }
    }
}

/// A single radius on all four corners.
impl From<f32> for CornerRadii {
    fn from(radius: f32) -> Self {
        Self::uniform(radius)
    }
}

impl From<f64> for CornerRadii {
    fn from(radius: f64) -> Self {
        Self::uniform(radius as f32)
    }
}

impl From<i32> for CornerRadii {
    fn from(radius: i32) -> Self {
        Self::uniform(radius as f32)
    }
}

impl From<u32> for CornerRadii {
    fn from(radius: u32) -> Self {
        Self::uniform(radius as f32)
    }
}

impl From<u16> for CornerRadii {
    fn from(radius: u16) -> Self {
        Self::uniform(radius as f32)
    }
}

/// `[top, bottom]` — the top pair and the bottom pair.
///
/// CSS pairs its two-value shorthand diagonally (`top-left & bottom-right`,
/// then the other two), which is a curiosity almost nobody writes on purpose;
/// what people write is "round the top of this row". It deliberately does not
/// mirror `padding([a, b])`, which pairs *opposite sides* — corners have no
/// opposite pairs to give. The four-value form below *is* CSS.
impl From<[f32; 2]> for CornerRadii {
    fn from(v: [f32; 2]) -> Self {
        Self {
            top_left: v[0],
            top_right: v[0],
            bottom_right: v[1],
            bottom_left: v[1],
        }
    }
}

/// `[top-left, top-right, bottom-right, bottom-left]` — clockwise, as CSS.
impl From<[f32; 4]> for CornerRadii {
    fn from(v: [f32; 4]) -> Self {
        Self {
            top_left: v[0],
            top_right: v[1],
            bottom_right: v[2],
            bottom_left: v[3],
        }
    }
}

/// `[top, bottom]`, integer.
impl From<[i32; 2]> for CornerRadii {
    fn from(v: [i32; 2]) -> Self {
        [v[0] as f32, v[1] as f32].into()
    }
}

/// `[top-left, top-right, bottom-right, bottom-left]`, integer.
impl From<[i32; 4]> for CornerRadii {
    fn from(v: [i32; 4]) -> Self {
        [v[0] as f32, v[1] as f32, v[2] as f32, v[3] as f32].into()
    }
}

impl From<(f32, f32, f32, f32)> for CornerRadii {
    /// `(top_left, top_right, bottom_right, bottom_left)`.
    fn from(r: (f32, f32, f32, f32)) -> Self {
        Self {
            top_left: r.0,
            top_right: r.1,
            bottom_right: r.2,
            bottom_left: r.3,
        }
    }
}

/// A single draw operation in local coordinates.
///
/// All coordinates and sizes are in the node's local coordinate space.
/// World transforms are applied during tree flattening.
#[derive(Debug, Clone)]
pub enum DrawCommand {
    /// Draw a rounded rectangle with optional gradient, border, shadow.
    RoundedRect {
        /// Rectangle bounds in local coordinates
        rect: Rect,
        /// Fill color
        color: Color,
        /// Corner radii in logical pixels
        radius: CornerRadii,
        /// Superellipse curvature (K-value: 1.0 = circle, 2.0 = squircle)
        curvature: f32,
        /// Optional border
        border: Option<Border>,
        /// Optional shadow
        shadow: Option<Shadow>,
        /// Optional gradient (overrides solid color)
        gradient: Option<Gradient>,
    },

    /// Draw a circle (used for ripple effects).
    Circle {
        /// Center point in local coordinates
        center: (f32, f32),
        /// Radius in logical pixels
        radius: f32,
        /// Fill color
        color: Color,
    },

    /// Draw text.
    Text {
        /// The text string to render. Shared rather than owned: the renderer
        /// takes it into a [`TextEntry`](super::TextEntry) every frame the
        /// text is drawn, and a decorated text pushes it once per copy.
        text: std::rc::Rc<str>,
        /// The bounding rectangle for the text in local coordinates
        rect: Rect,
        /// The text color
        color: Color,
        /// The font size in logical pixels
        font_size: f32,
        /// The font family
        font_family: FontFamily,
        /// The font weight
        font_weight: FontWeight,
        /// How tall each line is
        line_height: LineHeight,
        /// Extra advance after every glyph, in logical pixels
        letter_spacing: f32,
        /// Where each line sits across `rect`.
        align: TextAlign,
        /// The lines it is cut to, when it is cut. `None` draws it whole in
        /// its box.
        fit: Option<LineFit>,
    },

    /// Where pointer and touch input reaches this surface, or stops reaching
    /// it. Draws nothing: it rides the frame so that the region published to
    /// the compositor is read off the same list the pixels came from, which is
    /// what keeps a hidden, culled or cached container from leaving a stale
    /// hole behind it.
    InputRegion {
        /// The area declared, in local coordinates.
        rect: Rect,
        /// Corner radii of that area, so the region follows the shape drawn
        /// rather than its bounding box.
        corner_radii: CornerRadii,
        /// Superellipse curvature of those corners. A bevel is smaller than
        /// the circle of the same radius, so a region cut as a circle would
        /// take clicks past the diagonal the container actually drew.
        curvature: f32,
        /// Whether input reaches this area. `false` is a hole in a surface
        /// that otherwise takes input; `true` is an island in one that does
        /// not.
        takes: bool,
    },
    /// Filter what has already been drawn beneath `rect`, in place.
    ///
    /// Ordered before the container's own background so the container paints
    /// over its own blurred backdrop. See [`crate::backdrop`].
    BackdropBlur {
        /// Region to filter, in local coordinates.
        rect: Rect,
        /// Which backdrops this asks for. The surface's own is filtered by the
        /// renderer; the compositor's is published as a `wl_region` collected
        /// off this very command after flattening — which is what keeps the two
        /// halves from disagreeing about whether a container still wants it.
        sources: crate::backdrop::BackdropSources,
        /// Blur radius in logical pixels.
        radius: f32,
        /// Corner radii of the region, so the result is masked to the
        /// container's shape rather than its bounding box.
        corner_radii: CornerRadii,
        /// Superellipse curvature of those corners.
        curvature: f32,
    },

    /// Filter what has already been drawn beneath the glyphs of `text`.
    ///
    /// The rectangular sibling above filters a box; this one filters the shape
    /// of the letters, which no formula describes — the renderer rasterizes
    /// them into a coverage mask. Ordered before the text itself, so the glyphs
    /// are painted over their own blurred backdrop.
    ///
    /// Everything needed to shape the mask is repeated here, because the mask
    /// has to come out identical to the text drawn after it.
    TextBackdropBlur {
        /// The text whose coverage is the mask.
        text: String,
        /// A contour to draw around that coverage, once the blur is composited
        /// and before the glyphs land on it. Carried here because the mask it
        /// needs is the one this command already rasterizes — and because the
        /// cheap stroke, copies of the glyphs under the fill, would fill the
        /// letter the frost just cut out.
        stroke: Option<TextStroke>,
        /// The text's layout box in local coordinates.
        rect: Rect,
        /// Blur radius in logical pixels.
        radius: f32,
        /// The font size in logical pixels.
        font_size: f32,
        /// The font family.
        font_family: FontFamily,
        /// The font weight.
        font_weight: FontWeight,
        /// How tall each line is.
        line_height: LineHeight,
        /// Extra advance after every glyph, in logical pixels.
        letter_spacing: f32,
        /// Where each line sits across `rect` — the frost has to put its
        /// lines where the letters put theirs.
        align: TextAlign,
        /// The lines the text it frosts is cut to, so the mask is cut there too.
        fit: Option<LineFit>,
    },

    /// Draw an image.
    Image {
        /// Image source (path or bytes)
        source: ImageSource,
        /// Where a raster `Path` or `Bytes` source's decoded pixels, or an
        /// SVG's rasters, wait, off the frame, until the renderer takes them
        /// to upload — after which it is empty and the texture is the image.
        /// The renderer never decodes or rasterizes on the render path; `None`
        /// for a source that needs neither.
        decoded: Option<DecodedImage>,
        /// Bounding rectangle in local coordinates
        rect: Rect,
        /// How the image content fits within the rect
        content_fit: ContentFit,
        /// The colour every pixel is drawn in, keeping its alpha. `None`
        /// draws the image's own colours.
        tint: Option<Color>,
    },
}

impl DrawCommand {
    /// Create a simple rounded rectangle.
    pub fn rounded_rect(rect: Rect, color: Color, radius: impl Into<CornerRadii>) -> Self {
        Self::RoundedRect {
            rect,
            color,
            radius: radius.into(),
            curvature: 1.0,
            border: None,
            shadow: None,
            gradient: None,
        }
    }

    /// Create a rounded rectangle with curvature.
    pub fn rounded_rect_with_curvature(
        rect: Rect,
        color: Color,
        radius: impl Into<CornerRadii>,
        curvature: f32,
    ) -> Self {
        Self::RoundedRect {
            rect,
            color,
            radius: radius.into(),
            curvature,
            border: None,
            shadow: None,
            gradient: None,
        }
    }

    /// Create a circle.
    pub fn circle(center: (f32, f32), radius: f32, color: Color) -> Self {
        Self::Circle {
            center,
            radius,
            color,
        }
    }
}