1use std::{f64::consts::TAU, fmt::Display, sync::Arc};
2
3use kurbo::{Affine, Point, Rect};
4use parley::Alignment;
5use vello::peniko::{
6 self,
7 color::{ColorSpaceTag, HueDirection},
8};
9
10use crate::css::properties::ColorProperty;
11
12#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
14pub enum TextAlign {
15 #[default]
16 Start,
17 End,
18 Left,
19 Right,
20 Center,
21 Justify,
22}
23
24impl std::fmt::Display for TextAlign {
25 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26 match self {
27 TextAlign::Start => f.write_str("start"),
28 TextAlign::End => f.write_str("end"),
29 TextAlign::Left => f.write_str("left"),
30 TextAlign::Right => f.write_str("right"),
31 TextAlign::Center => f.write_str("center"),
32 TextAlign::Justify => f.write_str("justify"),
33 }
34 }
35}
36
37impl From<TextAlign> for Alignment {
38 fn from(val: TextAlign) -> Self {
39 match val {
40 TextAlign::Start => Alignment::Start,
41 TextAlign::End => Alignment::End,
42 TextAlign::Left => Alignment::Left,
43 TextAlign::Right => Alignment::Right,
44 TextAlign::Center => Alignment::Center,
45 TextAlign::Justify => Alignment::Justify,
46 }
47 }
48}
49
50#[derive(Debug, Copy, Clone, PartialEq)]
54pub enum Length {
55 Px(f32),
56 Em(f32),
57}
58
59impl Eq for Length {}
60
61impl Default for Length {
62 fn default() -> Self {
63 Length::Px(0.0)
64 }
65}
66
67impl Length {
68 pub const ZERO: Length = Length::Px(0.0);
69
70 #[inline]
72 pub fn resolve(&self, font_size: f32) -> f32 {
73 match *self {
74 Length::Px(px) => px,
75 Length::Em(em) => em * font_size,
76 }
77 }
78}
79
80impl Display for Length {
81 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82 match *self {
83 Length::Px(v) => write!(f, "{v}px"),
84 Length::Em(v) => write!(f, "{v}em"),
85 }
86 }
87}
88
89impl From<f32> for Length {
90 fn from(value: f32) -> Self {
91 Self::Px(value)
92 }
93}
94
95impl From<f64> for Length {
96 fn from(value: f64) -> Self {
97 Self::Px(value as f32)
98 }
99}
100
101#[derive(Debug, Default, Clone, Copy, PartialEq)]
105pub struct BoxShadow {
106 pub offset_x: Length,
108 pub offset_y: Length,
110 pub blur: Length,
112 pub spread: Length,
114 pub color: Option<peniko::Color>,
116 pub inset: bool, }
119
120impl Eq for BoxShadow {}
121
122impl Display for BoxShadow {
123 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
124 if self.inset {
125 f.write_str("inset ")?;
126 }
127
128 write!(f, "{} {} {} {}", self.offset_x, self.offset_y, self.blur, self.spread)?;
129
130 if let Some(color) = self.color {
131 let color = color.to_rgba8();
132 write!(f, " #{:02X}{:02X}{:02X}{:02X}", color.r, color.g, color.b, color.a)?;
133 } else {
134 write!(f, " currentcolor")?;
135 }
136
137 Ok(())
138 }
139}
140
141#[derive(Debug, Default, Clone, Copy, PartialEq)]
145pub struct TextShadow {
146 pub offset_x: Length,
148 pub offset_y: Length,
150 pub blur: Length,
152 pub color: Option<peniko::Color>,
154}
155
156impl Eq for TextShadow {}
157
158impl Display for TextShadow {
159 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
160 write!(f, "{} {} {}", self.offset_x, self.offset_y, self.blur)?;
161
162 if let Some(color) = self.color {
163 let color = color.to_rgba8();
164 write!(f, " #{:02X}{:02X}{:02X}{:02X}", color.r, color.g, color.b, color.a)?;
165 } else {
166 write!(f, " currentcolor")?;
167 }
168
169 Ok(())
170 }
171}
172
173#[derive(Debug, Clone, Copy, PartialEq, Eq)]
177pub enum Direction {
178 Row,
179 RowReverse,
180 Column,
181 ColumnReverse,
182}
183
184impl Display for Direction {
185 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
186 match self {
187 Direction::Row => f.write_str("row"),
188 Direction::RowReverse => f.write_str("row-reverse"),
189 Direction::Column => f.write_str("column"),
190 Direction::ColumnReverse => f.write_str("column-reverse"),
191 }
192 }
193}
194
195impl Direction {
196 pub fn is_row(&self) -> bool {
197 match self {
198 Direction::Row | Direction::RowReverse => true,
199 Direction::Column | Direction::ColumnReverse => false,
200 }
201 }
202
203 pub fn is_reverse(&self) -> bool {
204 match self {
205 Direction::RowReverse | Direction::ColumnReverse => true,
206 Direction::Column | Direction::Row => false,
207 }
208 }
209
210 pub fn other_axis(&self) -> Self {
211 match self {
212 Direction::Row => Direction::Column,
213 Direction::RowReverse => Direction::ColumnReverse,
214 Direction::Column => Direction::Row,
215 Direction::ColumnReverse => Direction::RowReverse,
216 }
217 }
218}
219
220#[derive(Debug, Copy, Clone)]
224pub enum GradientAngle {
225 ToTop,
226 ToRight,
227 ToBottom,
228 ToLeft,
229 ToTopRight,
230 ToTopLeft,
231 ToBottomRight,
232 ToBottomLeft,
233 Radians(f32),
234 Degrees(f32),
235}
236
237impl PartialEq for GradientAngle {
238 fn eq(&self, other: &Self) -> bool {
239 use GradientAngle::*;
240
241 const EPS: f32 = 1.0e-6;
242
243 fn radians(a: &GradientAngle) -> Option<f32> {
244 match a {
245 Radians(r) => Some(*r),
246 Degrees(d) => Some(d.to_radians()),
247 _ => None,
248 }
249 }
250
251 fn approx_eq(a: f32, b: f32) -> bool {
252 if a.is_nan() || b.is_nan() {
254 return false;
255 }
256 (a - b).abs() <= EPS
257 }
258
259 match (self, other) {
260 (ToTop, ToTop)
262 | (ToRight, ToRight)
263 | (ToBottom, ToBottom)
264 | (ToLeft, ToLeft)
265 | (ToTopRight, ToTopRight)
266 | (ToTopLeft, ToTopLeft)
267 | (ToBottomRight, ToBottomRight)
268 | (ToBottomLeft, ToBottomLeft) => true,
269
270 _ => match (radians(self), radians(other)) {
272 (Some(a), Some(b)) => approx_eq(a, b),
273 _ => false,
274 },
275 }
276 }
277}
278
279impl Display for GradientAngle {
280 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
281 match self {
282 GradientAngle::ToTop => f.write_str("to top"),
283 GradientAngle::ToRight => f.write_str("to right"),
284 GradientAngle::ToBottom => f.write_str("to bottom"),
285 GradientAngle::ToLeft => f.write_str("to left"),
286 GradientAngle::ToTopRight => f.write_str("to top right"),
287 GradientAngle::ToTopLeft => f.write_str("to top left"),
288 GradientAngle::ToBottomRight => f.write_str("to bottom right"),
289 GradientAngle::ToBottomLeft => f.write_str("to bottom left"),
290 GradientAngle::Radians(value) => write!(f, "{value}rad"),
291 GradientAngle::Degrees(value) => write!(f, "{value}deg"),
292 }
293 }
294}
295
296#[derive(Debug, Clone)]
298pub struct LinearGradient {
299 pub(crate) angle: GradientAngle,
300 pub(crate) gradient_stops: Vec<(f32, ColorProperty)>,
301 pub(crate) interpolation_cs: ColorSpaceTag,
302 pub(crate) hue_direction: HueDirection,
303}
304
305impl PartialEq for LinearGradient {
306 fn eq(&self, other: &Self) -> bool {
307 const EPS: f32 = 1.0e-6;
308
309 fn approx_eq(a: f32, b: f32) -> bool {
310 if a.is_nan() || b.is_nan() {
311 return false;
312 }
313 (a - b).abs() <= EPS
314 }
315
316 if self.angle != other.angle {
317 return false;
318 }
319 if self.interpolation_cs != other.interpolation_cs {
320 return false;
321 }
322 if self.hue_direction != other.hue_direction {
323 return false;
324 }
325
326 if self.gradient_stops.len() != other.gradient_stops.len() {
327 return false;
328 }
329
330 self.gradient_stops
331 .iter()
332 .zip(other.gradient_stops.iter())
333 .all(|((t1, c1), (t2, c2))| approx_eq(*t1, *t2) && c1 == c2)
334 }
335}
336
337impl Display for LinearGradient {
338 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
339 use cssparser::ToCss;
340
341 f.write_str("linear-gradient(")?;
342 self.angle.fmt(f)?;
343 f.write_str(", ")?;
344 for (i, (offset, property_color)) in self.gradient_stops.iter().enumerate() {
345 match property_color {
346 ColorProperty::CurrentColor => cssparser_color::Color::CurrentColor,
347 ColorProperty::Color(color) => {
348 let color = color.to_rgba8();
349 cssparser_color::Color::Rgba(cssparser_color::RgbaLegacy {
350 red: color.r,
351 green: color.g,
352 blue: color.b,
353 alpha: color.a as f32 / 255.0,
354 })
355 }
356 }
357 .to_css(f)?;
358 let percent = (offset * 100.0) as u32;
359 if percent != 0 && percent != 100 {
360 write!(f, " {percent}%")?;
361 }
362 if i != self.gradient_stops.len() - 1 {
363 f.write_str(", ")?;
364 }
365 }
366 f.write_str(")")
367 }
368}
369
370impl LinearGradient {
371 pub fn new(angle: impl Into<Option<GradientAngle>>) -> Self {
376 Self {
377 angle: angle.into().unwrap_or(GradientAngle::ToBottom),
378 gradient_stops: Vec::new(),
379 interpolation_cs: ColorSpaceTag::Srgb,
380 hue_direction: HueDirection::default(),
381 }
382 }
383
384 pub fn add_stop(mut self, offset: f32, color: impl Into<Option<peniko::Color>>) -> Self {
386 if let Some(color) = color.into() {
387 self.gradient_stops.push((offset.clamp(0.0, 1.0), ColorProperty::Color(color)));
388 } else {
389 self.gradient_stops.push((offset.clamp(0.0, 1.0), ColorProperty::CurrentColor));
390 }
391 self
392 }
393
394 pub fn with_interpolation_space(mut self, cs: ColorSpaceTag) -> Self {
396 self.interpolation_cs = cs;
397 self
398 }
399
400 pub fn with_hue_direction(mut self, dir: HueDirection) -> Self {
402 self.hue_direction = dir;
403 self
404 }
405
406 pub fn resolve(&self, rect: Rect, current_color: peniko::Color) -> peniko::Gradient {
408 let width = rect.width();
409 let height = rect.height();
410
411 let mut start;
412 let mut end;
413
414 let calc = |mut rad: f64| {
415 while rad < 0.0 {
416 rad += TAU;
417 }
418 while rad >= TAU {
419 rad -= TAU;
420 }
421
422 let u;
423 let v;
424 let hypot = width.hypot(height) / 2.0;
425 if rad < TAU * 0.25 {
426 let theta = (width / height).atan() - rad;
427 let len = theta.cos() * hypot;
428 let x = rad.sin() * len;
429 let y = rad.cos() * len;
430 u = (x / width) + 0.5;
431 v = ((height / 2.0) - y) / height;
432 } else if rad < TAU * 0.5 {
433 let theta = rad - (TAU / 4.0) - (height / width).atan();
434 let len = theta.cos() * hypot;
435 let x = rad.sin() * len;
436 let y = rad.cos() * len;
437 u = (x / width) + 0.5;
438 v = ((height / 2.0) - y) / height;
439 } else if rad < TAU * 0.75 {
440 let theta = (width / height).atan() - rad;
441 let len = theta.cos() * hypot;
442 let x = rad.sin() * len;
443 let y = rad.cos() * len;
444 u = 0.5 - (x / width);
445 v = 1.0 - ((height / 2.0) - y) / height;
446 } else {
447 let theta = rad - (3.0 * TAU / 4.0) - (height / width).atan();
448 let len = theta.cos() * hypot;
449 let x = rad.sin() * len;
450 let y = rad.cos() * len;
451 u = 1.0 - ((width / 2.0) - x) / width;
452 v = ((height / 2.0) - y) / height;
453 }
454
455 (Point::new(1.0 - u, 1.0 - v), Point::new(u, v))
456 };
457
458 match &self.angle {
459 GradientAngle::ToTop => {
460 start = Point::new(0.5, 1.0);
461 end = Point::new(0.5, 0.0);
462 }
463 GradientAngle::ToRight => {
464 start = Point::new(0.0, 0.5);
465 end = Point::new(1.0, 0.5);
466 }
467 GradientAngle::ToBottom => {
468 start = Point::new(0.5, 0.0);
469 end = Point::new(0.5, 1.0);
470 }
471 GradientAngle::ToLeft => {
472 start = Point::new(1.0, 0.5);
473 end = Point::new(0.0, 0.5);
474 }
475 GradientAngle::ToTopRight => {
476 (start, end) = calc((height / width).atan());
477 }
478 GradientAngle::ToTopLeft => {
479 (start, end) = calc((width / height).atan() - TAU / 4.0);
480 }
481 GradientAngle::ToBottomRight => {
482 (start, end) = calc((width / height).atan() + TAU / 4.0);
483 }
484 GradientAngle::ToBottomLeft => {
485 (start, end) = calc((height / width).atan() + TAU / 2.0);
486 }
487 GradientAngle::Degrees(deg) => {
488 (start, end) = calc(deg.to_radians() as f64);
489 }
490 GradientAngle::Radians(rad) => {
491 (start, end) = calc(*rad as f64);
492 }
493 }
494
495 start.x = start.x * width + rect.origin().x;
496 start.y = start.y * height + rect.origin().y;
497
498 end.x = end.x * width + rect.origin().x;
499 end.y = end.y * height + rect.origin().y;
500
501 let mut stops = peniko::ColorStops::new();
502 for &(offset, color) in &self.gradient_stops {
503 stops.push(peniko::ColorStop {
504 offset,
505 color: color.resolve(current_color).into(),
506 });
507 }
508
509 peniko::Gradient {
510 kind: peniko::GradientKind::Linear(peniko::LinearGradientPosition { start, end }),
511 extend: peniko::Extend::default(),
512 stops,
513 interpolation_cs: self.interpolation_cs,
514 ..Default::default()
515 }
516 }
517}
518
519#[derive(Clone, Debug, PartialEq)]
523pub struct GradientStack {
524 pub(crate) stack: Arc<Vec<LinearGradient>>,
525}
526
527impl std::fmt::Display for GradientStack {
528 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
529 for (i, gradient) in self.stack.iter().enumerate() {
530 if i != 0 {
531 write!(f, ", ")?;
532 }
533 write!(f, "{gradient}")?;
534 }
535 Ok(())
536 }
537}
538
539#[derive(Default)]
541pub struct GradientStackBuilder {
542 stack: Vec<LinearGradient>,
543}
544
545impl GradientStackBuilder {
546 pub fn new() -> Self {
548 Self { stack: Vec::new() }
549 }
550
551 pub fn add_linear(mut self, gradient: LinearGradient) -> Self {
553 self.stack.push(gradient);
554 self
555 }
556
557 pub fn build(self) -> GradientStack {
559 GradientStack { stack: Arc::new(self.stack) }
560 }
561}
562
563#[derive(Debug, Copy, Clone, PartialEq)]
567pub enum Unit {
568 Auto,
569 Em(f32),
570 Percent(f32),
571 Px(f32),
572 Stretch(f32),
573}
574
575impl Eq for Unit {}
576
577impl Default for Unit {
578 fn default() -> Self {
579 Self::Px(0.0)
580 }
581}
582
583impl Unit {
584 pub fn is_definite(&self) -> bool {
586 matches!(self, Unit::Em(_) | Unit::Percent(_) | Unit::Px(_))
587 }
588
589 pub fn definite_size(&self, font_size: f32, pct_base: f32) -> f32 {
591 match self {
592 Unit::Em(em) => em * font_size,
593 Unit::Percent(pct) => pct * pct_base,
594 Unit::Px(px) => *px,
595 _ => 0.0,
596 }
597 }
598}
599
600impl Display for Unit {
601 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
602 match self {
603 Unit::Auto => write!(f, "auto"),
604 Unit::Em(em) => write!(f, "{em}em"),
605 Unit::Percent(value) => write!(f, "{}%", value * 100.0),
606 Unit::Px(value) => write!(f, "{value}px"),
607 Unit::Stretch(value) => write!(f, "{value}s"),
608 }
609 }
610}
611
612#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
616pub enum Position {
617 #[default]
618 ParentDirected,
619 SelfDirected,
620 Fixed,
621}
622
623impl Display for Position {
624 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
625 match self {
626 Position::ParentDirected => f.write_str("parent-directed"),
627 Position::SelfDirected => f.write_str("self-directed"),
628 Position::Fixed => f.write_str("fixed"),
629 }
630 }
631}
632
633#[derive(Clone, Debug, PartialEq)]
642pub struct FontLayoutStyle {
643 pub font_family: Option<Arc<str>>,
644 pub font_size: f32,
645 pub font_style: parley::style::FontStyle,
646 pub font_weight: f32,
647 pub font_width: f32,
648 pub line_height: Unit,
649 pub letter_spacing: Option<Unit>,
650 pub word_spacing: Option<Unit>,
651 pub text_align: TextAlign,
652}
653
654#[derive(Clone, Debug, PartialEq)]
662pub(crate) struct LayoutStyle {
663 pub border_bottom_left_radius: Length,
665 pub border_bottom_right_radius: Length,
666 pub border_bottom_width: Length,
667 pub border_left_width: Length,
668 pub border_right_width: Length,
669 pub border_top_left_radius: Length,
670 pub border_top_right_radius: Length,
671 pub border_top_width: Length,
672 pub bottom: Unit,
673 pub child_between: Unit,
674 pub child_bottom: Unit,
675 pub child_left: Unit,
676 pub child_right: Unit,
677 pub child_top: Unit,
678 pub display: Option<Direction>,
679 pub flex_basis: Length,
680 pub font_family: Option<Arc<str>>,
681 pub font_size: f32,
682 pub font_style: parley::style::FontStyle,
683 pub font_weight: f32,
684 pub font_width: f32,
685 pub height: Unit,
686 pub left: Unit,
687 pub letter_spacing: Option<Unit>,
688 pub line_height: Unit,
689 pub max_bottom: Option<Length>,
690 pub max_child_between: Option<Length>,
691 pub max_child_bottom: Option<Length>,
692 pub max_child_left: Option<Length>,
693 pub max_child_right: Option<Length>,
694 pub max_child_top: Option<Length>,
695 pub max_height: Option<Length>,
696 pub max_left: Option<Length>,
697 pub max_right: Option<Length>,
698 pub max_top: Option<Length>,
699 pub max_width: Option<Length>,
700 pub min_bottom: Option<Length>,
701 pub min_child_between: Option<Length>,
702 pub min_child_bottom: Option<Length>,
703 pub min_child_left: Option<Length>,
704 pub min_child_right: Option<Length>,
705 pub min_child_top: Option<Length>,
706 pub min_height: Option<Length>,
707 pub min_left: Option<Length>,
708 pub min_right: Option<Length>,
709 pub min_top: Option<Length>,
710 pub min_width: Option<Length>,
711 pub position: Position,
712 pub right: Unit,
713 pub text_align: TextAlign,
714 pub top: Unit,
715 pub width: Unit,
716 pub word_spacing: Option<Unit>,
717}
718
719#[derive(Clone, PartialEq)]
725pub struct Style {
726 pub background_color: peniko::Color,
727 pub background_image: Option<GradientStack>,
728 pub border_bottom_color: peniko::Color,
729 pub border_bottom_left_radius: Length,
730 pub border_bottom_right_radius: Length,
731 pub border_bottom_width: Length,
732 pub border_left_color: peniko::Color,
733 pub border_left_width: Length,
734 pub border_right_color: peniko::Color,
735 pub border_right_width: Length,
736 pub border_top_color: peniko::Color,
737 pub border_top_left_radius: Length,
738 pub border_top_right_radius: Length,
739 pub border_top_width: Length,
740 pub bottom: Unit,
741 pub box_shadow: Option<Arc<[BoxShadow]>>,
742 pub child_between: Unit,
743 pub child_bottom: Unit,
744 pub child_left: Unit,
745 pub child_right: Unit,
746 pub child_top: Unit,
747 pub color: peniko::Color,
748 pub display: Option<Direction>,
749 pub flex_basis: Length,
750 pub font_family: Option<Arc<str>>,
751 pub font_size: f32,
752 pub font_style: parley::style::FontStyle,
753 pub font_weight: f32,
754 pub font_width: f32,
755 pub height: Unit,
756 pub left: Unit,
757 pub letter_spacing: Option<Unit>,
758 pub line_height: Unit,
759 pub max_bottom: Option<Length>,
760 pub max_child_between: Option<Length>,
761 pub max_child_bottom: Option<Length>,
762 pub max_child_left: Option<Length>,
763 pub max_child_right: Option<Length>,
764 pub max_child_top: Option<Length>,
765 pub max_height: Option<Length>,
766 pub max_left: Option<Length>,
767 pub max_right: Option<Length>,
768 pub max_top: Option<Length>,
769 pub max_width: Option<Length>,
770 pub min_bottom: Option<Length>,
771 pub min_child_between: Option<Length>,
772 pub min_child_bottom: Option<Length>,
773 pub min_child_left: Option<Length>,
774 pub min_child_right: Option<Length>,
775 pub min_child_top: Option<Length>,
776 pub min_height: Option<Length>,
777 pub min_left: Option<Length>,
778 pub min_right: Option<Length>,
779 pub min_top: Option<Length>,
780 pub min_width: Option<Length>,
781 pub opacity: f32,
782 pub outline_color: peniko::Color,
783 pub outline_offset: Length,
784 pub outline_width: Length,
785 pub position: Position,
786 pub right: Unit,
787 pub selection_background: peniko::Color,
788
789 pub selection_color: Option<peniko::Color>,
791 pub text_align: TextAlign,
792 pub text_shadow: Option<Arc<[TextShadow]>>,
793 pub top: Unit,
794 pub transform: Affine,
795 pub visibility: bool,
796 pub width: Unit,
797 pub word_spacing: Option<Unit>,
798 pub z_index: i32,
799}
800
801impl std::fmt::Debug for Style {
802 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
803 let d = Style::default();
804 let mut s = f.debug_struct("Style");
805
806 macro_rules! diff {
807 ($field:ident) => {
808 if self.$field != d.$field {
809 s.field(stringify!($field), &self.$field);
810 }
811 };
812 }
813
814 macro_rules! diff_color {
816 ($field:ident) => {
817 if self.$field != d.$field {
818 let c = self.$field.to_rgba8();
819 s.field(stringify!($field), &format!("#{:02X}{:02X}{:02X}{:02X}", c.r, c.g, c.b, c.a));
820 }
821 };
822 }
823
824 macro_rules! diff_opt_color {
825 ($field:ident) => {
826 if self.$field != d.$field {
827 match self.$field {
828 None => s.field(stringify!($field), &"None"),
829 Some(c) => {
830 let c = c.to_rgba8();
831 s.field(stringify!($field), &format!("Some(#{:02X}{:02X}{:02X}{:02X})", c.r, c.g, c.b, c.a))
832 }
833 };
834 }
835 };
836 }
837
838 diff_color!(background_color);
839 diff_color!(border_bottom_color);
840 diff_color!(border_left_color);
841 diff_color!(border_right_color);
842 diff_color!(border_top_color);
843 diff_color!(color);
844 diff_color!(outline_color);
845 diff_color!(selection_background);
846 diff_opt_color!(selection_color);
847 diff!(background_image);
848 diff!(border_bottom_left_radius);
849 diff!(border_bottom_right_radius);
850 diff!(border_bottom_width);
851 diff!(border_left_width);
852 diff!(border_right_width);
853 diff!(border_top_left_radius);
854 diff!(border_top_right_radius);
855 diff!(border_top_width);
856 diff!(bottom);
857 diff!(box_shadow);
858 diff!(child_between);
859 diff!(child_bottom);
860 diff!(child_left);
861 diff!(child_right);
862 diff!(child_top);
863 diff!(display);
864 diff!(flex_basis);
865 diff!(font_family);
866 diff!(font_size);
867 diff!(font_style);
868 diff!(font_weight);
869 diff!(font_width);
870 diff!(height);
871 diff!(left);
872 diff!(letter_spacing);
873 diff!(line_height);
874 diff!(max_bottom);
875 diff!(max_child_between);
876 diff!(max_child_bottom);
877 diff!(max_child_left);
878 diff!(max_child_right);
879 diff!(max_child_top);
880 diff!(max_height);
881 diff!(max_left);
882 diff!(max_right);
883 diff!(max_top);
884 diff!(max_width);
885 diff!(min_bottom);
886 diff!(min_child_between);
887 diff!(min_child_bottom);
888 diff!(min_child_left);
889 diff!(min_child_right);
890 diff!(min_child_top);
891 diff!(min_height);
892 diff!(min_left);
893 diff!(min_right);
894 diff!(min_top);
895 diff!(min_width);
896 diff!(opacity);
897 diff!(outline_offset);
898 diff!(outline_width);
899 diff!(position);
900 diff!(right);
901 diff!(text_align);
902 diff!(text_shadow);
903 diff!(top);
904 diff!(transform);
905 diff!(visibility);
906 diff!(width);
907 diff!(word_spacing);
908 diff!(z_index);
909
910 s.finish()
911 }
912}
913
914impl Default for Style {
915 fn default() -> Self {
916 Self {
917 background_color: peniko::Color::from_rgba8(0, 0, 0, 0),
918 background_image: None,
919 border_bottom_color: peniko::Color::from_rgba8(0, 0, 0, 255),
920 border_bottom_left_radius: Length::Px(0.0),
921 border_bottom_right_radius: Length::Px(0.0),
922 border_bottom_width: Length::Px(0.0),
923 border_left_color: peniko::Color::from_rgba8(0, 0, 0, 255),
924 border_left_width: Length::Px(0.0),
925 border_right_color: peniko::Color::from_rgba8(0, 0, 0, 255),
926 border_right_width: Length::Px(0.0),
927 border_top_color: peniko::Color::from_rgba8(0, 0, 0, 255),
928 border_top_left_radius: Length::Px(0.0),
929 border_top_right_radius: Length::Px(0.0),
930 border_top_width: Length::Px(0.0),
931 bottom: Unit::Auto,
932 box_shadow: None,
933 child_between: Unit::Auto,
934 child_bottom: Unit::Auto,
935 child_left: Unit::Auto,
936 child_right: Unit::Auto,
937 child_top: Unit::Auto,
938 color: peniko::Color::from_rgba8(0, 0, 0, 255),
939 display: Some(Direction::Column),
940 flex_basis: Length::Px(0.0),
941 font_family: None,
942 font_size: 16.0,
943 font_style: parley::style::FontStyle::Normal,
944 font_weight: 400.0,
945 font_width: 1.0,
946 height: Unit::Stretch(1.0),
947 left: Unit::Auto,
948 letter_spacing: None,
949 line_height: Unit::Stretch(1.2),
950 max_bottom: None,
951 max_child_between: None,
952 max_child_bottom: None,
953 max_child_left: None,
954 max_child_right: None,
955 max_child_top: None,
956 max_height: None,
957 max_left: None,
958 max_right: None,
959 max_top: None,
960 max_width: None,
961 min_bottom: None,
962 min_child_between: None,
963 min_child_bottom: None,
964 min_child_left: None,
965 min_child_right: None,
966 min_child_top: None,
967 min_height: None,
968 min_left: None,
969 min_right: None,
970 min_top: None,
971 min_width: None,
972 opacity: 1.0,
973 outline_color: peniko::Color::from_rgba8(0, 0, 0, 255),
974 outline_offset: Length::Px(0.0),
975 outline_width: Length::Px(0.0),
976 position: Position::ParentDirected,
977 right: Unit::Auto,
978 selection_background: peniko::Color::from_rgba8(4, 101, 175, 128),
979 selection_color: None,
980 text_align: TextAlign::Start,
981 text_shadow: None,
982 top: Unit::Auto,
983 transform: Affine::IDENTITY,
984 visibility: true,
985 width: Unit::Stretch(1.0),
986 word_spacing: None,
987 z_index: 0,
988 }
989 }
990}
991
992impl Style {
993 pub fn get_font_layout_style(&self) -> FontLayoutStyle {
994 FontLayoutStyle {
995 font_family: self.font_family.clone(),
996 font_size: self.font_size,
997 font_style: self.font_style,
998 font_weight: self.font_weight,
999 font_width: self.font_width,
1000 letter_spacing: self.letter_spacing,
1001 line_height: self.line_height,
1002 text_align: self.text_align,
1003 word_spacing: self.word_spacing,
1004 }
1005 }
1006
1007 pub(crate) fn get_layout_style(&self) -> LayoutStyle {
1008 LayoutStyle {
1009 border_bottom_left_radius: self.border_bottom_left_radius,
1010 border_bottom_right_radius: self.border_bottom_right_radius,
1011 border_bottom_width: self.border_bottom_width,
1012 border_left_width: self.border_left_width,
1013 border_right_width: self.border_right_width,
1014 border_top_left_radius: self.border_top_left_radius,
1015 border_top_right_radius: self.border_top_right_radius,
1016 border_top_width: self.border_top_width,
1017 bottom: self.bottom,
1018 child_between: self.child_between,
1019 child_bottom: self.child_bottom,
1020 child_left: self.child_left,
1021 child_right: self.child_right,
1022 child_top: self.child_top,
1023 display: self.display,
1024 flex_basis: self.flex_basis,
1025 font_family: self.font_family.clone(),
1026 font_size: self.font_size,
1027 font_style: self.font_style,
1028 font_weight: self.font_weight,
1029 font_width: self.font_width,
1030 height: self.height,
1031 left: self.left,
1032 letter_spacing: self.letter_spacing,
1033 line_height: self.line_height,
1034 max_bottom: self.max_bottom,
1035 max_child_between: self.max_child_between,
1036 max_child_bottom: self.max_child_bottom,
1037 max_child_left: self.max_child_left,
1038 max_child_right: self.max_child_right,
1039 max_child_top: self.max_child_top,
1040 max_height: self.max_height,
1041 max_left: self.max_left,
1042 max_right: self.max_right,
1043 max_top: self.max_top,
1044 max_width: self.max_width,
1045 min_bottom: self.min_bottom,
1046 min_child_between: self.min_child_between,
1047 min_child_bottom: self.min_child_bottom,
1048 min_child_left: self.min_child_left,
1049 min_child_right: self.min_child_right,
1050 min_child_top: self.min_child_top,
1051 min_height: self.min_height,
1052 min_left: self.min_left,
1053 min_right: self.min_right,
1054 min_top: self.min_top,
1055 min_width: self.min_width,
1056 position: self.position,
1057 right: self.right,
1058 text_align: self.text_align,
1059 top: self.top,
1060 width: self.width,
1061 word_spacing: self.word_spacing,
1062 }
1063 }
1064}