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 grayscale: bool,
176    pub data: Arc<ImageData>,
177}
178
179impl Scene {
180    pub fn new(scale_factor: f32) -> Self {
181        let stacking_context = StackingContext::new(0, None);
182        Scene {
183            scale_factor,
184            stacking_contexts: vec![stacking_context],
185            active_stacking_context_stack: vec![0],
186            #[cfg(debug_assertions)]
187            mouse_region_ids: Default::default(),
188        }
189    }
190
191    pub fn scale_factor(&self) -> f32 {
192        self.scale_factor
193    }
194
195    pub fn layers(&self) -> impl Iterator<Item = &Layer> {
196        self.stacking_contexts.iter().flat_map(|s| &s.layers)
197    }
198
199    pub fn cursor_regions(&self) -> Vec<CursorRegion> {
200        self.layers()
201            .flat_map(|layer| &layer.cursor_regions)
202            .copied()
203            .collect()
204    }
205
206    pub fn mouse_regions(&self) -> Vec<(MouseRegion, usize)> {
207        let mut regions = Vec::new();
208        for stacking_context in self.stacking_contexts.iter() {
209            for layer in &stacking_context.layers {
210                for mouse_region in &layer.mouse_regions {
211                    regions.push((mouse_region.clone(), stacking_context.depth));
212                }
213            }
214        }
215        regions.sort_by_key(|(_, depth)| *depth);
216        regions
217    }
218
219    pub fn push_stacking_context(&mut self, clip_bounds: Option<RectF>) {
220        let depth = self.active_stacking_context().depth + 1;
221        self.active_stacking_context_stack
222            .push(self.stacking_contexts.len());
223        self.stacking_contexts
224            .push(StackingContext::new(depth, clip_bounds))
225    }
226
227    pub fn pop_stacking_context(&mut self) {
228        self.active_stacking_context_stack.pop();
229        assert!(!self.active_stacking_context_stack.is_empty());
230    }
231
232    pub fn push_layer(&mut self, clip_bounds: Option<RectF>) {
233        self.active_stacking_context().push_layer(clip_bounds);
234    }
235
236    pub fn pop_layer(&mut self) {
237        self.active_stacking_context().pop_layer();
238    }
239
240    pub fn push_quad(&mut self, quad: Quad) {
241        self.active_layer().push_quad(quad)
242    }
243
244    pub fn push_cursor_region(&mut self, region: CursorRegion) {
245        if can_draw(region.bounds) {
246            self.active_layer().push_cursor_region(region);
247        }
248    }
249
250    pub fn push_mouse_region(&mut self, region: MouseRegion) {
251        if can_draw(region.bounds) {
252            // Ensure that Regions cannot be added to a scene with the same region id.
253            #[cfg(debug_assertions)]
254            let region_id;
255            #[cfg(debug_assertions)]
256            {
257                region_id = region.id();
258            }
259
260            if self.active_layer().push_mouse_region(region) {
261                #[cfg(debug_assertions)]
262                {
263                    if !self.mouse_region_ids.insert(region_id) {
264                        let tag_name = region_id.tag_type_name();
265                        panic!("Same MouseRegionId: {region_id:?} inserted multiple times to the same scene. \
266                            Will cause problems! Look for MouseRegion that uses Tag: {tag_name}");
267                    }
268                }
269            }
270        }
271    }
272
273    pub fn push_image(&mut self, image: Image) {
274        self.active_layer().push_image(image)
275    }
276
277    pub fn push_surface(&mut self, surface: Surface) {
278        self.active_layer().push_surface(surface)
279    }
280
281    pub fn push_underline(&mut self, underline: Underline) {
282        self.active_layer().push_underline(underline)
283    }
284
285    pub fn push_shadow(&mut self, shadow: Shadow) {
286        self.active_layer().push_shadow(shadow)
287    }
288
289    pub fn push_glyph(&mut self, glyph: Glyph) {
290        self.active_layer().push_glyph(glyph)
291    }
292
293    pub fn push_image_glyph(&mut self, image_glyph: ImageGlyph) {
294        self.active_layer().push_image_glyph(image_glyph)
295    }
296
297    pub fn push_icon(&mut self, icon: Icon) {
298        self.active_layer().push_icon(icon)
299    }
300
301    pub fn push_path(&mut self, path: Path) {
302        self.active_layer().push_path(path);
303    }
304
305    fn active_stacking_context(&mut self) -> &mut StackingContext {
306        let ix = *self.active_stacking_context_stack.last().unwrap();
307        &mut self.stacking_contexts[ix]
308    }
309
310    fn active_layer(&mut self) -> &mut Layer {
311        self.active_stacking_context().active_layer()
312    }
313}
314
315impl StackingContext {
316    fn new(depth: usize, clip_bounds: Option<RectF>) -> Self {
317        Self {
318            layers: vec![Layer::new(clip_bounds)],
319            active_layer_stack: vec![0],
320            depth,
321        }
322    }
323
324    fn active_layer(&mut self) -> &mut Layer {
325        &mut self.layers[*self.active_layer_stack.last().unwrap()]
326    }
327
328    fn push_layer(&mut self, clip_bounds: Option<RectF>) {
329        let parent_clip_bounds = self.active_layer().clip_bounds();
330        let clip_bounds = clip_bounds
331            .map(|clip_bounds| {
332                clip_bounds
333                    .intersection(parent_clip_bounds.unwrap_or(clip_bounds))
334                    .unwrap_or_else(|| {
335                        if !clip_bounds.is_empty() {
336                            log::warn!("specified clip bounds are disjoint from parent layer");
337                        }
338                        RectF::default()
339                    })
340            })
341            .or(parent_clip_bounds);
342
343        let ix = self.layers.len();
344        self.layers.push(Layer::new(clip_bounds));
345        self.active_layer_stack.push(ix);
346    }
347
348    fn pop_layer(&mut self) {
349        self.active_layer_stack.pop().unwrap();
350        assert!(!self.active_layer_stack.is_empty());
351    }
352}
353
354impl Layer {
355    pub fn new(clip_bounds: Option<RectF>) -> Self {
356        Self {
357            clip_bounds,
358            quads: Default::default(),
359            underlines: Default::default(),
360            images: Default::default(),
361            surfaces: Default::default(),
362            shadows: Default::default(),
363            image_glyphs: Default::default(),
364            glyphs: Default::default(),
365            icons: Default::default(),
366            paths: Default::default(),
367            cursor_regions: Default::default(),
368            mouse_regions: Default::default(),
369        }
370    }
371
372    pub fn clip_bounds(&self) -> Option<RectF> {
373        self.clip_bounds
374    }
375
376    fn push_quad(&mut self, quad: Quad) {
377        if can_draw(quad.bounds) {
378            self.quads.push(quad);
379        }
380    }
381
382    pub fn quads(&self) -> &[Quad] {
383        self.quads.as_slice()
384    }
385
386    fn push_cursor_region(&mut self, region: CursorRegion) {
387        if let Some(bounds) = region
388            .bounds
389            .intersection(self.clip_bounds.unwrap_or(region.bounds))
390        {
391            if can_draw(bounds) {
392                self.cursor_regions.push(region);
393            }
394        }
395    }
396
397    fn push_mouse_region(&mut self, region: MouseRegion) -> bool {
398        if let Some(bounds) = region
399            .bounds
400            .intersection(self.clip_bounds.unwrap_or(region.bounds))
401        {
402            if can_draw(bounds) {
403                self.mouse_regions.push(region);
404                return true;
405            }
406        }
407        false
408    }
409
410    fn push_underline(&mut self, underline: Underline) {
411        if underline.width > 0. {
412            self.underlines.push(underline);
413        }
414    }
415
416    pub fn underlines(&self) -> &[Underline] {
417        self.underlines.as_slice()
418    }
419
420    fn push_image(&mut self, image: Image) {
421        if can_draw(image.bounds) {
422            self.images.push(image);
423        }
424    }
425
426    pub fn images(&self) -> &[Image] {
427        self.images.as_slice()
428    }
429
430    fn push_surface(&mut self, surface: Surface) {
431        if can_draw(surface.bounds) {
432            self.surfaces.push(surface);
433        }
434    }
435
436    pub fn surfaces(&self) -> &[Surface] {
437        self.surfaces.as_slice()
438    }
439
440    fn push_shadow(&mut self, shadow: Shadow) {
441        if can_draw(shadow.bounds) {
442            self.shadows.push(shadow);
443        }
444    }
445
446    pub fn shadows(&self) -> &[Shadow] {
447        self.shadows.as_slice()
448    }
449
450    fn push_image_glyph(&mut self, glyph: ImageGlyph) {
451        self.image_glyphs.push(glyph);
452    }
453
454    pub fn image_glyphs(&self) -> &[ImageGlyph] {
455        self.image_glyphs.as_slice()
456    }
457
458    fn push_glyph(&mut self, glyph: Glyph) {
459        self.glyphs.push(glyph);
460    }
461
462    pub fn glyphs(&self) -> &[Glyph] {
463        self.glyphs.as_slice()
464    }
465
466    pub fn push_icon(&mut self, icon: Icon) {
467        if can_draw(icon.bounds) {
468            self.icons.push(icon);
469        }
470    }
471
472    pub fn icons(&self) -> &[Icon] {
473        self.icons.as_slice()
474    }
475
476    fn push_path(&mut self, path: Path) {
477        if can_draw(path.bounds) {
478            self.paths.push(path);
479        }
480    }
481
482    pub fn paths(&self) -> &[Path] {
483        self.paths.as_slice()
484    }
485}
486
487impl Border {
488    pub fn new(width: f32, color: Color) -> Self {
489        Self {
490            width,
491            color,
492            overlay: false,
493            top: false,
494            left: false,
495            bottom: false,
496            right: false,
497        }
498    }
499
500    pub fn all(width: f32, color: Color) -> Self {
501        Self {
502            width,
503            color,
504            overlay: false,
505            top: true,
506            left: true,
507            bottom: true,
508            right: true,
509        }
510    }
511
512    pub fn top(width: f32, color: Color) -> Self {
513        let mut border = Self::new(width, color);
514        border.top = true;
515        border
516    }
517
518    pub fn left(width: f32, color: Color) -> Self {
519        let mut border = Self::new(width, color);
520        border.left = true;
521        border
522    }
523
524    pub fn bottom(width: f32, color: Color) -> Self {
525        let mut border = Self::new(width, color);
526        border.bottom = true;
527        border
528    }
529
530    pub fn right(width: f32, color: Color) -> Self {
531        let mut border = Self::new(width, color);
532        border.right = true;
533        border
534    }
535
536    pub fn with_sides(mut self, top: bool, left: bool, bottom: bool, right: bool) -> Self {
537        self.top = top;
538        self.left = left;
539        self.bottom = bottom;
540        self.right = right;
541        self
542    }
543
544    pub fn top_width(&self) -> f32 {
545        if self.top {
546            self.width
547        } else {
548            0.0
549        }
550    }
551
552    pub fn left_width(&self) -> f32 {
553        if self.left {
554            self.width
555        } else {
556            0.0
557        }
558    }
559}
560
561impl ToJson for Border {
562    fn to_json(&self) -> serde_json::Value {
563        let mut value = json!({});
564        if self.top {
565            value["top"] = json!(self.width);
566        }
567        if self.right {
568            value["right"] = json!(self.width);
569        }
570        if self.bottom {
571            value["bottom"] = json!(self.width);
572        }
573        if self.left {
574            value["left"] = json!(self.width);
575        }
576        value
577    }
578}
579
580impl MouseRegion {
581    pub fn id(&self) -> MouseRegionId {
582        self.id
583    }
584}
585
586fn can_draw(bounds: RectF) -> bool {
587    let size = bounds.size();
588    size.x() > 0. && size.y() > 0.
589}