use alloc::string::{String, ToString};
use crate::props::{
basic::{
color::{parse_css_color, ColorU, CssColorParseError, CssColorParseErrorOwned},
pixel::PixelValue,
},
formatter::PrintAsCssValue,
layout::{
dimensions::LayoutWidth,
spacing::{LayoutPaddingLeft, LayoutPaddingRight},
},
style::background::StyleBackgroundContent,
};
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
#[repr(C)]
pub enum ScrollBehavior {
#[default]
Auto,
Smooth,
}
impl PrintAsCssValue for ScrollBehavior {
fn print_as_css_value(&self) -> String {
match self {
Self::Auto => "auto".to_string(),
Self::Smooth => "smooth".to_string(),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
#[repr(C)]
pub enum OverscrollBehavior {
#[default]
Auto,
Contain,
None,
}
impl PrintAsCssValue for OverscrollBehavior {
fn print_as_css_value(&self) -> String {
match self {
Self::Auto => "auto".to_string(),
Self::Contain => "contain".to_string(),
Self::None => "none".to_string(),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
#[repr(C)]
pub struct OverscrollBehaviorXY {
pub x: OverscrollBehavior,
pub y: OverscrollBehavior,
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
#[repr(C)]
pub struct ScrollPhysics {
pub smooth_scroll_duration_ms: u32,
pub deceleration_rate: f32,
pub min_velocity_threshold: f32,
pub max_velocity: f32,
pub wheel_multiplier: f32,
pub invert_direction: bool,
pub overscroll_elasticity: f32,
pub max_overscroll_distance: f32,
pub bounce_back_duration_ms: u32,
pub timer_interval_ms: u32,
}
impl Default for ScrollPhysics {
fn default() -> Self {
Self {
smooth_scroll_duration_ms: 300,
deceleration_rate: 0.95,
min_velocity_threshold: 50.0,
max_velocity: 8000.0,
wheel_multiplier: 1.0,
invert_direction: false,
overscroll_elasticity: 0.0, max_overscroll_distance: 100.0,
bounce_back_duration_ms: 400,
timer_interval_ms: 16,
}
}
}
impl ScrollPhysics {
pub const fn ios() -> Self {
Self {
smooth_scroll_duration_ms: 300,
deceleration_rate: 0.998,
min_velocity_threshold: 20.0,
max_velocity: 8000.0,
wheel_multiplier: 1.0,
invert_direction: true, overscroll_elasticity: 0.5,
max_overscroll_distance: 120.0,
bounce_back_duration_ms: 500,
timer_interval_ms: 16,
}
}
pub const fn macos() -> Self {
Self {
smooth_scroll_duration_ms: 250,
deceleration_rate: 0.997,
min_velocity_threshold: 30.0,
max_velocity: 6000.0,
wheel_multiplier: 1.0,
invert_direction: true, overscroll_elasticity: 0.3,
max_overscroll_distance: 80.0,
bounce_back_duration_ms: 400,
timer_interval_ms: 16,
}
}
pub const fn windows() -> Self {
Self {
smooth_scroll_duration_ms: 200,
deceleration_rate: 0.9,
min_velocity_threshold: 100.0,
max_velocity: 4000.0,
wheel_multiplier: 1.0,
invert_direction: false,
overscroll_elasticity: 0.0,
max_overscroll_distance: 0.0,
bounce_back_duration_ms: 200,
timer_interval_ms: 16,
}
}
pub const fn android() -> Self {
Self {
smooth_scroll_duration_ms: 250,
deceleration_rate: 0.996,
min_velocity_threshold: 40.0,
max_velocity: 8000.0,
wheel_multiplier: 1.0,
invert_direction: false,
overscroll_elasticity: 0.2, max_overscroll_distance: 60.0,
bounce_back_duration_ms: 300,
timer_interval_ms: 16,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
#[repr(C)]
pub enum OverflowScrolling {
#[default]
Auto,
Touch,
}
impl PrintAsCssValue for OverflowScrolling {
fn print_as_css_value(&self) -> String {
match self {
Self::Auto => "auto".to_string(),
Self::Touch => "touch".to_string(),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub enum LayoutScrollbarWidth {
Auto,
Thin,
None,
}
impl Default for LayoutScrollbarWidth {
fn default() -> Self {
Self::Auto
}
}
impl PrintAsCssValue for LayoutScrollbarWidth {
fn print_as_css_value(&self) -> String {
match self {
Self::Auto => "auto".to_string(),
Self::Thin => "thin".to_string(),
Self::None => "none".to_string(),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub struct ScrollbarColorCustom {
pub thumb: ColorU,
pub track: ColorU,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C, u8)]
pub enum StyleScrollbarColor {
Auto,
Custom(ScrollbarColorCustom),
}
impl Default for StyleScrollbarColor {
fn default() -> Self {
Self::Auto
}
}
impl PrintAsCssValue for StyleScrollbarColor {
fn print_as_css_value(&self) -> String {
match self {
Self::Auto => "auto".to_string(),
Self::Custom(c) => format!("{} {}", c.thumb.to_hash(), c.track.to_hash()),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub struct ScrollbarInfo {
pub width: LayoutWidth,
pub padding_left: LayoutPaddingLeft,
pub padding_right: LayoutPaddingRight,
pub track: StyleBackgroundContent,
pub thumb: StyleBackgroundContent,
pub button: StyleBackgroundContent,
pub corner: StyleBackgroundContent,
pub resizer: StyleBackgroundContent,
pub clip_to_container_border: bool,
pub scroll_behavior: ScrollBehavior,
pub overscroll_behavior_x: OverscrollBehavior,
pub overscroll_behavior_y: OverscrollBehavior,
pub overflow_scrolling: OverflowScrolling,
}
impl Default for ScrollbarInfo {
fn default() -> Self {
SCROLLBAR_CLASSIC_LIGHT
}
}
impl PrintAsCssValue for ScrollbarInfo {
fn print_as_css_value(&self) -> String {
format!(
"width: {}; padding-left: {}; padding-right: {}; track: {}; thumb: {}; button: {}; \
corner: {}; resizer: {}",
self.width.print_as_css_value(),
self.padding_left.print_as_css_value(),
self.padding_right.print_as_css_value(),
self.track.print_as_css_value(),
self.thumb.print_as_css_value(),
self.button.print_as_css_value(),
self.corner.print_as_css_value(),
self.resizer.print_as_css_value(),
)
}
}
#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub struct ScrollbarStyle {
pub horizontal: ScrollbarInfo,
pub vertical: ScrollbarInfo,
}
impl PrintAsCssValue for ScrollbarStyle {
fn print_as_css_value(&self) -> String {
format!(
"horz({}), vert({})",
self.horizontal.print_as_css_value(),
self.vertical.print_as_css_value()
)
}
}
impl crate::format_rust_code::FormatAsRustCode for ScrollbarStyle {
fn format_as_rust_code(&self, tabs: usize) -> String {
let t = String::from(" ").repeat(tabs);
let t1 = String::from(" ").repeat(tabs + 1);
format!(
"ScrollbarStyle {{\r\n{}horizontal: {},\r\n{}vertical: {},\r\n{}}}",
t1,
crate::format_rust_code::format_scrollbar_info(&self.horizontal, tabs + 1),
t1,
crate::format_rust_code::format_scrollbar_info(&self.vertical, tabs + 1),
t,
)
}
}
impl crate::format_rust_code::FormatAsRustCode for LayoutScrollbarWidth {
fn format_as_rust_code(&self, _tabs: usize) -> String {
match self {
LayoutScrollbarWidth::Auto => String::from("LayoutScrollbarWidth::Auto"),
LayoutScrollbarWidth::Thin => String::from("LayoutScrollbarWidth::Thin"),
LayoutScrollbarWidth::None => String::from("LayoutScrollbarWidth::None"),
}
}
}
impl crate::format_rust_code::FormatAsRustCode for StyleScrollbarColor {
fn format_as_rust_code(&self, _tabs: usize) -> String {
match self {
StyleScrollbarColor::Auto => String::from("StyleScrollbarColor::Auto"),
StyleScrollbarColor::Custom(c) => format!(
"StyleScrollbarColor::Custom(ScrollbarColorCustom {{ thumb: {}, track: {} }})",
crate::format_rust_code::format_color_value(&c.thumb),
crate::format_rust_code::format_color_value(&c.track)
),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ComputedScrollbarStyle {
pub width: Option<LayoutWidth>,
pub thumb_color: Option<ColorU>,
pub track_color: Option<ColorU>,
}
impl Default for ComputedScrollbarStyle {
fn default() -> Self {
let default_info = ScrollbarInfo::default();
Self {
width: Some(default_info.width), thumb_color: match default_info.thumb {
StyleBackgroundContent::Color(c) => Some(c),
_ => None,
},
track_color: match default_info.track {
StyleBackgroundContent::Color(c) => Some(c),
_ => None,
},
}
}
}
pub const SCROLLBAR_CLASSIC_LIGHT: ScrollbarInfo = ScrollbarInfo {
width: LayoutWidth::Px(crate::props::basic::pixel::PixelValue::const_px(17)),
padding_left: LayoutPaddingLeft {
inner: crate::props::basic::pixel::PixelValue::const_px(2),
},
padding_right: LayoutPaddingRight {
inner: crate::props::basic::pixel::PixelValue::const_px(2),
},
track: StyleBackgroundContent::Color(ColorU {
r: 241,
g: 241,
b: 241,
a: 255,
}),
thumb: StyleBackgroundContent::Color(ColorU {
r: 193,
g: 193,
b: 193,
a: 255,
}),
button: StyleBackgroundContent::Color(ColorU {
r: 163,
g: 163,
b: 163,
a: 255,
}),
corner: StyleBackgroundContent::Color(ColorU {
r: 241,
g: 241,
b: 241,
a: 255,
}),
resizer: StyleBackgroundContent::Color(ColorU {
r: 241,
g: 241,
b: 241,
a: 255,
}),
clip_to_container_border: false,
scroll_behavior: ScrollBehavior::Auto,
overscroll_behavior_x: OverscrollBehavior::Auto,
overscroll_behavior_y: OverscrollBehavior::Auto,
overflow_scrolling: OverflowScrolling::Auto,
};
pub const SCROLLBAR_CLASSIC_DARK: ScrollbarInfo = ScrollbarInfo {
width: LayoutWidth::Px(crate::props::basic::pixel::PixelValue::const_px(17)),
padding_left: LayoutPaddingLeft {
inner: crate::props::basic::pixel::PixelValue::const_px(2),
},
padding_right: LayoutPaddingRight {
inner: crate::props::basic::pixel::PixelValue::const_px(2),
},
track: StyleBackgroundContent::Color(ColorU {
r: 45,
g: 45,
b: 45,
a: 255,
}),
thumb: StyleBackgroundContent::Color(ColorU {
r: 100,
g: 100,
b: 100,
a: 255,
}),
button: StyleBackgroundContent::Color(ColorU {
r: 120,
g: 120,
b: 120,
a: 255,
}),
corner: StyleBackgroundContent::Color(ColorU {
r: 45,
g: 45,
b: 45,
a: 255,
}),
resizer: StyleBackgroundContent::Color(ColorU {
r: 45,
g: 45,
b: 45,
a: 255,
}),
clip_to_container_border: false,
scroll_behavior: ScrollBehavior::Auto,
overscroll_behavior_x: OverscrollBehavior::Auto,
overscroll_behavior_y: OverscrollBehavior::Auto,
overflow_scrolling: OverflowScrolling::Auto,
};
pub const SCROLLBAR_MACOS_LIGHT: ScrollbarInfo = ScrollbarInfo {
width: LayoutWidth::Px(crate::props::basic::pixel::PixelValue::const_px(8)),
padding_left: LayoutPaddingLeft {
inner: crate::props::basic::pixel::PixelValue::const_px(0),
},
padding_right: LayoutPaddingRight {
inner: crate::props::basic::pixel::PixelValue::const_px(0),
},
track: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
thumb: StyleBackgroundContent::Color(ColorU {
r: 0,
g: 0,
b: 0,
a: 100,
}), button: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
corner: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
resizer: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
clip_to_container_border: true, scroll_behavior: ScrollBehavior::Smooth,
overscroll_behavior_x: OverscrollBehavior::Auto,
overscroll_behavior_y: OverscrollBehavior::Auto,
overflow_scrolling: OverflowScrolling::Auto,
};
pub const SCROLLBAR_MACOS_DARK: ScrollbarInfo = ScrollbarInfo {
width: LayoutWidth::Px(crate::props::basic::pixel::PixelValue::const_px(8)),
padding_left: LayoutPaddingLeft {
inner: crate::props::basic::pixel::PixelValue::const_px(0),
},
padding_right: LayoutPaddingRight {
inner: crate::props::basic::pixel::PixelValue::const_px(0),
},
track: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
thumb: StyleBackgroundContent::Color(ColorU {
r: 255,
g: 255,
b: 255,
a: 100,
}), button: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
corner: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
resizer: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
clip_to_container_border: true, scroll_behavior: ScrollBehavior::Smooth,
overscroll_behavior_x: OverscrollBehavior::Auto,
overscroll_behavior_y: OverscrollBehavior::Auto,
overflow_scrolling: OverflowScrolling::Auto,
};
pub const SCROLLBAR_WINDOWS_LIGHT: ScrollbarInfo = ScrollbarInfo {
width: LayoutWidth::Px(crate::props::basic::pixel::PixelValue::const_px(12)),
padding_left: LayoutPaddingLeft {
inner: crate::props::basic::pixel::PixelValue::const_px(0),
},
padding_right: LayoutPaddingRight {
inner: crate::props::basic::pixel::PixelValue::const_px(0),
},
track: StyleBackgroundContent::Color(ColorU {
r: 241,
g: 241,
b: 241,
a: 255,
}),
thumb: StyleBackgroundContent::Color(ColorU {
r: 130,
g: 130,
b: 130,
a: 255,
}),
button: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
corner: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
resizer: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
clip_to_container_border: false,
scroll_behavior: ScrollBehavior::Auto,
overscroll_behavior_x: OverscrollBehavior::None,
overscroll_behavior_y: OverscrollBehavior::None,
overflow_scrolling: OverflowScrolling::Auto,
};
pub const SCROLLBAR_WINDOWS_DARK: ScrollbarInfo = ScrollbarInfo {
width: LayoutWidth::Px(crate::props::basic::pixel::PixelValue::const_px(12)),
padding_left: LayoutPaddingLeft {
inner: crate::props::basic::pixel::PixelValue::const_px(0),
},
padding_right: LayoutPaddingRight {
inner: crate::props::basic::pixel::PixelValue::const_px(0),
},
track: StyleBackgroundContent::Color(ColorU {
r: 32,
g: 32,
b: 32,
a: 255,
}),
thumb: StyleBackgroundContent::Color(ColorU {
r: 110,
g: 110,
b: 110,
a: 255,
}),
button: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
corner: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
resizer: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
clip_to_container_border: false,
scroll_behavior: ScrollBehavior::Auto,
overscroll_behavior_x: OverscrollBehavior::None,
overscroll_behavior_y: OverscrollBehavior::None,
overflow_scrolling: OverflowScrolling::Auto,
};
pub const SCROLLBAR_IOS_LIGHT: ScrollbarInfo = ScrollbarInfo {
width: LayoutWidth::Px(crate::props::basic::pixel::PixelValue::const_px(7)),
padding_left: LayoutPaddingLeft {
inner: crate::props::basic::pixel::PixelValue::const_px(0),
},
padding_right: LayoutPaddingRight {
inner: crate::props::basic::pixel::PixelValue::const_px(0),
},
track: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
thumb: StyleBackgroundContent::Color(ColorU {
r: 0,
g: 0,
b: 0,
a: 102,
}), button: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
corner: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
resizer: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
clip_to_container_border: true, scroll_behavior: ScrollBehavior::Smooth,
overscroll_behavior_x: OverscrollBehavior::Auto,
overscroll_behavior_y: OverscrollBehavior::Auto,
overflow_scrolling: OverflowScrolling::Auto,
};
pub const SCROLLBAR_IOS_DARK: ScrollbarInfo = ScrollbarInfo {
width: LayoutWidth::Px(crate::props::basic::pixel::PixelValue::const_px(7)),
padding_left: LayoutPaddingLeft {
inner: crate::props::basic::pixel::PixelValue::const_px(0),
},
padding_right: LayoutPaddingRight {
inner: crate::props::basic::pixel::PixelValue::const_px(0),
},
track: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
thumb: StyleBackgroundContent::Color(ColorU {
r: 255,
g: 255,
b: 255,
a: 102,
}), button: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
corner: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
resizer: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
clip_to_container_border: true, scroll_behavior: ScrollBehavior::Smooth,
overscroll_behavior_x: OverscrollBehavior::Auto,
overscroll_behavior_y: OverscrollBehavior::Auto,
overflow_scrolling: OverflowScrolling::Auto,
};
pub const SCROLLBAR_ANDROID_LIGHT: ScrollbarInfo = ScrollbarInfo {
width: LayoutWidth::Px(crate::props::basic::pixel::PixelValue::const_px(6)),
padding_left: LayoutPaddingLeft {
inner: crate::props::basic::pixel::PixelValue::const_px(0),
},
padding_right: LayoutPaddingRight {
inner: crate::props::basic::pixel::PixelValue::const_px(0),
},
track: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
thumb: StyleBackgroundContent::Color(ColorU {
r: 0,
g: 0,
b: 0,
a: 102,
}), button: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
corner: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
resizer: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
clip_to_container_border: true, scroll_behavior: ScrollBehavior::Smooth,
overscroll_behavior_x: OverscrollBehavior::Contain,
overscroll_behavior_y: OverscrollBehavior::Auto,
overflow_scrolling: OverflowScrolling::Auto,
};
pub const SCROLLBAR_ANDROID_DARK: ScrollbarInfo = ScrollbarInfo {
width: LayoutWidth::Px(crate::props::basic::pixel::PixelValue::const_px(6)),
padding_left: LayoutPaddingLeft {
inner: crate::props::basic::pixel::PixelValue::const_px(0),
},
padding_right: LayoutPaddingRight {
inner: crate::props::basic::pixel::PixelValue::const_px(0),
},
track: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
thumb: StyleBackgroundContent::Color(ColorU {
r: 255,
g: 255,
b: 255,
a: 102,
}), button: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
corner: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
resizer: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
clip_to_container_border: true, scroll_behavior: ScrollBehavior::Smooth,
overscroll_behavior_x: OverscrollBehavior::Contain,
overscroll_behavior_y: OverscrollBehavior::Auto,
overflow_scrolling: OverflowScrolling::Auto,
};
#[derive(Clone, PartialEq)]
pub enum LayoutScrollbarWidthParseError<'a> {
InvalidValue(&'a str),
}
impl_debug_as_display!(LayoutScrollbarWidthParseError<'a>);
impl_display! { LayoutScrollbarWidthParseError<'a>, {
InvalidValue(v) => format!("Invalid scrollbar-width value: \"{}\"", v),
}}
#[derive(Debug, Clone, PartialEq)]
pub enum LayoutScrollbarWidthParseErrorOwned {
InvalidValue(String),
}
impl<'a> LayoutScrollbarWidthParseError<'a> {
pub fn to_contained(&self) -> LayoutScrollbarWidthParseErrorOwned {
match self {
Self::InvalidValue(s) => {
LayoutScrollbarWidthParseErrorOwned::InvalidValue(s.to_string())
}
}
}
}
impl LayoutScrollbarWidthParseErrorOwned {
pub fn to_shared<'a>(&'a self) -> LayoutScrollbarWidthParseError<'a> {
match self {
Self::InvalidValue(s) => LayoutScrollbarWidthParseError::InvalidValue(s.as_str()),
}
}
}
#[cfg(feature = "parser")]
pub fn parse_layout_scrollbar_width<'a>(
input: &'a str,
) -> Result<LayoutScrollbarWidth, LayoutScrollbarWidthParseError<'a>> {
match input.trim() {
"auto" => Ok(LayoutScrollbarWidth::Auto),
"thin" => Ok(LayoutScrollbarWidth::Thin),
"none" => Ok(LayoutScrollbarWidth::None),
_ => Err(LayoutScrollbarWidthParseError::InvalidValue(input)),
}
}
#[derive(Clone, PartialEq)]
pub enum StyleScrollbarColorParseError<'a> {
InvalidValue(&'a str),
Color(CssColorParseError<'a>),
}
impl_debug_as_display!(StyleScrollbarColorParseError<'a>);
impl_display! { StyleScrollbarColorParseError<'a>, {
InvalidValue(v) => format!("Invalid scrollbar-color value: \"{}\"", v),
Color(e) => format!("Invalid color in scrollbar-color: {}", e),
}}
impl_from!(CssColorParseError<'a>, StyleScrollbarColorParseError::Color);
#[derive(Debug, Clone, PartialEq)]
pub enum StyleScrollbarColorParseErrorOwned {
InvalidValue(String),
Color(CssColorParseErrorOwned),
}
impl<'a> StyleScrollbarColorParseError<'a> {
pub fn to_contained(&self) -> StyleScrollbarColorParseErrorOwned {
match self {
Self::InvalidValue(s) => {
StyleScrollbarColorParseErrorOwned::InvalidValue(s.to_string())
}
Self::Color(e) => StyleScrollbarColorParseErrorOwned::Color(e.to_contained()),
}
}
}
impl StyleScrollbarColorParseErrorOwned {
pub fn to_shared<'a>(&'a self) -> StyleScrollbarColorParseError<'a> {
match self {
Self::InvalidValue(s) => StyleScrollbarColorParseError::InvalidValue(s.as_str()),
Self::Color(e) => StyleScrollbarColorParseError::Color(e.to_shared()),
}
}
}
#[cfg(feature = "parser")]
pub fn parse_style_scrollbar_color<'a>(
input: &'a str,
) -> Result<StyleScrollbarColor, StyleScrollbarColorParseError<'a>> {
let input = input.trim();
if input == "auto" {
return Ok(StyleScrollbarColor::Auto);
}
let mut parts = input.split_whitespace();
let thumb_str = parts
.next()
.ok_or(StyleScrollbarColorParseError::InvalidValue(input))?;
let track_str = parts
.next()
.ok_or(StyleScrollbarColorParseError::InvalidValue(input))?;
if parts.next().is_some() {
return Err(StyleScrollbarColorParseError::InvalidValue(input));
}
let thumb = parse_css_color(thumb_str)?;
let track = parse_css_color(track_str)?;
Ok(StyleScrollbarColor::Custom(ScrollbarColorCustom {
thumb,
track,
}))
}
#[derive(Clone, PartialEq)]
pub enum CssScrollbarStyleParseError<'a> {
Invalid(&'a str),
}
impl_debug_as_display!(CssScrollbarStyleParseError<'a>);
impl_display! { CssScrollbarStyleParseError<'a>, {
Invalid(e) => format!("Invalid scrollbar style: \"{}\"", e),
}}
#[derive(Debug, Clone, PartialEq)]
pub enum CssScrollbarStyleParseErrorOwned {
Invalid(String),
}
impl<'a> CssScrollbarStyleParseError<'a> {
pub fn to_contained(&self) -> CssScrollbarStyleParseErrorOwned {
match self {
CssScrollbarStyleParseError::Invalid(s) => {
CssScrollbarStyleParseErrorOwned::Invalid(s.to_string())
}
}
}
}
impl CssScrollbarStyleParseErrorOwned {
pub fn to_shared<'a>(&'a self) -> CssScrollbarStyleParseError<'a> {
match self {
CssScrollbarStyleParseErrorOwned::Invalid(s) => {
CssScrollbarStyleParseError::Invalid(s.as_str())
}
}
}
}
#[cfg(feature = "parser")]
pub fn parse_scrollbar_style<'a>(
_input: &'a str,
) -> Result<ScrollbarStyle, CssScrollbarStyleParseError<'a>> {
Ok(ScrollbarStyle::default())
}
pub fn resolve_scrollbar_style(
scrollbar_width: Option<&LayoutScrollbarWidth>,
scrollbar_color: Option<&StyleScrollbarColor>,
webkit_scrollbar_style: Option<&ScrollbarStyle>,
) -> ComputedScrollbarStyle {
let final_width = scrollbar_width
.cloned()
.unwrap_or(LayoutScrollbarWidth::Auto);
let final_color = scrollbar_color
.cloned()
.unwrap_or(StyleScrollbarColor::Auto);
if final_width != LayoutScrollbarWidth::Auto || final_color != StyleScrollbarColor::Auto {
let width = match final_width {
LayoutScrollbarWidth::None => None,
LayoutScrollbarWidth::Thin => Some(LayoutWidth::Px(PixelValue::px(8.0))),
LayoutScrollbarWidth::Auto => Some(
webkit_scrollbar_style
.map_or_else(|| ScrollbarInfo::default().width, |s| s.vertical.width.clone()),
),
};
let (thumb_color, track_color) = match final_color {
StyleScrollbarColor::Custom(c) => (Some(c.thumb), Some(c.track)),
StyleScrollbarColor::Auto => (None, None), };
return ComputedScrollbarStyle {
width: width.clone(),
thumb_color,
track_color,
};
}
if let Some(webkit_style) = webkit_scrollbar_style {
let info = &webkit_style.vertical;
let width_pixels = match info.width {
LayoutWidth::Px(px) => {
use crate::props::basic::pixel::DEFAULT_FONT_SIZE;
px.to_pixels_internal(0.0, DEFAULT_FONT_SIZE)
}
_ => 8.0, };
if width_pixels <= 0.0 {
return ComputedScrollbarStyle {
width: None,
thumb_color: None,
track_color: None,
};
}
let thumb = match &info.thumb {
StyleBackgroundContent::Color(c) => Some(*c),
_ => None, };
let track = match &info.track {
StyleBackgroundContent::Color(c) => Some(*c),
_ => None,
};
return ComputedScrollbarStyle {
width: Some(info.width.clone()),
thumb_color: thumb,
track_color: track,
};
}
ComputedScrollbarStyle::default()
}
#[cfg(all(test, feature = "parser"))]
mod tests {
use super::*;
use crate::props::{basic::color::ColorU, layout::dimensions::LayoutWidth};
#[test]
fn test_parse_scrollbar_width() {
assert_eq!(
parse_layout_scrollbar_width("auto").unwrap(),
LayoutScrollbarWidth::Auto
);
assert_eq!(
parse_layout_scrollbar_width("thin").unwrap(),
LayoutScrollbarWidth::Thin
);
assert_eq!(
parse_layout_scrollbar_width("none").unwrap(),
LayoutScrollbarWidth::None
);
assert!(parse_layout_scrollbar_width("thick").is_err());
}
#[test]
fn test_parse_scrollbar_color() {
assert_eq!(
parse_style_scrollbar_color("auto").unwrap(),
StyleScrollbarColor::Auto
);
let custom = parse_style_scrollbar_color("red blue").unwrap();
assert_eq!(
custom,
StyleScrollbarColor::Custom(ScrollbarColorCustom {
thumb: ColorU::RED,
track: ColorU::BLUE
})
);
let custom_hex = parse_style_scrollbar_color("#ff0000 #0000ff").unwrap();
assert_eq!(
custom_hex,
StyleScrollbarColor::Custom(ScrollbarColorCustom {
thumb: ColorU::RED,
track: ColorU::BLUE
})
);
assert!(parse_style_scrollbar_color("red").is_err());
assert!(parse_style_scrollbar_color("red blue green").is_err());
}
fn get_webkit_style() -> ScrollbarStyle {
let mut info = ScrollbarInfo::default();
info.width = LayoutWidth::px(15.0);
info.thumb = StyleBackgroundContent::Color(ColorU::GREEN);
info.track = StyleBackgroundContent::Color(ColorU::new_rgb(100, 100, 100));
ScrollbarStyle {
horizontal: info.clone(),
vertical: info,
}
}
#[test]
fn test_resolve_standard_overrides_webkit() {
let width = LayoutScrollbarWidth::Thin;
let color = StyleScrollbarColor::Custom(ScrollbarColorCustom {
thumb: ColorU::RED,
track: ColorU::BLUE,
});
let webkit_style = get_webkit_style();
let resolved = resolve_scrollbar_style(Some(&width), Some(&color), Some(&webkit_style));
assert_eq!(resolved.width, Some(LayoutWidth::px(8.0)));
assert_eq!(resolved.thumb_color, Some(ColorU::RED));
assert_eq!(resolved.track_color, Some(ColorU::BLUE));
}
#[test]
fn test_resolve_standard_auto_falls_back_to_webkit() {
let width = LayoutScrollbarWidth::Auto;
let color = StyleScrollbarColor::Auto;
let webkit_style = get_webkit_style();
let resolved = resolve_scrollbar_style(Some(&width), Some(&color), Some(&webkit_style));
assert_eq!(resolved.width, Some(LayoutWidth::px(15.0)));
assert_eq!(resolved.thumb_color, Some(ColorU::GREEN));
assert_eq!(resolved.track_color, Some(ColorU::new_rgb(100, 100, 100)));
}
#[test]
fn test_resolve_no_styles_uses_default() {
let resolved = resolve_scrollbar_style(None, None, None);
assert_eq!(resolved, ComputedScrollbarStyle::default());
}
#[test]
fn test_resolve_scrollbar_width_none() {
let width = LayoutScrollbarWidth::None;
let webkit_style = get_webkit_style();
let resolved = resolve_scrollbar_style(Some(&width), None, Some(&webkit_style));
assert_eq!(resolved.width, None);
}
#[test]
fn test_resolve_webkit_display_none_equivalent() {
let mut webkit_style = get_webkit_style();
webkit_style.vertical.width = LayoutWidth::px(0.0);
let resolved = resolve_scrollbar_style(None, None, Some(&webkit_style));
assert_eq!(resolved.width, None);
}
#[test]
fn test_resolve_only_color_is_set() {
let color = StyleScrollbarColor::Custom(ScrollbarColorCustom {
thumb: ColorU::RED,
track: ColorU::BLUE,
});
let webkit_style = get_webkit_style();
let resolved = resolve_scrollbar_style(None, Some(&color), Some(&webkit_style));
assert_eq!(resolved.width, Some(LayoutWidth::px(15.0)));
assert_eq!(resolved.thumb_color, Some(ColorU::RED));
assert_eq!(resolved.track_color, Some(ColorU::BLUE));
}
#[test]
fn test_resolve_only_width_is_set() {
let width = LayoutScrollbarWidth::Thin;
let webkit_style = get_webkit_style();
let resolved = resolve_scrollbar_style(Some(&width), None, Some(&webkit_style));
assert_eq!(resolved.width, Some(LayoutWidth::px(8.0)));
assert_eq!(resolved.thumb_color, None);
assert_eq!(resolved.track_color, None);
}
}