scene.rs

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