feat(progress_bar): custom style
This commit is contained in:
parent
d9431dc367
commit
d921602cde
3 changed files with 128 additions and 78 deletions
|
|
@ -1,6 +1,6 @@
|
|||
//! Show a circular progress indicator.
|
||||
use super::animation::{Animation, Progress};
|
||||
use super::style::StyleSheet;
|
||||
use super::style::{self, Catalog};
|
||||
use iced::advanced::widget::tree::{self, Tree};
|
||||
use iced::advanced::{self, Clipboard, Layout, Shell, Widget, layout, renderer};
|
||||
use iced::widget::canvas;
|
||||
|
|
@ -15,11 +15,11 @@ const MAX_WRAP: f32 = 1.0 - MIN_GAP_ANGLE.0 / (2.0 * PI);
|
|||
#[must_use]
|
||||
pub struct Circular<Theme>
|
||||
where
|
||||
Theme: StyleSheet,
|
||||
Theme: Catalog,
|
||||
{
|
||||
size: f32,
|
||||
bar_height: Option<f32>,
|
||||
style: Theme::Style,
|
||||
class: Theme::Class,
|
||||
cycle_duration: Duration,
|
||||
period: Duration,
|
||||
progress: Option<f32>,
|
||||
|
|
@ -27,14 +27,14 @@ where
|
|||
|
||||
impl<Theme> Circular<Theme>
|
||||
where
|
||||
Theme: StyleSheet,
|
||||
Theme: Catalog<Class = style::Class>,
|
||||
{
|
||||
/// Creates a new [`Circular`] with the given content.
|
||||
pub fn new() -> Self {
|
||||
Circular {
|
||||
size: 48.0,
|
||||
bar_height: None,
|
||||
style: Theme::Style::default(),
|
||||
class: Theme::Class::default(),
|
||||
cycle_duration: Duration::from_millis(1500),
|
||||
period: Duration::from_secs(2),
|
||||
progress: None,
|
||||
|
|
@ -54,9 +54,9 @@ where
|
|||
self
|
||||
}
|
||||
|
||||
/// Sets the style variant of this [`Circular`].
|
||||
pub fn style(mut self, style: Theme::Style) -> Self {
|
||||
self.style = style;
|
||||
/// Sets the style class of this [`Circular`].
|
||||
pub fn class(mut self, class: Theme::Class) -> Self {
|
||||
self.class = class;
|
||||
self
|
||||
}
|
||||
|
||||
|
|
@ -78,11 +78,29 @@ where
|
|||
self.progress = Some(progress.clamp(0.0, 1.0));
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the track color of this [`Circular`].
|
||||
pub fn track_color(mut self, color: impl Into<iced::Color>) -> Self {
|
||||
self.class = self.class.track_color(color);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the bar color of this [`Circular`].
|
||||
pub fn bar_color(mut self, color: impl Into<iced::Color>) -> Self {
|
||||
self.class = self.class.bar_color(color);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the border color of this [`Circular`].
|
||||
pub fn border_color(mut self, color: impl Into<iced::Color>) -> Self {
|
||||
self.class = self.class.border_color(color);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<Theme> Default for Circular<Theme>
|
||||
where
|
||||
Theme: StyleSheet,
|
||||
Theme: Catalog<Class = style::Class>,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
|
|
@ -99,7 +117,7 @@ struct State {
|
|||
impl<Message, Theme> Widget<Message, Theme, Renderer> for Circular<Theme>
|
||||
where
|
||||
Message: Clone,
|
||||
Theme: StyleSheet,
|
||||
Theme: Catalog,
|
||||
{
|
||||
fn tag(&self) -> tree::Tag {
|
||||
tree::Tag::of::<State>()
|
||||
|
|
@ -170,7 +188,7 @@ where
|
|||
|
||||
let state = tree.state.downcast_ref::<State>();
|
||||
let bounds = layout.bounds();
|
||||
let custom_style = Theme::appearance(theme, &self.style, self.progress.is_some(), true);
|
||||
let custom_style = theme.style(&self.class, self.progress.is_some(), true);
|
||||
|
||||
let geometry = state.cache.draw(renderer, bounds.size(), |frame| {
|
||||
let bar_height = self.bar_height.unwrap_or((frame.width() / 12.0).max(2.0));
|
||||
|
|
@ -240,7 +258,7 @@ where
|
|||
impl<'a, Message, Theme> From<Circular<Theme>> for Element<'a, Message, Theme, Renderer>
|
||||
where
|
||||
Message: Clone + 'a,
|
||||
Theme: StyleSheet + 'a,
|
||||
Theme: Catalog + 'a,
|
||||
{
|
||||
fn from(circular: Circular<Theme>) -> Self {
|
||||
Self::new(circular)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
//! Show a linear progress indicator.
|
||||
use super::animation::{Animation, Progress};
|
||||
use super::style::StyleSheet;
|
||||
use super::style::{self, Catalog};
|
||||
use iced::advanced::widget::tree::{self, Tree};
|
||||
use iced::advanced::{self, Clipboard, Layout, Shell, Widget, layout, renderer};
|
||||
use iced::{Border, Color, Element, Event, Length, Pixels, Rectangle, Size, mouse, window};
|
||||
|
|
@ -13,11 +13,11 @@ const WRAP_LENGTH: f32 = 0.618; // avoids animation repetition
|
|||
#[must_use]
|
||||
pub struct Linear<Theme>
|
||||
where
|
||||
Theme: StyleSheet,
|
||||
Theme: Catalog,
|
||||
{
|
||||
width: Length,
|
||||
girth: Length,
|
||||
style: Theme::Style,
|
||||
class: Theme::Class,
|
||||
cycle_duration: Duration,
|
||||
period: Duration,
|
||||
progress: Option<f32>,
|
||||
|
|
@ -27,14 +27,14 @@ where
|
|||
|
||||
impl<Theme> Linear<Theme>
|
||||
where
|
||||
Theme: StyleSheet,
|
||||
Theme: Catalog<Class = style::Class>,
|
||||
{
|
||||
/// Creates a new [`Linear`] with the given content.
|
||||
pub fn new() -> Self {
|
||||
Linear {
|
||||
width: Length::Fixed(100.0),
|
||||
girth: Length::Fixed(4.0),
|
||||
style: Theme::Style::default(),
|
||||
class: Theme::Class::default(),
|
||||
cycle_duration: Duration::from_millis(1500),
|
||||
period: Duration::from_secs(2),
|
||||
progress: None,
|
||||
|
|
@ -55,9 +55,9 @@ where
|
|||
self
|
||||
}
|
||||
|
||||
/// Sets the style variant of this [`Linear`].
|
||||
pub fn style(mut self, style: impl Into<Theme::Style>) -> Self {
|
||||
self.style = style.into();
|
||||
/// Sets the style class of this [`Linear`].
|
||||
pub fn class(mut self, class: Theme::Class) -> Self {
|
||||
self.class = class;
|
||||
self
|
||||
}
|
||||
|
||||
|
|
@ -99,11 +99,35 @@ where
|
|||
self.segment_spacing = spacing.into().0.max(1.0);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the track color of this [`Linear`].
|
||||
pub fn track_color(mut self, color: impl Into<iced::Color>) -> Self {
|
||||
self.class = self.class.track_color(color);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the bar color of this [`Linear`].
|
||||
pub fn bar_color(mut self, color: impl Into<iced::Color>) -> Self {
|
||||
self.class = self.class.bar_color(color);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the border color of this [`Linear`].
|
||||
pub fn border_color(mut self, color: impl Into<iced::Color>) -> Self {
|
||||
self.class = self.class.border_color(color);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the border radius of this [`Linear`].
|
||||
pub fn border_radius(mut self, radius: f32) -> Self {
|
||||
self.class = self.class.border_radius(radius);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<Theme> Default for Linear<Theme>
|
||||
where
|
||||
Theme: StyleSheet,
|
||||
Theme: Catalog<Class = style::Class>,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
|
|
@ -119,7 +143,7 @@ struct State {
|
|||
impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer> for Linear<Theme>
|
||||
where
|
||||
Message: Clone,
|
||||
Theme: StyleSheet,
|
||||
Theme: Catalog,
|
||||
Renderer: advanced::Renderer,
|
||||
{
|
||||
fn tag(&self) -> tree::Tag {
|
||||
|
|
@ -186,7 +210,7 @@ where
|
|||
_viewport: &Rectangle,
|
||||
) {
|
||||
let bounds = layout.bounds();
|
||||
let custom_style = theme.appearance(&self.style, self.progress.is_some(), false);
|
||||
let custom_style = theme.style(&self.class, self.progress.is_some(), false);
|
||||
let state = tree.state.downcast_ref::<State>();
|
||||
|
||||
let border_width = if custom_style.border_color.is_some() {
|
||||
|
|
@ -307,7 +331,7 @@ where
|
|||
impl<'a, Message, Theme, Renderer> From<Linear<Theme>> for Element<'a, Message, Theme, Renderer>
|
||||
where
|
||||
Message: Clone + 'a,
|
||||
Theme: StyleSheet + 'a,
|
||||
Theme: Catalog + 'a,
|
||||
Renderer: iced::advanced::Renderer + 'a,
|
||||
{
|
||||
fn from(linear: Linear<Theme>) -> Self {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
use iced::Color;
|
||||
use palette::WithAlpha;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Appearance {
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Style {
|
||||
/// The track [`Color`] of the progress indicator.
|
||||
pub track_color: Color,
|
||||
/// The bar [`Color`] of the progress indicator.
|
||||
|
|
@ -12,94 +13,101 @@ pub struct Appearance {
|
|||
pub border_radius: f32,
|
||||
}
|
||||
|
||||
impl std::default::Default for Appearance {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
track_color: Color::TRANSPARENT,
|
||||
bar_color: Color::BLACK,
|
||||
border_color: None,
|
||||
border_radius: 0.0,
|
||||
/// [`Style`] field overrides
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct Class {
|
||||
pub track_color: Option<Color>,
|
||||
pub bar_color: Option<Color>,
|
||||
pub border_color: Option<Color>,
|
||||
pub border_radius: Option<f32>,
|
||||
}
|
||||
|
||||
impl Class {
|
||||
pub fn track_color(mut self, color: impl Into<Color>) -> Self {
|
||||
self.track_color = Some(color.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn bar_color(mut self, color: impl Into<Color>) -> Self {
|
||||
self.bar_color = Some(color.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn border_color(mut self, color: impl Into<Color>) -> Self {
|
||||
self.border_color = Some(color.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn border_radius(mut self, radius: f32) -> Self {
|
||||
self.border_radius = Some(radius);
|
||||
self
|
||||
}
|
||||
|
||||
fn resolve(&self, base: Style) -> Style {
|
||||
Style {
|
||||
track_color: self.track_color.unwrap_or(base.track_color),
|
||||
bar_color: self.bar_color.unwrap_or(base.bar_color),
|
||||
border_color: self.border_color.or(base.border_color),
|
||||
border_radius: self.border_radius.unwrap_or(base.border_radius),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A set of rules that dictate the style of an indicator.
|
||||
pub trait StyleSheet {
|
||||
/// The supported style of the [`StyleSheet`].
|
||||
type Style: Default;
|
||||
pub trait Catalog: Sized {
|
||||
/// The supported class of the [`Catalog`].
|
||||
type Class: Default;
|
||||
|
||||
/// Produces the active [`Appearance`] of a indicator.
|
||||
fn appearance(
|
||||
&self,
|
||||
style: &Self::Style,
|
||||
is_determinate: bool,
|
||||
is_circular: bool,
|
||||
) -> Appearance;
|
||||
/// Produces the active [`Style`] of an indicator.
|
||||
fn style(&self, class: &Self::Class, is_determinate: bool, is_circular: bool) -> Style;
|
||||
}
|
||||
|
||||
impl StyleSheet for iced::Theme {
|
||||
type Style = ();
|
||||
impl Catalog for iced::Theme {
|
||||
type Class = Class;
|
||||
|
||||
fn appearance(
|
||||
&self,
|
||||
_style: &Self::Style,
|
||||
_is_determinate: bool,
|
||||
_is_circular: bool,
|
||||
) -> Appearance {
|
||||
fn style(&self, class: &Self::Class, _is_determinate: bool, _is_circular: bool) -> Style {
|
||||
let palette = self.extended_palette();
|
||||
|
||||
Appearance {
|
||||
class.resolve(Style {
|
||||
track_color: palette.background.weak.color,
|
||||
bar_color: palette.primary.base.color,
|
||||
border_color: None,
|
||||
border_radius: 0.0,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl StyleSheet for crate::Theme {
|
||||
type Style = ();
|
||||
impl Catalog for crate::Theme {
|
||||
type Class = Class;
|
||||
|
||||
fn appearance(
|
||||
&self,
|
||||
_style: &Self::Style,
|
||||
is_determinate: bool,
|
||||
is_circular: bool,
|
||||
) -> Appearance {
|
||||
let cur = self.current_container();
|
||||
let mut cur_divider = cur.divider;
|
||||
cur_divider.alpha = 0.5;
|
||||
fn style(&self, class: &Self::Class, is_determinate: bool, is_circular: bool) -> Style {
|
||||
let theme = self.cosmic();
|
||||
|
||||
let (mut track_color, bar_color) = if theme.is_dark && theme.is_high_contrast {
|
||||
(
|
||||
let (mut track_color, bar_color) = match (theme.is_dark, theme.is_high_contrast) {
|
||||
(true, true) => (
|
||||
theme.palette.neutral_6.into(),
|
||||
theme.accent_text_color().into(),
|
||||
)
|
||||
} else if theme.is_dark {
|
||||
(theme.palette.neutral_5.into(), theme.accent_color().into())
|
||||
} else if theme.is_high_contrast {
|
||||
(
|
||||
),
|
||||
(true, false) => (theme.palette.neutral_5.into(), theme.accent_color().into()),
|
||||
(false, true) => (
|
||||
theme.palette.neutral_4.into(),
|
||||
theme.accent_text_color().into(),
|
||||
)
|
||||
} else {
|
||||
(theme.palette.neutral_3.into(), theme.accent_color().into())
|
||||
),
|
||||
(false, false) => (theme.palette.neutral_3.into(), theme.accent_color().into()),
|
||||
};
|
||||
|
||||
if !is_determinate && is_circular {
|
||||
track_color = Color::TRANSPARENT;
|
||||
}
|
||||
|
||||
Appearance {
|
||||
class.resolve(Style {
|
||||
track_color,
|
||||
bar_color,
|
||||
border_color: if is_determinate && theme.is_high_contrast {
|
||||
Some(cur_divider.into())
|
||||
Some(self.current_container().divider.with_alpha(0.5).into())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
border_radius: theme.corner_radii.radius_xl[0],
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue