scene.rs

  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}