Use Catalog approach for all widgets

This commit is contained in:
Héctor Ramón Jiménez 2024-03-24 05:03:09 +01:00
parent e657dc2ecd
commit f0ae9a0c38
No known key found for this signature in database
GPG key ID: 7CC46565708259A7
30 changed files with 1134 additions and 919 deletions

View file

@ -1,4 +1,5 @@
//! Display a horizontal or vertical rule for dividing content.
use crate::core;
use crate::core::border::{self, Border};
use crate::core::layout;
use crate::core::mouse;
@ -10,43 +11,55 @@ use crate::core::{
/// Display a horizontal or vertical rule for dividing content.
#[allow(missing_debug_implementations)]
pub struct Rule<'a, Theme = crate::Theme> {
pub struct Rule<'a, Theme = crate::Theme>
where
Theme: Catalog,
{
width: Length,
height: Length,
is_horizontal: bool,
style: Style<'a, Theme>,
class: Theme::Class<'a>,
}
impl<'a, Theme> Rule<'a, Theme> {
impl<'a, Theme> Rule<'a, Theme>
where
Theme: Catalog,
{
/// Creates a horizontal [`Rule`] with the given height.
pub fn horizontal(height: impl Into<Pixels>) -> Self
where
Theme: DefaultStyle + 'a,
{
pub fn horizontal(height: impl Into<Pixels>) -> Self {
Rule {
width: Length::Fill,
height: Length::Fixed(height.into().0),
is_horizontal: true,
style: Box::new(Theme::default_style),
class: Theme::default(),
}
}
/// Creates a vertical [`Rule`] with the given width.
pub fn vertical(width: impl Into<Pixels>) -> Self
where
Theme: DefaultStyle + 'a,
{
pub fn vertical(width: impl Into<Pixels>) -> Self {
Rule {
width: Length::Fixed(width.into().0),
height: Length::Fill,
is_horizontal: false,
style: Box::new(Theme::default_style),
class: Theme::default(),
}
}
/// Sets the style of the [`Rule`].
pub fn style(mut self, style: impl Fn(&Theme) -> Appearance + 'a) -> Self {
self.style = Box::new(style);
#[must_use]
pub fn style(mut self, style: impl Fn(&Theme) -> Style + 'a) -> Self
where
Theme::Class<'a>: From<StyleFn<'a, Theme>>,
{
self.class = (Box::new(style) as StyleFn<'a, Theme>).into();
self
}
/// Sets the style class of the [`Rule`].
#[cfg(feature = "advanced")]
#[must_use]
pub fn class(mut self, class: impl Into<Theme::Class<'a>>) -> Self {
self.class = class.into();
self
}
}
@ -54,7 +67,8 @@ impl<'a, Theme> Rule<'a, Theme> {
impl<'a, Message, Theme, Renderer> Widget<Message, Theme, Renderer>
for Rule<'a, Theme>
where
Renderer: crate::core::Renderer,
Renderer: core::Renderer,
Theme: Catalog,
{
fn size(&self) -> Size<Length> {
Size {
@ -83,35 +97,34 @@ where
_viewport: &Rectangle,
) {
let bounds = layout.bounds();
let appearance = (self.style)(theme);
let style = theme.style(&self.class);
let bounds = if self.is_horizontal {
let line_y = (bounds.y + (bounds.height / 2.0)
- (appearance.width as f32 / 2.0))
- (style.width as f32 / 2.0))
.round();
let (offset, line_width) = appearance.fill_mode.fill(bounds.width);
let (offset, line_width) = style.fill_mode.fill(bounds.width);
let line_x = bounds.x + offset;
Rectangle {
x: line_x,
y: line_y,
width: line_width,
height: appearance.width as f32,
height: style.width as f32,
}
} else {
let line_x = (bounds.x + (bounds.width / 2.0)
- (appearance.width as f32 / 2.0))
- (style.width as f32 / 2.0))
.round();
let (offset, line_height) =
appearance.fill_mode.fill(bounds.height);
let (offset, line_height) = style.fill_mode.fill(bounds.height);
let line_y = bounds.y + offset;
Rectangle {
x: line_x,
y: line_y,
width: appearance.width as f32,
width: style.width as f32,
height: line_height,
}
};
@ -119,10 +132,10 @@ where
renderer.fill_quad(
renderer::Quad {
bounds,
border: Border::rounded(appearance.radius),
border: Border::rounded(style.radius),
..renderer::Quad::default()
},
appearance.color,
style.color,
);
}
}
@ -131,8 +144,8 @@ impl<'a, Message, Theme, Renderer> From<Rule<'a, Theme>>
for Element<'a, Message, Theme, Renderer>
where
Message: 'a,
Theme: 'a,
Renderer: 'a + crate::core::Renderer,
Theme: 'a + Catalog,
Renderer: 'a + core::Renderer,
{
fn from(rule: Rule<'a, Theme>) -> Element<'a, Message, Theme, Renderer> {
Element::new(rule)
@ -141,7 +154,7 @@ where
/// The appearance of a rule.
#[derive(Debug, Clone, Copy)]
pub struct Appearance {
pub struct Style {
/// The color of the rule.
pub color: Color,
/// The width (thickness) of the rule line.
@ -216,32 +229,40 @@ impl FillMode {
}
}
/// The style of a [`Rule`].
pub type Style<'a, Theme> = Box<dyn Fn(&Theme) -> Appearance + 'a>;
/// The theme catalog of a [`Rule`].
pub trait Catalog: Sized {
/// The item class of the [`Catalog`].
type Class<'a>;
/// The default style of a [`Rule`].
pub trait DefaultStyle {
/// Returns the default style of a [`Rule`].
fn default_style(&self) -> Appearance;
/// The default class produced by the [`Catalog`].
fn default<'a>() -> Self::Class<'a>;
/// The [`Style`] of a class with the given status.
fn style(&self, class: &Self::Class<'_>) -> Style;
}
impl DefaultStyle for Theme {
fn default_style(&self) -> Appearance {
default(self)
/// A styling function for a [`Rule`].
///
/// This is just a boxed closure: `Fn(&Theme, Status) -> Style`.
pub type StyleFn<'a, Theme> = Box<dyn Fn(&Theme) -> Style + 'a>;
impl Catalog for Theme {
type Class<'a> = StyleFn<'a, Self>;
fn default<'a>() -> Self::Class<'a> {
Box::new(default)
}
}
impl DefaultStyle for Appearance {
fn default_style(&self) -> Appearance {
*self
fn style(&self, class: &Self::Class<'_>) -> Style {
class(self)
}
}
/// The default styling of a [`Rule`].
pub fn default(theme: &Theme) -> Appearance {
pub fn default(theme: &Theme) -> Style {
let palette = theme.extended_palette();
Appearance {
Style {
color: palette.background.strong.color,
width: 1,
radius: 0.0.into(),