diff --git a/examples/widget-gallery/src/main.rs b/examples/widget-gallery/src/main.rs index c4c46f5..642404a 100644 --- a/examples/widget-gallery/src/main.rs +++ b/examples/widget-gallery/src/main.rs @@ -25,6 +25,7 @@ use oxytail_base::{ widgets::{button::button, text_divider::text_divider, text_header::text_header}, }; use oxytail_theme_dark::Theme; +use progresses::progresses_variants; use radio_buttons::{ disabled_labeled_radio_variants, labeled_radio_variants, radio_sizes, radio_variants, }; @@ -36,6 +37,7 @@ mod btn; mod checkboxes; pub mod headers; mod inputs; +pub mod progresses; mod radio_buttons; mod toggles; pub mod tooltips; @@ -46,7 +48,8 @@ fn padded_container_box(child: impl View + 'static) -> ContainerBox { fn app_view() -> impl View { let tabs: im::Vector<&str> = vec![ - "Intro", "Header", "Button", "Badge", "Radio", "Checkbox", "Input", "Tooltip", "Toggle", + "Intro", "Header", "Button", "Badge", "Progress", "Radio", "Checkbox", "Input", "Tooltip", + "Toggle", ] .into_iter() .collect(); @@ -171,6 +174,7 @@ fn app_view() -> impl View { text_divider(),badges_variants(), text_header("Sizes", None), text_divider(), badges_sizes(), text_header("Outlines", None), text_divider(), badges_outlines()))), + "Progress" => padded_container_box(progresses_variants()), "Radio" => padded_container_box(v_stack((radio_variants(),radio_sizes(),labeled_radio_variants(),disabled_labeled_radio_variants()))), "Checkbox" => padded_container_box(v_stack(( checkboxes_variants(), diff --git a/examples/widget-gallery/src/progresses.rs b/examples/widget-gallery/src/progresses.rs new file mode 100644 index 0000000..86a85a8 --- /dev/null +++ b/examples/widget-gallery/src/progresses.rs @@ -0,0 +1,34 @@ +use floem::{view::View, views::v_stack}; +use oxytail_base::widgets::{ + common_props::OxyVariant, + progress::{progress, ProgressProps}, +}; + +pub fn progresses_variants() -> impl View { + v_stack(( + progress( + 20, + 80, + Some(ProgressProps { + variant: OxyVariant::Primary, + ..Default::default() + }), + ), + progress( + 40, + 80, + Some(ProgressProps { + variant: OxyVariant::Primary, + ..Default::default() + }), + ), + progress( + 60, + 80, + Some(ProgressProps { + variant: OxyVariant::Primary, + ..Default::default() + }), + ), + )) +} diff --git a/oxytail-base/src/themes.rs b/oxytail-base/src/themes.rs index d651842..52b4962 100644 --- a/oxytail-base/src/themes.rs +++ b/oxytail-base/src/themes.rs @@ -2,8 +2,8 @@ use floem::{peniko::Color, style::Style}; use crate::widgets::{ badge::BadgeProps, button::ButtonProps, checkbox::CheckboxProps, common_props::OxyVariant, - radio_button::RadioProps, text_header::HeaderProps, text_input::InputProps, - toggle::ToggleProps, tooltip::TooltipProps, + progress::ProgressProps, radio_button::RadioProps, text_header::HeaderProps, + text_input::InputProps, toggle::ToggleProps, tooltip::TooltipProps, }; // TODO: LOAD TTF FROM FILE SO THAT ITS CONSISTENT. @@ -59,7 +59,9 @@ pub trait ThemeStyling { /// Defines how a `toggle` should look like. fn get_toggle_style(&self, toggle_props: ToggleProps) -> Box Style + '_>; /// Defines how a `radio_button` should look like. - /// Returns a tuple, where first argument styles the "dot" of the active radio and the second one is the "outer circle", containing the default state of the radio. + /// Returns a tuple, where + /// First element styles the "dot" of the active radio + /// Second element is the "outer circle", containing the default state of the radio. fn get_radio_style( &self, radio_props: RadioProps, @@ -79,4 +81,17 @@ pub trait ThemeStyling { /// Defines how a `badge` should look like. fn get_badge_style(&self, badge_props: BadgeProps) -> Box Style + '_>; + + /// Defines how a `progress` should look like. + /// First argument: inner progress + /// Second argument: outline + /// Third argument: the "ball at the end of the first arg" + fn get_progress_style( + &self, + progress_props: ProgressProps, + ) -> ( + Box Style + '_>, + Box Style + '_>, + Box Style + '_>, + ); } diff --git a/oxytail-base/src/widgets/mod.rs b/oxytail-base/src/widgets/mod.rs index c47d1d8..1caacfc 100644 --- a/oxytail-base/src/widgets/mod.rs +++ b/oxytail-base/src/widgets/mod.rs @@ -8,3 +8,4 @@ pub mod text_header; pub mod text_divider; pub mod tooltip; pub mod badge; +pub mod progress; diff --git a/oxytail-base/src/widgets/progress.rs b/oxytail-base/src/widgets/progress.rs new file mode 100644 index 0000000..e9b371a --- /dev/null +++ b/oxytail-base/src/widgets/progress.rs @@ -0,0 +1,48 @@ +use std::fmt::Display; + +use floem::{ + style::FlexDirection, + unit::{PxPct, PxPctAuto}, + view::View, + views::{container, empty, h_stack, Decorators}, +}; + +use crate::get_current_theme; + +use super::common_props::{OxySize, OxyVariant}; + +#[derive(Default, Clone, Copy)] +pub struct ProgressProps { + pub variant: OxyVariant, + pub size: OxySize, +} + +/// The progress element. You unfortunately, you need to define the width yourself for this one to work. +/// `value` should be a pixel/percentage value. +/// `max_value` should be the max_value. +/// value should be less or equal to max_value. +pub fn progress( + value: impl Into + Copy + 'static, + max_value: impl Into + Copy + 'static, + props: Option, +) -> impl View { + let props = props.unwrap_or(ProgressProps::default()); + + let theme = get_current_theme(); + let (inner_enhancer, outer_enhancer, ball_enhancer) = theme.get_progress_style(props); + + let ball_widget = empty(); + let ball_widget = ball_widget.style(move |s| ball_enhancer(s)); + let inner_widget = container(ball_widget).style(move |s| { + s.width(value) + .max_width(value) + .flex() + .flex_direction(FlexDirection::RowReverse) + }); + let inner_widget = inner_widget.style(move |s| inner_enhancer(s)); + let outline_widget = container(inner_widget).style(move |s| s.width(max_value)); + + let styled_progress = outline_widget.style(move |s| outer_enhancer(s)); + + styled_progress +} diff --git a/oxytail-theme-dark/src/lib.rs b/oxytail-theme-dark/src/lib.rs index 4a5e22c..4e31eda 100644 --- a/oxytail-theme-dark/src/lib.rs +++ b/oxytail-theme-dark/src/lib.rs @@ -3,8 +3,8 @@ use oxytail_base::{ themes::{DefaultThemeProps, ThemeStyling}, widgets::{ badge::BadgeProps, button::ButtonProps, checkbox::CheckboxProps, common_props::OxyVariant, - radio_button::RadioProps, text_header::HeaderProps, text_input::InputProps, - toggle::ToggleProps, tooltip::TooltipProps, + progress::ProgressProps, radio_button::RadioProps, text_header::HeaderProps, + text_input::InputProps, toggle::ToggleProps, tooltip::TooltipProps, }, }; use oxytail_theme_defaults::DEFAULT_ACCENT; @@ -122,8 +122,21 @@ impl ThemeStyling for Theme { ) } - /// Defines how a `badge` should look like. fn get_badge_style(&self, badge_props: BadgeProps) -> Box Style + '_> { oxytail_theme_defaults::ThemeDefault::get_badge_style(badge_props, self.theme_defaults()) } + + fn get_progress_style( + &self, + progress_props: ProgressProps, + ) -> ( + Box Style + '_>, + Box Style + '_>, + Box Style + '_>, + ) { + oxytail_theme_defaults::ThemeDefault::get_progress_style( + progress_props, + self.theme_defaults(), + ) + } } diff --git a/oxytail-theme-defaults/src/lib.rs b/oxytail-theme-defaults/src/lib.rs index 04b7d49..fad64dd 100644 --- a/oxytail-theme-defaults/src/lib.rs +++ b/oxytail-theme-defaults/src/lib.rs @@ -11,6 +11,7 @@ use oxytail_base::{ button::ButtonProps, checkbox::CheckboxProps, common_props::{OxySize, OxyVariant}, + progress::ProgressProps, radio_button::RadioProps, text_header::HeaderProps, text_input::InputProps, @@ -427,4 +428,78 @@ impl ThemeDefault { Box::new(styles_creator) } + + /// Defines how a `progress` should look like. Returns a tuple + /// First argument: inner progress + /// Second argument: outline + /// Third argument: the "ball at the end of the first arg" + pub fn get_progress_style( + progress_props: ProgressProps, + theme_defaults: DefaultThemeProps, + ) -> ( + Box Style>, + Box Style>, + Box Style>, + ) { + let size = 26.0; + let curr_variant_color = (theme_defaults.get_variant_colors)(progress_props.variant); + let outer_styles_creator = move |s: Style| { + let base_style = s + .border(0.5) + .border_radius(16) + .border_color(Color::WHITE) + .background(Color::TRANSPARENT); + let variant_enhancer = |s: Style| match progress_props.variant { + OxyVariant::Default => { + s.border_color((theme_defaults.get_variant_colors)(OxyVariant::Neutral)) + } + OxyVariant::Neutral => s.border_color(curr_variant_color), + _ => s.border_color(curr_variant_color), + }; + + let enhanced_style = variant_enhancer(base_style); + + enhanced_style + }; + let inner_styles_creator = move |s: Style| { + let base_style = s.background(Color::WHITE).border_radius(16).min_width(size); + let variant_enhancer = |s: Style| match progress_props.variant { + OxyVariant::Default => { + s.background((theme_defaults.get_variant_colors)(OxyVariant::Neutral)) + } + OxyVariant::Neutral => s.background(curr_variant_color), + _ => s.background(curr_variant_color), + }; + + let enhanced_style = variant_enhancer(base_style); + + enhanced_style + }; + let ball_styles_creator = move |s: Style| { + let base_style = s + .min_width(size) + .min_height(size) + .border(3) + .border_radius(Pct(50.)) + .background(Color::rgb8(29, 35, 42)); + + let variant_enhancer = |s: Style| match progress_props.variant { + OxyVariant::Default => { + s.border_color((theme_defaults.get_variant_colors)(OxyVariant::Neutral)) + } + OxyVariant::Neutral => s.border_color(curr_variant_color), + _ => s.border_color(curr_variant_color), + }; + + let enhanced_style = variant_enhancer(base_style); + + enhanced_style + }; + + ( + Box::new(inner_styles_creator), + Box::new(outer_styles_creator), + Box::new(ball_styles_creator), + ) + } }