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