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}