1mod mouse_event;
2mod mouse_region;
3
4#[cfg(debug_assertions)]
5use collections::HashSet;
6use schemars::JsonSchema;
7use serde::Deserialize;
8use serde_json::json;
9use std::{borrow::Cow, sync::Arc};
10
11use crate::{
12 color::Color,
13 fonts::{FontId, GlyphId},
14 geometry::{rect::RectF, vector::Vector2F},
15 json::ToJson,
16 platform::{current::Surface, CursorStyle},
17 ImageData,
18};
19pub use mouse_event::*;
20pub use mouse_region::*;
21
22pub struct SceneBuilder {
23 scale_factor: f32,
24 stacking_contexts: Vec<StackingContext>,
25 active_stacking_context_stack: Vec<usize>,
26 #[cfg(debug_assertions)]
27 mouse_region_ids: HashSet<MouseRegionId>,
28}
29
30pub struct Scene {
31 scale_factor: f32,
32 stacking_contexts: Vec<StackingContext>,
33}
34
35struct StackingContext {
36 layers: Vec<Layer>,
37 active_layer_stack: Vec<usize>,
38 z_index: usize,
39}
40
41#[derive(Default)]
42pub struct Layer {
43 clip_bounds: Option<RectF>,
44 quads: Vec<Quad>,
45 underlines: Vec<Underline>,
46 images: Vec<Image>,
47 surfaces: Vec<Surface>,
48 shadows: Vec<Shadow>,
49 glyphs: Vec<Glyph>,
50 image_glyphs: Vec<ImageGlyph>,
51 icons: Vec<Icon>,
52 paths: Vec<Path>,
53 cursor_regions: Vec<CursorRegion>,
54 mouse_regions: Vec<MouseRegion>,
55}
56
57#[derive(Copy, Clone)]
58pub struct CursorRegion {
59 pub bounds: RectF,
60 pub style: CursorStyle,
61}
62
63#[derive(Default, Debug)]
64pub struct Quad {
65 pub bounds: RectF,
66 pub background: Option<Color>,
67 pub border: Border,
68 pub corner_radius: f32,
69}
70
71#[derive(Debug)]
72pub struct Shadow {
73 pub bounds: RectF,
74 pub corner_radius: f32,
75 pub sigma: f32,
76 pub color: Color,
77}
78
79#[derive(Debug, Clone, Copy)]
80pub struct Glyph {
81 pub font_id: FontId,
82 pub font_size: f32,
83 pub id: GlyphId,
84 pub origin: Vector2F,
85 pub color: Color,
86}
87
88#[derive(Debug)]
89pub struct ImageGlyph {
90 pub font_id: FontId,
91 pub font_size: f32,
92 pub id: GlyphId,
93 pub origin: Vector2F,
94}
95
96pub struct Icon {
97 pub bounds: RectF,
98 pub svg: usvg::Tree,
99 pub path: Cow<'static, str>,
100 pub color: Color,
101}
102
103#[derive(Clone, Copy, Default, Debug, JsonSchema)]
104pub struct Border {
105 pub width: f32,
106 pub color: Color,
107 pub overlay: bool,
108 pub top: bool,
109 pub right: bool,
110 pub bottom: bool,
111 pub left: bool,
112}
113
114#[derive(Clone, Copy, Default, Debug)]
115pub struct Underline {
116 pub origin: Vector2F,
117 pub width: f32,
118 pub thickness: f32,
119 pub color: Color,
120 pub squiggly: bool,
121}
122
123impl<'de> Deserialize<'de> for Border {
124 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
125 where
126 D: serde::Deserializer<'de>,
127 {
128 #[derive(Deserialize)]
129 struct BorderData {
130 pub width: f32,
131 pub color: Color,
132 #[serde(default)]
133 pub overlay: bool,
134 #[serde(default)]
135 pub top: bool,
136 #[serde(default)]
137 pub right: bool,
138 #[serde(default)]
139 pub bottom: bool,
140 #[serde(default)]
141 pub left: bool,
142 }
143
144 let data = BorderData::deserialize(deserializer)?;
145 let mut border = Border {
146 width: data.width,
147 color: data.color,
148 overlay: data.overlay,
149 top: data.top,
150 bottom: data.bottom,
151 left: data.left,
152 right: data.right,
153 };
154 if !border.top && !border.bottom && !border.left && !border.right {
155 border.top = true;
156 border.bottom = true;
157 border.left = true;
158 border.right = true;
159 }
160 Ok(border)
161 }
162}
163
164#[derive(Debug)]
165pub struct Path {
166 pub bounds: RectF,
167 pub color: Color,
168 pub vertices: Vec<PathVertex>,
169}
170
171#[derive(Debug)]
172pub struct PathVertex {
173 pub xy_position: Vector2F,
174 pub st_position: Vector2F,
175}
176
177pub struct Image {
178 pub bounds: RectF,
179 pub border: Border,
180 pub corner_radius: f32,
181 pub grayscale: bool,
182 pub data: Arc<ImageData>,
183}
184
185impl Scene {
186 pub fn scale_factor(&self) -> f32 {
187 self.scale_factor
188 }
189
190 pub fn layers(&self) -> impl Iterator<Item = &Layer> {
191 self.stacking_contexts.iter().flat_map(|s| &s.layers)
192 }
193
194 pub fn cursor_regions(&self) -> Vec<CursorRegion> {
195 self.layers()
196 .flat_map(|layer| &layer.cursor_regions)
197 .copied()
198 .collect()
199 }
200
201 pub fn mouse_regions(&self) -> Vec<(MouseRegion, usize)> {
202 self.stacking_contexts
203 .iter()
204 .flat_map(|context| {
205 context
206 .layers
207 .iter()
208 .flat_map(|layer| &layer.mouse_regions)
209 .map(|region| (region.clone(), context.z_index))
210 })
211 .collect()
212 }
213}
214
215impl SceneBuilder {
216 pub fn new(scale_factor: f32) -> Self {
217 let stacking_context = StackingContext::new(None, 0);
218 SceneBuilder {
219 scale_factor,
220 stacking_contexts: vec![stacking_context],
221 active_stacking_context_stack: vec![0],
222 #[cfg(debug_assertions)]
223 mouse_region_ids: Default::default(),
224 }
225 }
226
227 pub fn build(mut self) -> Scene {
228 self.stacking_contexts
229 .sort_by_key(|context| context.z_index);
230 Scene {
231 scale_factor: self.scale_factor,
232 stacking_contexts: self.stacking_contexts,
233 }
234 }
235
236 pub fn scale_factor(&self) -> f32 {
237 self.scale_factor
238 }
239
240 pub fn paint_stacking_context<F>(
241 &mut self,
242 clip_bounds: Option<RectF>,
243 z_index: Option<usize>,
244 f: F,
245 ) where
246 F: FnOnce(&mut Self),
247 {
248 self.push_stacking_context(clip_bounds, z_index);
249 f(self);
250 self.pop_stacking_context();
251 }
252
253 pub fn push_stacking_context(&mut self, clip_bounds: Option<RectF>, z_index: Option<usize>) {
254 let z_index = z_index.unwrap_or_else(|| self.active_stacking_context().z_index + 1);
255 self.active_stacking_context_stack
256 .push(self.stacking_contexts.len());
257 self.stacking_contexts
258 .push(StackingContext::new(clip_bounds, z_index))
259 }
260
261 pub fn pop_stacking_context(&mut self) {
262 self.active_stacking_context_stack.pop();
263 assert!(!self.active_stacking_context_stack.is_empty());
264 }
265
266 pub fn paint_layer<F>(&mut self, clip_bounds: Option<RectF>, f: F)
267 where
268 F: FnOnce(&mut Self),
269 {
270 self.push_layer(clip_bounds);
271 f(self);
272 self.pop_layer();
273 }
274
275 pub fn push_layer(&mut self, clip_bounds: Option<RectF>) {
276 self.active_stacking_context().push_layer(clip_bounds);
277 }
278
279 pub fn pop_layer(&mut self) {
280 self.active_stacking_context().pop_layer();
281 }
282
283 pub fn push_quad(&mut self, quad: Quad) {
284 self.active_layer().push_quad(quad)
285 }
286
287 pub fn push_cursor_region(&mut self, region: CursorRegion) {
288 if can_draw(region.bounds) {
289 self.active_layer().push_cursor_region(region);
290 }
291 }
292
293 pub fn push_mouse_region(&mut self, region: MouseRegion) {
294 if can_draw(region.bounds) {
295 // Ensure that Regions cannot be added to a scene with the same region id.
296 #[cfg(debug_assertions)]
297 let region_id;
298 #[cfg(debug_assertions)]
299 {
300 region_id = region.id();
301 }
302
303 if self.active_layer().push_mouse_region(region) {
304 #[cfg(debug_assertions)]
305 {
306 if !self.mouse_region_ids.insert(region_id) {
307 let tag_name = region_id.tag_type_name();
308 panic!("Same MouseRegionId: {region_id:?} inserted multiple times to the same scene. \
309 Will cause problems! Look for MouseRegion that uses Tag: {tag_name}");
310 }
311 }
312 }
313 }
314 }
315
316 pub fn push_image(&mut self, image: Image) {
317 self.active_layer().push_image(image)
318 }
319
320 pub fn push_surface(&mut self, surface: Surface) {
321 self.active_layer().push_surface(surface)
322 }
323
324 pub fn push_underline(&mut self, underline: Underline) {
325 self.active_layer().push_underline(underline)
326 }
327
328 pub fn push_shadow(&mut self, shadow: Shadow) {
329 self.active_layer().push_shadow(shadow)
330 }
331
332 pub fn push_glyph(&mut self, glyph: Glyph) {
333 self.active_layer().push_glyph(glyph)
334 }
335
336 pub fn push_image_glyph(&mut self, image_glyph: ImageGlyph) {
337 self.active_layer().push_image_glyph(image_glyph)
338 }
339
340 pub fn push_icon(&mut self, icon: Icon) {
341 self.active_layer().push_icon(icon)
342 }
343
344 pub fn push_path(&mut self, path: Path) {
345 self.active_layer().push_path(path);
346 }
347
348 fn active_stacking_context(&mut self) -> &mut StackingContext {
349 let ix = *self.active_stacking_context_stack.last().unwrap();
350 &mut self.stacking_contexts[ix]
351 }
352
353 fn active_layer(&mut self) -> &mut Layer {
354 self.active_stacking_context().active_layer()
355 }
356}
357
358impl StackingContext {
359 fn new(clip_bounds: Option<RectF>, z_index: usize) -> Self {
360 Self {
361 layers: vec![Layer::new(clip_bounds)],
362 active_layer_stack: vec![0],
363 z_index,
364 }
365 }
366
367 fn active_layer(&mut self) -> &mut Layer {
368 &mut self.layers[*self.active_layer_stack.last().unwrap()]
369 }
370
371 fn push_layer(&mut self, clip_bounds: Option<RectF>) {
372 let parent_clip_bounds = self.active_layer().clip_bounds();
373 let clip_bounds = clip_bounds
374 .map(|clip_bounds| {
375 clip_bounds
376 .intersection(parent_clip_bounds.unwrap_or(clip_bounds))
377 .unwrap_or_else(|| {
378 if !clip_bounds.is_empty() {
379 log::warn!("specified clip bounds are disjoint from parent layer");
380 }
381 RectF::default()
382 })
383 })
384 .or(parent_clip_bounds);
385
386 let ix = self.layers.len();
387 self.layers.push(Layer::new(clip_bounds));
388 self.active_layer_stack.push(ix);
389 }
390
391 fn pop_layer(&mut self) {
392 self.active_layer_stack.pop().unwrap();
393 assert!(!self.active_layer_stack.is_empty());
394 }
395}
396
397impl Layer {
398 pub fn new(clip_bounds: Option<RectF>) -> Self {
399 Self {
400 clip_bounds,
401 quads: Default::default(),
402 underlines: Default::default(),
403 images: Default::default(),
404 surfaces: Default::default(),
405 shadows: Default::default(),
406 image_glyphs: Default::default(),
407 glyphs: Default::default(),
408 icons: Default::default(),
409 paths: Default::default(),
410 cursor_regions: Default::default(),
411 mouse_regions: Default::default(),
412 }
413 }
414
415 pub fn clip_bounds(&self) -> Option<RectF> {
416 self.clip_bounds
417 }
418
419 fn push_quad(&mut self, quad: Quad) {
420 if can_draw(quad.bounds) {
421 self.quads.push(quad);
422 }
423 }
424
425 pub fn quads(&self) -> &[Quad] {
426 self.quads.as_slice()
427 }
428
429 fn push_cursor_region(&mut self, region: CursorRegion) {
430 if let Some(bounds) = region
431 .bounds
432 .intersection(self.clip_bounds.unwrap_or(region.bounds))
433 {
434 if can_draw(bounds) {
435 self.cursor_regions.push(region);
436 }
437 }
438 }
439
440 fn push_mouse_region(&mut self, region: MouseRegion) -> bool {
441 if let Some(bounds) = region
442 .bounds
443 .intersection(self.clip_bounds.unwrap_or(region.bounds))
444 {
445 if can_draw(bounds) {
446 self.mouse_regions.push(region);
447 return true;
448 }
449 }
450 false
451 }
452
453 fn push_underline(&mut self, underline: Underline) {
454 if underline.width > 0. {
455 self.underlines.push(underline);
456 }
457 }
458
459 pub fn underlines(&self) -> &[Underline] {
460 self.underlines.as_slice()
461 }
462
463 fn push_image(&mut self, image: Image) {
464 if can_draw(image.bounds) {
465 self.images.push(image);
466 }
467 }
468
469 pub fn images(&self) -> &[Image] {
470 self.images.as_slice()
471 }
472
473 fn push_surface(&mut self, surface: Surface) {
474 if can_draw(surface.bounds) {
475 self.surfaces.push(surface);
476 }
477 }
478
479 pub fn surfaces(&self) -> &[Surface] {
480 self.surfaces.as_slice()
481 }
482
483 fn push_shadow(&mut self, shadow: Shadow) {
484 if can_draw(shadow.bounds) {
485 self.shadows.push(shadow);
486 }
487 }
488
489 pub fn shadows(&self) -> &[Shadow] {
490 self.shadows.as_slice()
491 }
492
493 fn push_image_glyph(&mut self, glyph: ImageGlyph) {
494 self.image_glyphs.push(glyph);
495 }
496
497 pub fn image_glyphs(&self) -> &[ImageGlyph] {
498 self.image_glyphs.as_slice()
499 }
500
501 fn push_glyph(&mut self, glyph: Glyph) {
502 self.glyphs.push(glyph);
503 }
504
505 pub fn glyphs(&self) -> &[Glyph] {
506 self.glyphs.as_slice()
507 }
508
509 pub fn push_icon(&mut self, icon: Icon) {
510 if can_draw(icon.bounds) {
511 self.icons.push(icon);
512 }
513 }
514
515 pub fn icons(&self) -> &[Icon] {
516 self.icons.as_slice()
517 }
518
519 fn push_path(&mut self, path: Path) {
520 if can_draw(path.bounds) {
521 self.paths.push(path);
522 }
523 }
524
525 pub fn paths(&self) -> &[Path] {
526 self.paths.as_slice()
527 }
528}
529
530impl Border {
531 pub fn new(width: f32, color: Color) -> Self {
532 Self {
533 width,
534 color,
535 overlay: false,
536 top: false,
537 left: false,
538 bottom: false,
539 right: false,
540 }
541 }
542
543 pub fn all(width: f32, color: Color) -> Self {
544 Self {
545 width,
546 color,
547 overlay: false,
548 top: true,
549 left: true,
550 bottom: true,
551 right: true,
552 }
553 }
554
555 pub fn top(width: f32, color: Color) -> Self {
556 let mut border = Self::new(width, color);
557 border.top = true;
558 border
559 }
560
561 pub fn left(width: f32, color: Color) -> Self {
562 let mut border = Self::new(width, color);
563 border.left = true;
564 border
565 }
566
567 pub fn bottom(width: f32, color: Color) -> Self {
568 let mut border = Self::new(width, color);
569 border.bottom = true;
570 border
571 }
572
573 pub fn right(width: f32, color: Color) -> Self {
574 let mut border = Self::new(width, color);
575 border.right = true;
576 border
577 }
578
579 pub fn with_sides(mut self, top: bool, left: bool, bottom: bool, right: bool) -> Self {
580 self.top = top;
581 self.left = left;
582 self.bottom = bottom;
583 self.right = right;
584 self
585 }
586
587 pub fn top_width(&self) -> f32 {
588 if self.top {
589 self.width
590 } else {
591 0.0
592 }
593 }
594
595 pub fn left_width(&self) -> f32 {
596 if self.left {
597 self.width
598 } else {
599 0.0
600 }
601 }
602}
603
604impl ToJson for Border {
605 fn to_json(&self) -> serde_json::Value {
606 let mut value = json!({});
607 if self.top {
608 value["top"] = json!(self.width);
609 }
610 if self.right {
611 value["right"] = json!(self.width);
612 }
613 if self.bottom {
614 value["bottom"] = json!(self.width);
615 }
616 if self.left {
617 value["left"] = json!(self.width);
618 }
619 value
620 }
621}
622
623impl MouseRegion {
624 pub fn id(&self) -> MouseRegionId {
625 self.id
626 }
627}
628
629fn can_draw(bounds: RectF) -> bool {
630 let size = bounds.size();
631 size.x() > 0. && size.y() > 0.
632}