1use serde::Deserialize;
2use serde_json::json;
3use std::borrow::Cow;
4
5use crate::{
6 color::Color,
7 fonts::{FontId, GlyphId},
8 geometry::{rect::RectF, vector::Vector2F},
9 json::ToJson,
10};
11
12pub struct Scene {
13 scale_factor: f32,
14 stacking_contexts: Vec<StackingContext>,
15 active_stacking_context_stack: Vec<usize>,
16}
17
18struct StackingContext {
19 layers: Vec<Layer>,
20 active_layer_stack: Vec<usize>,
21}
22
23#[derive(Default)]
24pub struct Layer {
25 clip_bounds: Option<RectF>,
26 quads: Vec<Quad>,
27 shadows: Vec<Shadow>,
28 glyphs: Vec<Glyph>,
29 icons: Vec<Icon>,
30 paths: Vec<Path>,
31}
32
33#[derive(Default, Debug)]
34pub struct Quad {
35 pub bounds: RectF,
36 pub background: Option<Color>,
37 pub border: Border,
38 pub corner_radius: f32,
39}
40
41#[derive(Debug)]
42pub struct Shadow {
43 pub bounds: RectF,
44 pub corner_radius: f32,
45 pub sigma: f32,
46 pub color: Color,
47}
48
49#[derive(Debug)]
50pub struct Glyph {
51 pub font_id: FontId,
52 pub font_size: f32,
53 pub id: GlyphId,
54 pub origin: Vector2F,
55 pub color: Color,
56}
57
58pub struct Icon {
59 pub bounds: RectF,
60 pub svg: usvg::Tree,
61 pub path: Cow<'static, str>,
62 pub color: Color,
63}
64
65#[derive(Clone, Copy, Default, Debug)]
66pub struct Border {
67 pub width: f32,
68 pub color: Color,
69 pub top: bool,
70 pub right: bool,
71 pub bottom: bool,
72 pub left: bool,
73}
74
75impl<'de> Deserialize<'de> for Border {
76 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
77 where
78 D: serde::Deserializer<'de>,
79 {
80 #[derive(Deserialize)]
81 struct BorderData {
82 pub width: f32,
83 pub color: Color,
84 #[serde(default)]
85 pub top: bool,
86 #[serde(default)]
87 pub right: bool,
88 #[serde(default)]
89 pub bottom: bool,
90 #[serde(default)]
91 pub left: bool,
92 }
93
94 let data = BorderData::deserialize(deserializer)?;
95 let mut border = Border {
96 width: data.width,
97 color: data.color,
98 top: data.top,
99 bottom: data.bottom,
100 left: data.left,
101 right: data.right,
102 };
103 if !border.top && !border.bottom && !border.left && !border.right {
104 border.top = true;
105 border.bottom = true;
106 border.left = true;
107 border.right = true;
108 }
109 Ok(border)
110 }
111}
112
113#[derive(Debug)]
114pub struct Path {
115 pub bounds: RectF,
116 pub color: Color,
117 pub vertices: Vec<PathVertex>,
118}
119
120#[derive(Debug)]
121pub struct PathVertex {
122 pub xy_position: Vector2F,
123 pub st_position: Vector2F,
124}
125
126impl Scene {
127 pub fn new(scale_factor: f32) -> Self {
128 let stacking_context = StackingContext::new(None);
129 Scene {
130 scale_factor,
131 stacking_contexts: vec![stacking_context],
132 active_stacking_context_stack: vec![0],
133 }
134 }
135
136 pub fn scale_factor(&self) -> f32 {
137 self.scale_factor
138 }
139
140 pub fn layers(&self) -> impl Iterator<Item = &Layer> {
141 self.stacking_contexts.iter().flat_map(|s| &s.layers)
142 }
143
144 pub fn push_stacking_context(&mut self, clip_bounds: Option<RectF>) {
145 self.active_stacking_context_stack
146 .push(self.stacking_contexts.len());
147 self.stacking_contexts
148 .push(StackingContext::new(clip_bounds))
149 }
150
151 pub fn pop_stacking_context(&mut self) {
152 self.active_stacking_context_stack.pop();
153 assert!(!self.active_stacking_context_stack.is_empty());
154 }
155
156 pub fn push_layer(&mut self, clip_bounds: Option<RectF>) {
157 self.active_stacking_context().push_layer(clip_bounds);
158 }
159
160 pub fn pop_layer(&mut self) {
161 self.active_stacking_context().pop_layer();
162 }
163
164 pub fn push_quad(&mut self, quad: Quad) {
165 self.active_layer().push_quad(quad)
166 }
167
168 pub fn push_shadow(&mut self, shadow: Shadow) {
169 self.active_layer().push_shadow(shadow)
170 }
171
172 pub fn push_glyph(&mut self, glyph: Glyph) {
173 self.active_layer().push_glyph(glyph)
174 }
175
176 pub fn push_icon(&mut self, icon: Icon) {
177 self.active_layer().push_icon(icon)
178 }
179
180 pub fn push_path(&mut self, path: Path) {
181 self.active_layer().push_path(path);
182 }
183
184 fn active_stacking_context(&mut self) -> &mut StackingContext {
185 let ix = *self.active_stacking_context_stack.last().unwrap();
186 &mut self.stacking_contexts[ix]
187 }
188
189 fn active_layer(&mut self) -> &mut Layer {
190 self.active_stacking_context().active_layer()
191 }
192}
193
194impl StackingContext {
195 fn new(clip_bounds: Option<RectF>) -> Self {
196 Self {
197 layers: vec![Layer::new(clip_bounds)],
198 active_layer_stack: vec![0],
199 }
200 }
201
202 fn active_layer(&mut self) -> &mut Layer {
203 &mut self.layers[*self.active_layer_stack.last().unwrap()]
204 }
205
206 fn push_layer(&mut self, clip_bounds: Option<RectF>) {
207 let parent_clip_bounds = self.active_layer().clip_bounds();
208 let clip_bounds = clip_bounds
209 .map(|clip_bounds| {
210 clip_bounds
211 .intersection(parent_clip_bounds.unwrap_or(clip_bounds))
212 .unwrap_or_else(|| {
213 if !clip_bounds.is_empty() {
214 log::warn!("specified clip bounds are disjoint from parent layer");
215 }
216 RectF::default()
217 })
218 })
219 .or(parent_clip_bounds);
220
221 let ix = self.layers.len();
222 self.layers.push(Layer::new(clip_bounds));
223 self.active_layer_stack.push(ix);
224 }
225
226 fn pop_layer(&mut self) {
227 self.active_layer_stack.pop().unwrap();
228 assert!(!self.active_layer_stack.is_empty());
229 }
230}
231
232impl Layer {
233 pub fn new(clip_bounds: Option<RectF>) -> Self {
234 Self {
235 clip_bounds,
236 quads: Vec::new(),
237 shadows: Vec::new(),
238 glyphs: Vec::new(),
239 icons: Vec::new(),
240 paths: Vec::new(),
241 }
242 }
243
244 pub fn clip_bounds(&self) -> Option<RectF> {
245 self.clip_bounds
246 }
247
248 fn push_quad(&mut self, quad: Quad) {
249 self.quads.push(quad);
250 }
251
252 pub fn quads(&self) -> &[Quad] {
253 self.quads.as_slice()
254 }
255
256 fn push_shadow(&mut self, shadow: Shadow) {
257 self.shadows.push(shadow);
258 }
259
260 pub fn shadows(&self) -> &[Shadow] {
261 self.shadows.as_slice()
262 }
263
264 fn push_glyph(&mut self, glyph: Glyph) {
265 self.glyphs.push(glyph);
266 }
267
268 pub fn glyphs(&self) -> &[Glyph] {
269 self.glyphs.as_slice()
270 }
271
272 pub fn push_icon(&mut self, icon: Icon) {
273 self.icons.push(icon);
274 }
275
276 pub fn icons(&self) -> &[Icon] {
277 self.icons.as_slice()
278 }
279
280 fn push_path(&mut self, path: Path) {
281 if !path.bounds.is_empty() {
282 self.paths.push(path);
283 }
284 }
285
286 pub fn paths(&self) -> &[Path] {
287 self.paths.as_slice()
288 }
289}
290
291impl Border {
292 pub fn new(width: f32, color: Color) -> Self {
293 Self {
294 width,
295 color,
296 top: false,
297 left: false,
298 bottom: false,
299 right: false,
300 }
301 }
302
303 pub fn all(width: f32, color: Color) -> Self {
304 Self {
305 width,
306 color,
307 top: true,
308 left: true,
309 bottom: true,
310 right: true,
311 }
312 }
313
314 pub fn top(width: f32, color: Color) -> Self {
315 let mut border = Self::new(width, color);
316 border.top = true;
317 border
318 }
319
320 pub fn left(width: f32, color: Color) -> Self {
321 let mut border = Self::new(width, color);
322 border.left = true;
323 border
324 }
325
326 pub fn bottom(width: f32, color: Color) -> Self {
327 let mut border = Self::new(width, color);
328 border.bottom = true;
329 border
330 }
331
332 pub fn right(width: f32, color: Color) -> Self {
333 let mut border = Self::new(width, color);
334 border.right = true;
335 border
336 }
337
338 pub fn with_sides(mut self, top: bool, left: bool, bottom: bool, right: bool) -> Self {
339 self.top = top;
340 self.left = left;
341 self.bottom = bottom;
342 self.right = right;
343 self
344 }
345
346 pub fn top_width(&self) -> f32 {
347 if self.top {
348 self.width
349 } else {
350 0.0
351 }
352 }
353
354 pub fn left_width(&self) -> f32 {
355 if self.left {
356 self.width
357 } else {
358 0.0
359 }
360 }
361}
362
363impl ToJson for Border {
364 fn to_json(&self) -> serde_json::Value {
365 let mut value = json!({});
366 if self.top {
367 value["top"] = json!(self.width);
368 }
369 if self.right {
370 value["right"] = json!(self.width);
371 }
372 if self.bottom {
373 value["bottom"] = json!(self.width);
374 }
375 if self.left {
376 value["left"] = json!(self.width);
377 }
378 value
379 }
380}