From 00d296b230e41dbfaf33d1ba374beadf9d113948 Mon Sep 17 00:00:00 2001 From: pml68 Date: Tue, 8 Apr 2025 18:12:10 +0200 Subject: feat(material_theme): impl `scrollable::Catalog` --- material_theme/src/button.rs | 54 +++++++------------- material_theme/src/lib.rs | 1 + material_theme/src/scrollable.rs | 107 +++++++++++++++++++++++++++++++++++++++ material_theme/src/utils.rs | 19 ++++++- 4 files changed, 145 insertions(+), 36 deletions(-) create mode 100644 material_theme/src/scrollable.rs (limited to 'material_theme') diff --git a/material_theme/src/button.rs b/material_theme/src/button.rs index 051d6c9..21d77b7 100644 --- a/material_theme/src/button.rs +++ b/material_theme/src/button.rs @@ -1,9 +1,11 @@ -#![allow(dead_code)] use iced_widget::button::{Catalog, Status, Style, StyleFn}; -use iced_widget::core::{Background, Border, Color, Shadow, Vector}; +use iced_widget::core::{Background, Border, Color, border}; use crate::Theme; -use crate::utils::{elevation, mix}; +use crate::utils::{ + DISABLED_CONTAINER_OPACITY, DISABLED_TEXT_OPACITY, HOVERED_LAYER_OPACITY, + PRESSED_LAYER_OPACITY, elevation, mix, shadow_from_elevation, +}; impl Catalog for Theme { type Class<'a> = StyleFn<'a, Self>; @@ -23,27 +25,14 @@ fn button( tone_overlay: Color, disabled: Color, shadow_color: Color, - shadow_elevation: u8, + elevation_level: u8, status: Status, ) -> Style { - let border = Border { - radius: 400.into(), - ..Default::default() - }; - let active = Style { background: Some(Background::Color(background)), text_color: foreground, - border, - shadow: Shadow { - color: shadow_color, - offset: Vector { - x: 0.0, - y: elevation(shadow_elevation), - }, - blur_radius: elevation(shadow_elevation) - * (1.0 + 0.4_f32.powf(elevation(shadow_elevation))), - }, + border: border::rounded(400), + shadow: shadow_from_elevation(elevation(elevation_level), shadow_color), }; match status { @@ -52,7 +41,7 @@ fn button( background: Some(Background::Color(mix( background, tone_overlay, - 0.08, + HOVERED_LAYER_OPACITY, ))), ..active }, @@ -60,30 +49,25 @@ fn button( background: Some(Background::Color(mix( background, tone_overlay, - 0.1, + PRESSED_LAYER_OPACITY, ))), text_color: foreground, - border, - shadow: Shadow { - color: shadow_color, - offset: Vector { - x: 0.0, - y: elevation(shadow_elevation + 1), - }, - blur_radius: (elevation(shadow_elevation + 1)) - * (1.0 + 0.4_f32.powf(elevation(shadow_elevation + 1))), - }, + border: border::rounded(400), + shadow: shadow_from_elevation( + elevation(elevation_level + 1), + shadow_color, + ), }, Status::Disabled => Style { background: Some(Background::Color(Color { - a: 0.12, + a: DISABLED_CONTAINER_OPACITY, ..disabled })), text_color: Color { - a: 0.38, + a: DISABLED_TEXT_OPACITY, ..disabled }, - border, + border: border::rounded(400), ..Default::default() }, } @@ -163,7 +147,7 @@ pub fn outlined(theme: &Theme, status: Status) -> Style { }, Status::Disabled => Border { color: Color { - a: 0.12, + a: DISABLED_CONTAINER_OPACITY, ..disabled }, width: 1.0, diff --git a/material_theme/src/lib.rs b/material_theme/src/lib.rs index 87cf353..273ef9a 100644 --- a/material_theme/src/lib.rs +++ b/material_theme/src/lib.rs @@ -6,6 +6,7 @@ use serde::Deserialize; pub mod button; pub mod container; +pub mod scrollable; pub mod text; pub mod utils; diff --git a/material_theme/src/scrollable.rs b/material_theme/src/scrollable.rs new file mode 100644 index 0000000..c2dde67 --- /dev/null +++ b/material_theme/src/scrollable.rs @@ -0,0 +1,107 @@ +use iced_widget::core::{Border, border}; +use iced_widget::scrollable::{ + Catalog, Rail, Scroller, Status, Style, StyleFn, +}; + +use super::Theme; +use super::container::surface_container; +use super::utils::mix; +use crate::utils::{HOVERED_LAYER_OPACITY, PRESSED_LAYER_OPACITY}; + +impl Catalog for Theme { + type Class<'a> = StyleFn<'a, Self>; + + fn default<'a>() -> Self::Class<'a> { + Box::new(default) + } + + fn style(&self, class: &Self::Class<'_>, status: Status) -> Style { + class(self, status) + } +} + +pub fn default(theme: &Theme, status: Status) -> Style { + let colors = theme.colorscheme.surface; + + let rail = Rail { + background: None, + scroller: Scroller { + color: colors.on_surface, + border: border::rounded(400), + }, + border: Border::default(), + }; + + let style = Style { + container: surface_container(theme), + vertical_rail: rail, + horizontal_rail: rail, + gap: None, + }; + + match status { + Status::Active { .. } => style, + Status::Hovered { + is_horizontal_scrollbar_hovered, + is_vertical_scrollbar_hovered, + .. + } => { + let hovered_rail = Rail { + scroller: Scroller { + color: mix( + colors.on_surface, + colors.color, + HOVERED_LAYER_OPACITY, + ), + border: border::rounded(400), + }, + ..rail + }; + + Style { + horizontal_rail: if is_horizontal_scrollbar_hovered { + hovered_rail + } else { + rail + }, + vertical_rail: if is_vertical_scrollbar_hovered { + hovered_rail + } else { + rail + }, + ..style + } + } + Status::Dragged { + is_horizontal_scrollbar_dragged, + is_vertical_scrollbar_dragged, + .. + } => { + let dragged_rail = Rail { + scroller: Scroller { + color: mix( + colors.on_surface, + colors.color, + PRESSED_LAYER_OPACITY, + ), + border: border::rounded(400), + }, + ..rail + }; + + Style { + horizontal_rail: if is_horizontal_scrollbar_dragged { + dragged_rail + } else { + rail + }, + vertical_rail: if is_vertical_scrollbar_dragged { + dragged_rail + } else { + rail + }, + ..style + } + } + } +} diff --git a/material_theme/src/utils.rs b/material_theme/src/utils.rs index c9eb78e..7efec9b 100644 --- a/material_theme/src/utils.rs +++ b/material_theme/src/utils.rs @@ -1,4 +1,10 @@ -use iced_widget::core::Color; +use iced_widget::core::{Color, Shadow, Vector}; + +pub const HOVERED_LAYER_OPACITY: f32 = 0.08; +pub const PRESSED_LAYER_OPACITY: f32 = 0.1; + +pub const DISABLED_TEXT_OPACITY: f32 = 0.38; +pub const DISABLED_CONTAINER_OPACITY: f32 = 0.12; pub fn elevation(elevation_level: u8) -> f32 { (match elevation_level { @@ -11,6 +17,17 @@ pub fn elevation(elevation_level: u8) -> f32 { } as f32) } +pub fn shadow_from_elevation(elevation: f32, color: Color) -> Shadow { + Shadow { + color, + offset: Vector { + x: 0.0, + y: elevation, + }, + blur_radius: (elevation) * (1.0 + 0.4_f32.powf(elevation)), + } +} + pub fn mix(color1: Color, color2: Color, p2: f32) -> Color { if p2 <= 0.0 { return color1; -- cgit v1.2.3