scene.rs

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