scene.rs

  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}