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