1use alloc::string::{String, ToString};
4use crate::corety::AzString;
5
6use crate::props::{
7 basic::{
8 color::{parse_css_color, ColorU, CssColorParseError, CssColorParseErrorOwned},
9 },
10 formatter::PrintAsCssValue,
11 layout::{
12 dimensions::LayoutWidth,
13 spacing::{LayoutPaddingLeft, LayoutPaddingRight},
14 },
15 style::background::StyleBackgroundContent,
16};
17
18#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
25#[repr(C)]
26pub enum ScrollBehavior {
27 #[default]
29 Auto,
30 Smooth,
32}
33
34impl PrintAsCssValue for ScrollBehavior {
35 fn print_as_css_value(&self) -> String {
36 match self {
37 Self::Auto => "auto".to_string(),
38 Self::Smooth => "smooth".to_string(),
39 }
40 }
41}
42
43#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
46#[repr(C)]
47pub enum OverscrollBehavior {
48 #[default]
50 Auto,
51 Contain,
53 None,
55}
56
57impl PrintAsCssValue for OverscrollBehavior {
58 fn print_as_css_value(&self) -> String {
59 match self {
60 Self::Auto => "auto".to_string(),
61 Self::Contain => "contain".to_string(),
62 Self::None => "none".to_string(),
63 }
64 }
65}
66
67#[derive(Debug, Clone, PartialEq, PartialOrd)]
76#[repr(C)]
77pub struct ScrollPhysics {
78 pub smooth_scroll_duration_ms: u32,
81
82 pub deceleration_rate: f32,
86
87 pub min_velocity_threshold: f32,
90
91 pub max_velocity: f32,
93
94 pub wheel_multiplier: f32,
97
98 pub invert_direction: bool,
100
101 pub overscroll_elasticity: f32,
104
105 pub max_overscroll_distance: f32,
108
109 pub bounce_back_duration_ms: u32,
112
113 pub timer_interval_ms: u32,
117}
118
119impl Default for ScrollPhysics {
120 fn default() -> Self {
121 Self {
122 smooth_scroll_duration_ms: 300,
123 deceleration_rate: 0.95,
124 min_velocity_threshold: 50.0,
125 max_velocity: 8000.0,
126 wheel_multiplier: 1.0,
127 invert_direction: false,
128 overscroll_elasticity: 0.0, max_overscroll_distance: 100.0,
130 bounce_back_duration_ms: 400,
131 timer_interval_ms: 16,
132 }
133 }
134}
135
136impl ScrollPhysics {
137 pub const fn ios() -> Self {
139 Self {
140 smooth_scroll_duration_ms: 300,
141 deceleration_rate: 0.998,
142 min_velocity_threshold: 20.0,
143 max_velocity: 8000.0,
144 wheel_multiplier: 1.0,
145 invert_direction: true, overscroll_elasticity: 0.5,
147 max_overscroll_distance: 120.0,
148 bounce_back_duration_ms: 500,
149 timer_interval_ms: 16,
150 }
151 }
152
153 pub const fn macos() -> Self {
155 Self {
156 smooth_scroll_duration_ms: 250,
157 deceleration_rate: 0.997,
158 min_velocity_threshold: 30.0,
159 max_velocity: 6000.0,
160 wheel_multiplier: 1.0,
161 invert_direction: true, overscroll_elasticity: 0.3,
163 max_overscroll_distance: 80.0,
164 bounce_back_duration_ms: 400,
165 timer_interval_ms: 16,
166 }
167 }
168
169 pub const fn windows() -> Self {
171 Self {
172 smooth_scroll_duration_ms: 200,
173 deceleration_rate: 0.9,
174 min_velocity_threshold: 100.0,
175 max_velocity: 4000.0,
176 wheel_multiplier: 1.0,
177 invert_direction: false,
178 overscroll_elasticity: 0.0,
179 max_overscroll_distance: 0.0,
180 bounce_back_duration_ms: 200,
181 timer_interval_ms: 16,
182 }
183 }
184
185 pub const fn android() -> Self {
187 Self {
188 smooth_scroll_duration_ms: 250,
189 deceleration_rate: 0.996,
190 min_velocity_threshold: 40.0,
191 max_velocity: 8000.0,
192 wheel_multiplier: 1.0,
193 invert_direction: false,
194 overscroll_elasticity: 0.2, max_overscroll_distance: 60.0,
196 bounce_back_duration_ms: 300,
197 timer_interval_ms: 16,
198 }
199 }
200}
201
202#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
220#[repr(C)]
221pub enum ScrollbarVisibilityMode {
222 #[default]
225 Always,
226 WhenScrolling,
229 Auto,
231}
232
233impl PrintAsCssValue for ScrollbarVisibilityMode {
234 fn print_as_css_value(&self) -> String {
235 match self {
236 Self::Always => "always".to_string(),
237 Self::WhenScrolling => "when-scrolling".to_string(),
238 Self::Auto => "auto".to_string(),
239 }
240 }
241}
242
243#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
254#[repr(C)]
255pub struct ScrollbarFadeDelay {
256 pub ms: u32,
258}
259
260impl ScrollbarFadeDelay {
261 pub const fn new(ms: u32) -> Self { Self { ms } }
262 pub const ZERO: Self = Self { ms: 0 };
263}
264
265impl PrintAsCssValue for ScrollbarFadeDelay {
266 fn print_as_css_value(&self) -> String {
267 if self.ms == 0 { "0".to_string() } else { format!("{}ms", self.ms) }
268 }
269}
270
271#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
282#[repr(C)]
283pub struct ScrollbarFadeDuration {
284 pub ms: u32,
286}
287
288impl ScrollbarFadeDuration {
289 pub const fn new(ms: u32) -> Self { Self { ms } }
290 pub const ZERO: Self = Self { ms: 0 };
291}
292
293impl PrintAsCssValue for ScrollbarFadeDuration {
294 fn print_as_css_value(&self) -> String {
295 if self.ms == 0 { "0".to_string() } else { format!("{}ms", self.ms) }
296 }
297}
298
299#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
313#[repr(C)]
314pub enum OverflowScrolling {
315 #[default]
317 Auto,
318 Touch,
320}
321
322impl PrintAsCssValue for OverflowScrolling {
323 fn print_as_css_value(&self) -> String {
324 match self {
325 Self::Auto => "auto".to_string(),
326 Self::Touch => "touch".to_string(),
327 }
328 }
329}
330
331#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
337#[repr(C)]
338#[derive(Default)]
339pub enum LayoutScrollbarWidth {
340 #[default]
341 Auto,
342 Thin,
343 None,
344}
345
346
347impl PrintAsCssValue for LayoutScrollbarWidth {
348 fn print_as_css_value(&self) -> String {
349 match self {
350 Self::Auto => "auto".to_string(),
351 Self::Thin => "thin".to_string(),
352 Self::None => "none".to_string(),
353 }
354 }
355}
356
357#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
359#[repr(C)]
360pub struct ScrollbarColorCustom {
361 pub thumb: ColorU,
362 pub track: ColorU,
363}
364
365#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
367#[repr(C, u8)]
368#[derive(Default)]
369pub enum StyleScrollbarColor {
370 #[default]
371 Auto,
372 Custom(ScrollbarColorCustom),
373}
374
375
376impl PrintAsCssValue for StyleScrollbarColor {
377 fn print_as_css_value(&self) -> String {
378 match self {
379 Self::Auto => "auto".to_string(),
380 Self::Custom(c) => format!("{} {}", c.thumb.to_hash(), c.track.to_hash()),
381 }
382 }
383}
384
385#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
389#[repr(C)]
390pub struct ScrollbarInfo {
391 pub width: LayoutWidth,
393 pub padding_left: LayoutPaddingLeft,
396 pub padding_right: LayoutPaddingRight,
398 pub track: StyleBackgroundContent,
402 pub thumb: StyleBackgroundContent,
404 pub button: StyleBackgroundContent,
406 pub corner: StyleBackgroundContent,
409 pub resizer: StyleBackgroundContent,
412 pub clip_to_container_border: bool,
417 pub scroll_behavior: ScrollBehavior,
419 pub overscroll_behavior_x: OverscrollBehavior,
421 pub overscroll_behavior_y: OverscrollBehavior,
423 pub overflow_scrolling: OverflowScrolling,
426}
427
428impl Default for ScrollbarInfo {
429 fn default() -> Self {
430 SCROLLBAR_CLASSIC_LIGHT
431 }
432}
433
434impl PrintAsCssValue for ScrollbarInfo {
435 fn print_as_css_value(&self) -> String {
436 format!(
438 "width: {}; padding-left: {}; padding-right: {}; track: {}; thumb: {}; button: {}; \
439 corner: {}; resizer: {}",
440 self.width.print_as_css_value(),
441 self.padding_left.print_as_css_value(),
442 self.padding_right.print_as_css_value(),
443 self.track.print_as_css_value(),
444 self.thumb.print_as_css_value(),
445 self.button.print_as_css_value(),
446 self.corner.print_as_css_value(),
447 self.resizer.print_as_css_value(),
448 )
449 }
450}
451
452#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
454#[repr(C)]
455pub struct ScrollbarStyle {
456 pub horizontal: ScrollbarInfo,
458 pub vertical: ScrollbarInfo,
460}
461
462impl PrintAsCssValue for ScrollbarStyle {
463 fn print_as_css_value(&self) -> String {
464 format!(
466 "horz({}), vert({})",
467 self.horizontal.print_as_css_value(),
468 self.vertical.print_as_css_value()
469 )
470 }
471}
472
473impl crate::format_rust_code::FormatAsRustCode for ScrollbarStyle {
475 fn format_as_rust_code(&self, tabs: usize) -> String {
476 let t = String::from(" ").repeat(tabs);
477 let t1 = String::from(" ").repeat(tabs + 1);
478 format!(
479 "ScrollbarStyle {{\r\n{}horizontal: {},\r\n{}vertical: {},\r\n{}}}",
480 t1,
481 crate::format_rust_code::format_scrollbar_info(&self.horizontal, tabs + 1),
482 t1,
483 crate::format_rust_code::format_scrollbar_info(&self.vertical, tabs + 1),
484 t,
485 )
486 }
487}
488
489impl crate::format_rust_code::FormatAsRustCode for LayoutScrollbarWidth {
490 fn format_as_rust_code(&self, _tabs: usize) -> String {
491 match self {
492 LayoutScrollbarWidth::Auto => String::from("LayoutScrollbarWidth::Auto"),
493 LayoutScrollbarWidth::Thin => String::from("LayoutScrollbarWidth::Thin"),
494 LayoutScrollbarWidth::None => String::from("LayoutScrollbarWidth::None"),
495 }
496 }
497}
498
499impl crate::format_rust_code::FormatAsRustCode for StyleScrollbarColor {
500 fn format_as_rust_code(&self, _tabs: usize) -> String {
501 match self {
502 StyleScrollbarColor::Auto => String::from("StyleScrollbarColor::Auto"),
503 StyleScrollbarColor::Custom(c) => format!(
504 "StyleScrollbarColor::Custom(ScrollbarColorCustom {{ thumb: {}, track: {} }})",
505 crate::format_rust_code::format_color_value(&c.thumb),
506 crate::format_rust_code::format_color_value(&c.track)
507 ),
508 }
509 }
510}
511
512impl crate::format_rust_code::FormatAsRustCode for ScrollbarVisibilityMode {
513 fn format_as_rust_code(&self, _tabs: usize) -> String {
514 match self {
515 ScrollbarVisibilityMode::Always => String::from("ScrollbarVisibilityMode::Always"),
516 ScrollbarVisibilityMode::WhenScrolling => String::from("ScrollbarVisibilityMode::WhenScrolling"),
517 ScrollbarVisibilityMode::Auto => String::from("ScrollbarVisibilityMode::Auto"),
518 }
519 }
520}
521
522impl crate::format_rust_code::FormatAsRustCode for ScrollbarFadeDelay {
523 fn format_as_rust_code(&self, _tabs: usize) -> String {
524 format!("ScrollbarFadeDelay::new({})", self.ms)
525 }
526}
527
528impl crate::format_rust_code::FormatAsRustCode for ScrollbarFadeDuration {
529 fn format_as_rust_code(&self, _tabs: usize) -> String {
530 format!("ScrollbarFadeDuration::new({})", self.ms)
531 }
532}
533
534#[derive(Debug, Clone, PartialEq)]
539pub struct ComputedScrollbarStyle {
540 pub width: Option<LayoutWidth>,
542 pub thumb_color: Option<ColorU>,
544 pub track_color: Option<ColorU>,
546}
547
548impl Default for ComputedScrollbarStyle {
549 fn default() -> Self {
550 let default_info = ScrollbarInfo::default();
551 Self {
552 width: Some(default_info.width), thumb_color: match default_info.thumb {
554 StyleBackgroundContent::Color(c) => Some(c),
555 _ => None,
556 },
557 track_color: match default_info.track {
558 StyleBackgroundContent::Color(c) => Some(c),
559 _ => None,
560 },
561 }
562 }
563}
564
565pub const SCROLLBAR_CLASSIC_LIGHT: ScrollbarInfo = ScrollbarInfo {
569 width: LayoutWidth::Px(crate::props::basic::pixel::PixelValue::const_px(17)),
570 padding_left: LayoutPaddingLeft {
571 inner: crate::props::basic::pixel::PixelValue::const_px(2),
572 },
573 padding_right: LayoutPaddingRight {
574 inner: crate::props::basic::pixel::PixelValue::const_px(2),
575 },
576 track: StyleBackgroundContent::Color(ColorU {
577 r: 241,
578 g: 241,
579 b: 241,
580 a: 255,
581 }),
582 thumb: StyleBackgroundContent::Color(ColorU {
583 r: 193,
584 g: 193,
585 b: 193,
586 a: 255,
587 }),
588 button: StyleBackgroundContent::Color(ColorU {
589 r: 163,
590 g: 163,
591 b: 163,
592 a: 255,
593 }),
594 corner: StyleBackgroundContent::Color(ColorU {
595 r: 241,
596 g: 241,
597 b: 241,
598 a: 255,
599 }),
600 resizer: StyleBackgroundContent::Color(ColorU {
601 r: 241,
602 g: 241,
603 b: 241,
604 a: 255,
605 }),
606 clip_to_container_border: false,
607 scroll_behavior: ScrollBehavior::Auto,
608 overscroll_behavior_x: OverscrollBehavior::Auto,
609 overscroll_behavior_y: OverscrollBehavior::Auto,
610 overflow_scrolling: OverflowScrolling::Auto,
611};
612
613pub const SCROLLBAR_CLASSIC_DARK: ScrollbarInfo = ScrollbarInfo {
615 width: LayoutWidth::Px(crate::props::basic::pixel::PixelValue::const_px(17)),
616 padding_left: LayoutPaddingLeft {
617 inner: crate::props::basic::pixel::PixelValue::const_px(2),
618 },
619 padding_right: LayoutPaddingRight {
620 inner: crate::props::basic::pixel::PixelValue::const_px(2),
621 },
622 track: StyleBackgroundContent::Color(ColorU {
623 r: 45,
624 g: 45,
625 b: 45,
626 a: 255,
627 }),
628 thumb: StyleBackgroundContent::Color(ColorU {
629 r: 100,
630 g: 100,
631 b: 100,
632 a: 255,
633 }),
634 button: StyleBackgroundContent::Color(ColorU {
635 r: 120,
636 g: 120,
637 b: 120,
638 a: 255,
639 }),
640 corner: StyleBackgroundContent::Color(ColorU {
641 r: 45,
642 g: 45,
643 b: 45,
644 a: 255,
645 }),
646 resizer: StyleBackgroundContent::Color(ColorU {
647 r: 45,
648 g: 45,
649 b: 45,
650 a: 255,
651 }),
652 clip_to_container_border: false,
653 scroll_behavior: ScrollBehavior::Auto,
654 overscroll_behavior_x: OverscrollBehavior::Auto,
655 overscroll_behavior_y: OverscrollBehavior::Auto,
656 overflow_scrolling: OverflowScrolling::Auto,
657};
658
659pub const SCROLLBAR_MACOS_LIGHT: ScrollbarInfo = ScrollbarInfo {
661 width: LayoutWidth::Px(crate::props::basic::pixel::PixelValue::const_px(8)),
662 padding_left: LayoutPaddingLeft {
663 inner: crate::props::basic::pixel::PixelValue::const_px(0),
664 },
665 padding_right: LayoutPaddingRight {
666 inner: crate::props::basic::pixel::PixelValue::const_px(0),
667 },
668 track: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
669 thumb: StyleBackgroundContent::Color(ColorU {
670 r: 0,
671 g: 0,
672 b: 0,
673 a: 100,
674 }), button: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
676 corner: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
677 resizer: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
678 clip_to_container_border: true, scroll_behavior: ScrollBehavior::Smooth,
680 overscroll_behavior_x: OverscrollBehavior::Auto,
681 overscroll_behavior_y: OverscrollBehavior::Auto,
682 overflow_scrolling: OverflowScrolling::Auto,
683};
684
685pub const SCROLLBAR_MACOS_DARK: ScrollbarInfo = ScrollbarInfo {
687 width: LayoutWidth::Px(crate::props::basic::pixel::PixelValue::const_px(8)),
688 padding_left: LayoutPaddingLeft {
689 inner: crate::props::basic::pixel::PixelValue::const_px(0),
690 },
691 padding_right: LayoutPaddingRight {
692 inner: crate::props::basic::pixel::PixelValue::const_px(0),
693 },
694 track: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
695 thumb: StyleBackgroundContent::Color(ColorU {
696 r: 255,
697 g: 255,
698 b: 255,
699 a: 100,
700 }), button: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
702 corner: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
703 resizer: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
704 clip_to_container_border: true, scroll_behavior: ScrollBehavior::Smooth,
706 overscroll_behavior_x: OverscrollBehavior::Auto,
707 overscroll_behavior_y: OverscrollBehavior::Auto,
708 overflow_scrolling: OverflowScrolling::Auto,
709};
710
711pub const SCROLLBAR_WINDOWS_LIGHT: ScrollbarInfo = ScrollbarInfo {
713 width: LayoutWidth::Px(crate::props::basic::pixel::PixelValue::const_px(12)),
714 padding_left: LayoutPaddingLeft {
715 inner: crate::props::basic::pixel::PixelValue::const_px(0),
716 },
717 padding_right: LayoutPaddingRight {
718 inner: crate::props::basic::pixel::PixelValue::const_px(0),
719 },
720 track: StyleBackgroundContent::Color(ColorU {
721 r: 241,
722 g: 241,
723 b: 241,
724 a: 255,
725 }),
726 thumb: StyleBackgroundContent::Color(ColorU {
727 r: 130,
728 g: 130,
729 b: 130,
730 a: 255,
731 }),
732 button: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
733 corner: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
734 resizer: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
735 clip_to_container_border: false,
736 scroll_behavior: ScrollBehavior::Auto,
737 overscroll_behavior_x: OverscrollBehavior::None,
738 overscroll_behavior_y: OverscrollBehavior::None,
739 overflow_scrolling: OverflowScrolling::Auto,
740};
741
742pub const SCROLLBAR_WINDOWS_DARK: ScrollbarInfo = ScrollbarInfo {
744 width: LayoutWidth::Px(crate::props::basic::pixel::PixelValue::const_px(12)),
745 padding_left: LayoutPaddingLeft {
746 inner: crate::props::basic::pixel::PixelValue::const_px(0),
747 },
748 padding_right: LayoutPaddingRight {
749 inner: crate::props::basic::pixel::PixelValue::const_px(0),
750 },
751 track: StyleBackgroundContent::Color(ColorU {
752 r: 32,
753 g: 32,
754 b: 32,
755 a: 255,
756 }),
757 thumb: StyleBackgroundContent::Color(ColorU {
758 r: 110,
759 g: 110,
760 b: 110,
761 a: 255,
762 }),
763 button: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
764 corner: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
765 resizer: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
766 clip_to_container_border: false,
767 scroll_behavior: ScrollBehavior::Auto,
768 overscroll_behavior_x: OverscrollBehavior::None,
769 overscroll_behavior_y: OverscrollBehavior::None,
770 overflow_scrolling: OverflowScrolling::Auto,
771};
772
773pub const SCROLLBAR_IOS_LIGHT: ScrollbarInfo = ScrollbarInfo {
775 width: LayoutWidth::Px(crate::props::basic::pixel::PixelValue::const_px(7)),
776 padding_left: LayoutPaddingLeft {
777 inner: crate::props::basic::pixel::PixelValue::const_px(0),
778 },
779 padding_right: LayoutPaddingRight {
780 inner: crate::props::basic::pixel::PixelValue::const_px(0),
781 },
782 track: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
783 thumb: StyleBackgroundContent::Color(ColorU {
784 r: 0,
785 g: 0,
786 b: 0,
787 a: 102,
788 }), button: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
790 corner: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
791 resizer: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
792 clip_to_container_border: true, scroll_behavior: ScrollBehavior::Smooth,
794 overscroll_behavior_x: OverscrollBehavior::Auto,
795 overscroll_behavior_y: OverscrollBehavior::Auto,
796 overflow_scrolling: OverflowScrolling::Auto,
797};
798
799pub const SCROLLBAR_IOS_DARK: ScrollbarInfo = ScrollbarInfo {
801 width: LayoutWidth::Px(crate::props::basic::pixel::PixelValue::const_px(7)),
802 padding_left: LayoutPaddingLeft {
803 inner: crate::props::basic::pixel::PixelValue::const_px(0),
804 },
805 padding_right: LayoutPaddingRight {
806 inner: crate::props::basic::pixel::PixelValue::const_px(0),
807 },
808 track: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
809 thumb: StyleBackgroundContent::Color(ColorU {
810 r: 255,
811 g: 255,
812 b: 255,
813 a: 102,
814 }), button: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
816 corner: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
817 resizer: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
818 clip_to_container_border: true, scroll_behavior: ScrollBehavior::Smooth,
820 overscroll_behavior_x: OverscrollBehavior::Auto,
821 overscroll_behavior_y: OverscrollBehavior::Auto,
822 overflow_scrolling: OverflowScrolling::Auto,
823};
824
825pub const SCROLLBAR_ANDROID_LIGHT: ScrollbarInfo = ScrollbarInfo {
827 width: LayoutWidth::Px(crate::props::basic::pixel::PixelValue::const_px(6)),
828 padding_left: LayoutPaddingLeft {
829 inner: crate::props::basic::pixel::PixelValue::const_px(0),
830 },
831 padding_right: LayoutPaddingRight {
832 inner: crate::props::basic::pixel::PixelValue::const_px(0),
833 },
834 track: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
835 thumb: StyleBackgroundContent::Color(ColorU {
836 r: 0,
837 g: 0,
838 b: 0,
839 a: 102,
840 }), button: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
842 corner: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
843 resizer: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
844 clip_to_container_border: true, scroll_behavior: ScrollBehavior::Smooth,
846 overscroll_behavior_x: OverscrollBehavior::Contain,
847 overscroll_behavior_y: OverscrollBehavior::Auto,
848 overflow_scrolling: OverflowScrolling::Auto,
849};
850
851pub const SCROLLBAR_ANDROID_DARK: ScrollbarInfo = ScrollbarInfo {
853 width: LayoutWidth::Px(crate::props::basic::pixel::PixelValue::const_px(6)),
854 padding_left: LayoutPaddingLeft {
855 inner: crate::props::basic::pixel::PixelValue::const_px(0),
856 },
857 padding_right: LayoutPaddingRight {
858 inner: crate::props::basic::pixel::PixelValue::const_px(0),
859 },
860 track: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
861 thumb: StyleBackgroundContent::Color(ColorU {
862 r: 255,
863 g: 255,
864 b: 255,
865 a: 102,
866 }), button: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
868 corner: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
869 resizer: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
870 clip_to_container_border: true, scroll_behavior: ScrollBehavior::Smooth,
872 overscroll_behavior_x: OverscrollBehavior::Contain,
873 overscroll_behavior_y: OverscrollBehavior::Auto,
874 overflow_scrolling: OverflowScrolling::Auto,
875};
876
877#[derive(Clone, PartialEq)]
880pub enum LayoutScrollbarWidthParseError<'a> {
881 InvalidValue(&'a str),
882}
883impl_debug_as_display!(LayoutScrollbarWidthParseError<'a>);
884impl_display! { LayoutScrollbarWidthParseError<'a>, {
885 InvalidValue(v) => format!("Invalid scrollbar-width value: \"{}\"", v),
886}}
887
888#[derive(Debug, Clone, PartialEq)]
889#[repr(C, u8)]
890pub enum LayoutScrollbarWidthParseErrorOwned {
891 InvalidValue(AzString),
892}
893impl<'a> LayoutScrollbarWidthParseError<'a> {
894 pub fn to_contained(&self) -> LayoutScrollbarWidthParseErrorOwned {
895 match self {
896 Self::InvalidValue(s) => {
897 LayoutScrollbarWidthParseErrorOwned::InvalidValue(s.to_string().into())
898 }
899 }
900 }
901}
902impl LayoutScrollbarWidthParseErrorOwned {
903 pub fn to_shared<'a>(&'a self) -> LayoutScrollbarWidthParseError<'a> {
904 match self {
905 Self::InvalidValue(s) => LayoutScrollbarWidthParseError::InvalidValue(s.as_str()),
906 }
907 }
908}
909
910#[cfg(feature = "parser")]
911pub fn parse_layout_scrollbar_width<'a>(
912 input: &'a str,
913) -> Result<LayoutScrollbarWidth, LayoutScrollbarWidthParseError<'a>> {
914 match input.trim() {
915 "auto" => Ok(LayoutScrollbarWidth::Auto),
916 "thin" => Ok(LayoutScrollbarWidth::Thin),
917 "none" => Ok(LayoutScrollbarWidth::None),
918 _ => Err(LayoutScrollbarWidthParseError::InvalidValue(input)),
919 }
920}
921
922#[derive(Clone, PartialEq)]
923pub enum StyleScrollbarColorParseError<'a> {
924 InvalidValue(&'a str),
925 Color(CssColorParseError<'a>),
926}
927impl_debug_as_display!(StyleScrollbarColorParseError<'a>);
928impl_display! { StyleScrollbarColorParseError<'a>, {
929 InvalidValue(v) => format!("Invalid scrollbar-color value: \"{}\"", v),
930 Color(e) => format!("Invalid color in scrollbar-color: {}", e),
931}}
932impl_from!(CssColorParseError<'a>, StyleScrollbarColorParseError::Color);
933
934#[derive(Debug, Clone, PartialEq)]
935#[repr(C, u8)]
936pub enum StyleScrollbarColorParseErrorOwned {
937 InvalidValue(AzString),
938 Color(CssColorParseErrorOwned),
939}
940impl<'a> StyleScrollbarColorParseError<'a> {
941 pub fn to_contained(&self) -> StyleScrollbarColorParseErrorOwned {
942 match self {
943 Self::InvalidValue(s) => {
944 StyleScrollbarColorParseErrorOwned::InvalidValue(s.to_string().into())
945 }
946 Self::Color(e) => StyleScrollbarColorParseErrorOwned::Color(e.to_contained()),
947 }
948 }
949}
950impl StyleScrollbarColorParseErrorOwned {
951 pub fn to_shared<'a>(&'a self) -> StyleScrollbarColorParseError<'a> {
952 match self {
953 Self::InvalidValue(s) => StyleScrollbarColorParseError::InvalidValue(s.as_str()),
954 Self::Color(e) => StyleScrollbarColorParseError::Color(e.to_shared()),
955 }
956 }
957}
958
959#[cfg(feature = "parser")]
960pub fn parse_style_scrollbar_color<'a>(
961 input: &'a str,
962) -> Result<StyleScrollbarColor, StyleScrollbarColorParseError<'a>> {
963 let input = input.trim();
964 if input == "auto" {
965 return Ok(StyleScrollbarColor::Auto);
966 }
967
968 let mut parts = input.split_whitespace();
969 let thumb_str = parts
970 .next()
971 .ok_or(StyleScrollbarColorParseError::InvalidValue(input))?;
972 let track_str = parts
973 .next()
974 .ok_or(StyleScrollbarColorParseError::InvalidValue(input))?;
975
976 if parts.next().is_some() {
977 return Err(StyleScrollbarColorParseError::InvalidValue(input));
978 }
979
980 let thumb = parse_css_color(thumb_str)?;
981 let track = parse_css_color(track_str)?;
982
983 Ok(StyleScrollbarColor::Custom(ScrollbarColorCustom {
984 thumb,
985 track,
986 }))
987}
988
989#[derive(Clone, PartialEq)]
992pub enum ScrollbarVisibilityModeParseError<'a> {
993 InvalidValue(&'a str),
994}
995impl_debug_as_display!(ScrollbarVisibilityModeParseError<'a>);
996impl_display! { ScrollbarVisibilityModeParseError<'a>, {
997 InvalidValue(v) => format!("Invalid scrollbar-visibility value: \"{}\"", v),
998}}
999
1000#[derive(Debug, Clone, PartialEq)]
1001#[repr(C, u8)]
1002pub enum ScrollbarVisibilityModeParseErrorOwned {
1003 InvalidValue(AzString),
1004}
1005impl<'a> ScrollbarVisibilityModeParseError<'a> {
1006 pub fn to_contained(&self) -> ScrollbarVisibilityModeParseErrorOwned {
1007 match self {
1008 Self::InvalidValue(s) => ScrollbarVisibilityModeParseErrorOwned::InvalidValue(s.to_string().into()),
1009 }
1010 }
1011}
1012impl ScrollbarVisibilityModeParseErrorOwned {
1013 pub fn to_shared<'a>(&'a self) -> ScrollbarVisibilityModeParseError<'a> {
1014 match self {
1015 Self::InvalidValue(s) => ScrollbarVisibilityModeParseError::InvalidValue(s.as_str()),
1016 }
1017 }
1018}
1019
1020#[cfg(feature = "parser")]
1021pub fn parse_scrollbar_visibility_mode<'a>(
1022 input: &'a str,
1023) -> Result<ScrollbarVisibilityMode, ScrollbarVisibilityModeParseError<'a>> {
1024 match input.trim() {
1025 "always" => Ok(ScrollbarVisibilityMode::Always),
1026 "when-scrolling" => Ok(ScrollbarVisibilityMode::WhenScrolling),
1027 "auto" => Ok(ScrollbarVisibilityMode::Auto),
1028 _ => Err(ScrollbarVisibilityModeParseError::InvalidValue(input)),
1029 }
1030}
1031
1032#[derive(Clone, PartialEq)]
1035pub enum ScrollbarFadeDelayParseError<'a> {
1036 InvalidValue(&'a str),
1037}
1038impl_debug_as_display!(ScrollbarFadeDelayParseError<'a>);
1039impl_display! { ScrollbarFadeDelayParseError<'a>, {
1040 InvalidValue(v) => format!("Invalid scrollbar-fade-delay value: \"{}\"", v),
1041}}
1042
1043#[derive(Debug, Clone, PartialEq)]
1044#[repr(C, u8)]
1045pub enum ScrollbarFadeDelayParseErrorOwned {
1046 InvalidValue(AzString),
1047}
1048impl<'a> ScrollbarFadeDelayParseError<'a> {
1049 pub fn to_contained(&self) -> ScrollbarFadeDelayParseErrorOwned {
1050 match self {
1051 Self::InvalidValue(s) => ScrollbarFadeDelayParseErrorOwned::InvalidValue(s.to_string().into()),
1052 }
1053 }
1054}
1055impl ScrollbarFadeDelayParseErrorOwned {
1056 pub fn to_shared<'a>(&'a self) -> ScrollbarFadeDelayParseError<'a> {
1057 match self {
1058 Self::InvalidValue(s) => ScrollbarFadeDelayParseError::InvalidValue(s.as_str()),
1059 }
1060 }
1061}
1062
1063#[cfg(feature = "parser")]
1064fn parse_time_ms(input: &str) -> Option<u32> {
1065 crate::props::basic::time::parse_duration(input).ok().map(|d| d.inner)
1066}
1067
1068#[cfg(feature = "parser")]
1069pub fn parse_scrollbar_fade_delay<'a>(
1070 input: &'a str,
1071) -> Result<ScrollbarFadeDelay, ScrollbarFadeDelayParseError<'a>> {
1072 parse_time_ms(input)
1073 .map(ScrollbarFadeDelay::new)
1074 .ok_or(ScrollbarFadeDelayParseError::InvalidValue(input))
1075}
1076
1077#[derive(Clone, PartialEq)]
1080pub enum ScrollbarFadeDurationParseError<'a> {
1081 InvalidValue(&'a str),
1082}
1083impl_debug_as_display!(ScrollbarFadeDurationParseError<'a>);
1084impl_display! { ScrollbarFadeDurationParseError<'a>, {
1085 InvalidValue(v) => format!("Invalid scrollbar-fade-duration value: \"{}\"", v),
1086}}
1087
1088#[derive(Debug, Clone, PartialEq)]
1089#[repr(C, u8)]
1090pub enum ScrollbarFadeDurationParseErrorOwned {
1091 InvalidValue(AzString),
1092}
1093impl<'a> ScrollbarFadeDurationParseError<'a> {
1094 pub fn to_contained(&self) -> ScrollbarFadeDurationParseErrorOwned {
1095 match self {
1096 Self::InvalidValue(s) => ScrollbarFadeDurationParseErrorOwned::InvalidValue(s.to_string().into()),
1097 }
1098 }
1099}
1100impl ScrollbarFadeDurationParseErrorOwned {
1101 pub fn to_shared<'a>(&'a self) -> ScrollbarFadeDurationParseError<'a> {
1102 match self {
1103 Self::InvalidValue(s) => ScrollbarFadeDurationParseError::InvalidValue(s.as_str()),
1104 }
1105 }
1106}
1107
1108#[cfg(feature = "parser")]
1109pub fn parse_scrollbar_fade_duration<'a>(
1110 input: &'a str,
1111) -> Result<ScrollbarFadeDuration, ScrollbarFadeDurationParseError<'a>> {
1112 parse_time_ms(input)
1113 .map(ScrollbarFadeDuration::new)
1114 .ok_or(ScrollbarFadeDurationParseError::InvalidValue(input))
1115}
1116
1117#[cfg(all(test, feature = "parser"))]
1118mod tests {
1119 use super::*;
1120 use crate::props::basic::color::ColorU;
1121
1122 #[test]
1123 fn test_parse_scrollbar_width() {
1124 assert_eq!(
1125 parse_layout_scrollbar_width("auto").unwrap(),
1126 LayoutScrollbarWidth::Auto
1127 );
1128 assert_eq!(
1129 parse_layout_scrollbar_width("thin").unwrap(),
1130 LayoutScrollbarWidth::Thin
1131 );
1132 assert_eq!(
1133 parse_layout_scrollbar_width("none").unwrap(),
1134 LayoutScrollbarWidth::None
1135 );
1136 assert!(parse_layout_scrollbar_width("thick").is_err());
1137 }
1138
1139 #[test]
1140 fn test_parse_scrollbar_color() {
1141 assert_eq!(
1142 parse_style_scrollbar_color("auto").unwrap(),
1143 StyleScrollbarColor::Auto
1144 );
1145
1146 let custom = parse_style_scrollbar_color("red blue").unwrap();
1147 assert_eq!(
1148 custom,
1149 StyleScrollbarColor::Custom(ScrollbarColorCustom {
1150 thumb: ColorU::RED,
1151 track: ColorU::BLUE
1152 })
1153 );
1154
1155 let custom_hex = parse_style_scrollbar_color("#ff0000 #0000ff").unwrap();
1156 assert_eq!(
1157 custom_hex,
1158 StyleScrollbarColor::Custom(ScrollbarColorCustom {
1159 thumb: ColorU::RED,
1160 track: ColorU::BLUE
1161 })
1162 );
1163
1164 assert!(parse_style_scrollbar_color("red").is_err());
1165 assert!(parse_style_scrollbar_color("red blue green").is_err());
1166 }
1167}