Use Style struct pattern instead of trait for all widgets

This commit is contained in:
Héctor Ramón Jiménez 2024-03-06 20:30:58 +01:00
parent 8a63774b24
commit 34e7c6593a
No known key found for this signature in database
GPG key ID: 7CC46565708259A7
25 changed files with 466 additions and 282 deletions

View file

@ -17,7 +17,6 @@ pub use text::{LineHeight, Shaping};
#[allow(missing_debug_implementations)] #[allow(missing_debug_implementations)]
pub struct Text<'a, Theme, Renderer> pub struct Text<'a, Theme, Renderer>
where where
Theme: StyleSheet,
Renderer: text::Renderer, Renderer: text::Renderer,
{ {
content: Cow<'a, str>, content: Cow<'a, str>,
@ -34,7 +33,6 @@ where
impl<'a, Theme, Renderer> Text<'a, Theme, Renderer> impl<'a, Theme, Renderer> Text<'a, Theme, Renderer>
where where
Theme: StyleSheet,
Renderer: text::Renderer, Renderer: text::Renderer,
{ {
/// Create a new fragment of [`Text`] with the given contents. /// Create a new fragment of [`Text`] with the given contents.
@ -49,7 +47,7 @@ where
horizontal_alignment: alignment::Horizontal::Left, horizontal_alignment: alignment::Horizontal::Left,
vertical_alignment: alignment::Vertical::Top, vertical_alignment: alignment::Vertical::Top,
shaping: Shaping::Basic, shaping: Shaping::Basic,
style: Style::Themed(Theme::default()), style: Style::default(),
} }
} }
@ -135,7 +133,6 @@ pub struct State<P: Paragraph>(P);
impl<'a, Message, Theme, Renderer> Widget<Message, Theme, Renderer> impl<'a, Message, Theme, Renderer> Widget<Message, Theme, Renderer>
for Text<'a, Theme, Renderer> for Text<'a, Theme, Renderer>
where where
Theme: StyleSheet,
Renderer: text::Renderer, Renderer: text::Renderer,
{ {
fn tag(&self) -> tree::Tag { fn tag(&self) -> tree::Tag {
@ -283,7 +280,7 @@ pub fn draw<Renderer>(
impl<'a, Message, Theme, Renderer> From<Text<'a, Theme, Renderer>> impl<'a, Message, Theme, Renderer> From<Text<'a, Theme, Renderer>>
for Element<'a, Message, Theme, Renderer> for Element<'a, Message, Theme, Renderer>
where where
Theme: StyleSheet + 'a, Theme: 'a,
Renderer: text::Renderer + 'a, Renderer: text::Renderer + 'a,
{ {
fn from( fn from(
@ -295,7 +292,6 @@ where
impl<'a, Theme, Renderer> Clone for Text<'a, Theme, Renderer> impl<'a, Theme, Renderer> Clone for Text<'a, Theme, Renderer>
where where
Theme: StyleSheet,
Renderer: text::Renderer, Renderer: text::Renderer,
{ {
fn clone(&self) -> Self { fn clone(&self) -> Self {
@ -316,7 +312,6 @@ where
impl<'a, Theme, Renderer> From<&'a str> for Text<'a, Theme, Renderer> impl<'a, Theme, Renderer> From<&'a str> for Text<'a, Theme, Renderer>
where where
Theme: StyleSheet,
Renderer: text::Renderer, Renderer: text::Renderer,
{ {
fn from(content: &'a str) -> Self { fn from(content: &'a str) -> Self {
@ -327,7 +322,7 @@ where
impl<'a, Message, Theme, Renderer> From<&'a str> impl<'a, Message, Theme, Renderer> From<&'a str>
for Element<'a, Message, Theme, Renderer> for Element<'a, Message, Theme, Renderer>
where where
Theme: StyleSheet + 'a, Theme: 'a,
Renderer: text::Renderer + 'a, Renderer: text::Renderer + 'a,
{ {
fn from(content: &'a str) -> Self { fn from(content: &'a str) -> Self {
@ -335,22 +330,6 @@ where
} }
} }
/// The style sheet of some text.
pub trait StyleSheet {
/// Returns the default styling strategy for [`Text`].
fn default() -> fn(&Self) -> Appearance {
|_| Appearance::default()
}
}
impl StyleSheet for Color {
fn default() -> fn(&Self) -> Appearance {
|color| Appearance {
color: Some(*color),
}
}
}
/// The apperance of some text. /// The apperance of some text.
#[derive(Debug, Clone, Copy, Default)] #[derive(Debug, Clone, Copy, Default)]
pub struct Appearance { pub struct Appearance {
@ -373,3 +352,15 @@ impl<Theme> Clone for Style<Theme> {
} }
impl<Theme> Copy for Style<Theme> {} impl<Theme> Copy for Style<Theme> {}
impl<Theme> Default for Style<Theme> {
fn default() -> Self {
Style::Colored(None)
}
}
impl<Theme> From<fn(&Theme) -> Appearance> for Style<Theme> {
fn from(f: fn(&Theme) -> Appearance) -> Self {
Style::Themed(f)
}
}

View file

@ -84,7 +84,7 @@ impl Sandbox for Gradient {
}; };
let gradient_box = themer( let gradient_box = themer(
move |_| appearance, appearance,
container(horizontal_space()) container(horizontal_space())
.width(Length::Fill) .width(Length::Fill)
.height(Length::Fill), .height(Length::Fill),

View file

@ -4,7 +4,6 @@ pub mod palette;
pub use palette::Palette; pub use palette::Palette;
use crate::application; use crate::application;
use crate::core::widget::text;
use std::fmt; use std::fmt;
use std::sync::Arc; use std::sync::Arc;
@ -265,5 +264,3 @@ impl<T: Fn(&Theme) -> application::Appearance> application::StyleSheet for T {
(self)(style) (self)(style)
} }
} }
impl text::StyleSheet for Theme {}

View file

@ -53,7 +53,6 @@ use crate::style::Theme;
#[allow(missing_debug_implementations)] #[allow(missing_debug_implementations)]
pub struct Button<'a, Message, Theme = crate::Theme, Renderer = crate::Renderer> pub struct Button<'a, Message, Theme = crate::Theme, Renderer = crate::Renderer>
where where
Theme: Style,
Renderer: crate::core::Renderer, Renderer: crate::core::Renderer,
{ {
content: Element<'a, Message, Theme, Renderer>, content: Element<'a, Message, Theme, Renderer>,
@ -62,18 +61,20 @@ where
height: Length, height: Length,
padding: Padding, padding: Padding,
clip: bool, clip: bool,
style: fn(&Theme, Status) -> Appearance, style: Style<Theme>,
} }
impl<'a, Message, Theme, Renderer> Button<'a, Message, Theme, Renderer> impl<'a, Message, Theme, Renderer> Button<'a, Message, Theme, Renderer>
where where
Theme: Style,
Renderer: crate::core::Renderer, Renderer: crate::core::Renderer,
{ {
/// Creates a new [`Button`] with the given content. /// Creates a new [`Button`] with the given content.
pub fn new( pub fn new(
content: impl Into<Element<'a, Message, Theme, Renderer>>, content: impl Into<Element<'a, Message, Theme, Renderer>>,
) -> Self { ) -> Self
where
Style<Theme>: Default,
{
let content = content.into(); let content = content.into();
let size = content.as_widget().size_hint(); let size = content.as_widget().size_hint();
@ -84,7 +85,7 @@ where
height: size.height.fluid(), height: size.height.fluid(),
padding: Padding::new(5.0), padding: Padding::new(5.0),
clip: false, clip: false,
style: Theme::DEFAULT, style: Style::default(),
} }
} }
@ -125,7 +126,7 @@ where
/// Sets the style variant of this [`Button`]. /// Sets the style variant of this [`Button`].
pub fn style(mut self, style: fn(&Theme, Status) -> Appearance) -> Self { pub fn style(mut self, style: fn(&Theme, Status) -> Appearance) -> Self {
self.style = style.into(); self.style = Style(style);
self self
} }
@ -146,7 +147,6 @@ impl<'a, Message, Theme, Renderer> Widget<Message, Theme, Renderer>
for Button<'a, Message, Theme, Renderer> for Button<'a, Message, Theme, Renderer>
where where
Message: 'a + Clone, Message: 'a + Clone,
Theme: Style,
Renderer: 'a + crate::core::Renderer, Renderer: 'a + crate::core::Renderer,
{ {
fn tag(&self) -> tree::Tag { fn tag(&self) -> tree::Tag {
@ -306,7 +306,7 @@ where
Status::Active Status::Active
}; };
let styling = (self.style)(theme, status); let styling = (self.style.0)(theme, status);
if styling.background.is_some() if styling.background.is_some()
|| styling.border.width > 0.0 || styling.border.width > 0.0
@ -380,7 +380,7 @@ impl<'a, Message, Theme, Renderer> From<Button<'a, Message, Theme, Renderer>>
for Element<'a, Message, Theme, Renderer> for Element<'a, Message, Theme, Renderer>
where where
Message: Clone + 'a, Message: Clone + 'a,
Theme: Style + 'a, Theme: 'a,
Renderer: crate::core::Renderer + 'a, Renderer: crate::core::Renderer + 'a,
{ {
fn from(button: Button<'a, Message, Theme, Renderer>) -> Self { fn from(button: Button<'a, Message, Theme, Renderer>) -> Self {
@ -428,14 +428,28 @@ impl std::default::Default for Appearance {
} }
} }
/// The default style of a [`Button`] for a given theme. /// The style of a [`Button`].
pub trait Style { #[derive(Debug, PartialEq, Eq)]
/// The default style. pub struct Style<Theme>(fn(&Theme, Status) -> Appearance);
const DEFAULT: fn(&Self, Status) -> Appearance;
impl<Theme> Clone for Style<Theme> {
fn clone(&self) -> Self {
*self
}
} }
impl Style for Theme { impl<Theme> Copy for Style<Theme> {}
const DEFAULT: fn(&Self, Status) -> Appearance = primary;
impl Default for Style<Theme> {
fn default() -> Self {
Style(primary)
}
}
impl<Theme> From<fn(&Theme, Status) -> Appearance> for Style<Theme> {
fn from(f: fn(&Theme, Status) -> Appearance) -> Self {
Style(f)
}
} }
/// A primary button; denoting a main action. /// A primary button; denoting a main action.

View file

@ -53,13 +53,12 @@ pub struct Checkbox<
text_shaping: text::Shaping, text_shaping: text::Shaping,
font: Option<Renderer::Font>, font: Option<Renderer::Font>,
icon: Icon<Renderer::Font>, icon: Icon<Renderer::Font>,
style: fn(&Theme, Status) -> Appearance, style: Style<Theme>,
} }
impl<'a, Message, Theme, Renderer> Checkbox<'a, Message, Theme, Renderer> impl<'a, Message, Theme, Renderer> Checkbox<'a, Message, Theme, Renderer>
where where
Renderer: text::Renderer, Renderer: text::Renderer,
Theme: Style,
{ {
/// The default size of a [`Checkbox`]. /// The default size of a [`Checkbox`].
const DEFAULT_SIZE: f32 = 15.0; const DEFAULT_SIZE: f32 = 15.0;
@ -72,7 +71,10 @@ where
/// It expects: /// It expects:
/// * the label of the [`Checkbox`] /// * the label of the [`Checkbox`]
/// * a boolean describing whether the [`Checkbox`] is checked or not /// * a boolean describing whether the [`Checkbox`] is checked or not
pub fn new(label: impl Into<String>, is_checked: bool) -> Self { pub fn new(label: impl Into<String>, is_checked: bool) -> Self
where
Style<Theme>: Default,
{
Checkbox { Checkbox {
is_checked, is_checked,
on_toggle: None, on_toggle: None,
@ -91,7 +93,7 @@ where
line_height: text::LineHeight::default(), line_height: text::LineHeight::default(),
shaping: text::Shaping::Basic, shaping: text::Shaping::Basic,
}, },
style: Theme::style(), style: Style::default(),
} }
} }
@ -175,7 +177,7 @@ where
/// Sets the style of the [`Checkbox`]. /// Sets the style of the [`Checkbox`].
pub fn style(mut self, style: fn(&Theme, Status) -> Appearance) -> Self { pub fn style(mut self, style: fn(&Theme, Status) -> Appearance) -> Self {
self.style = style.into(); self.style = Style(style);
self self
} }
} }
@ -301,7 +303,7 @@ where
Status::Active { is_checked } Status::Active { is_checked }
}; };
let appearance = (self.style)(theme, status); let appearance = (self.style.0)(theme, status);
{ {
let layout = children.next().unwrap(); let layout = children.next().unwrap();
@ -423,15 +425,27 @@ pub struct Appearance {
pub text_color: Option<Color>, pub text_color: Option<Color>,
} }
/// A set of rules that dictate the style of a checkbox. /// The style of a [`Checkbox`].
pub trait Style { #[derive(Debug, PartialEq, Eq)]
/// The supported style of the [`StyleSheet`]. pub struct Style<Theme>(fn(&Theme, Status) -> Appearance);
fn style() -> fn(&Self, Status) -> Appearance;
impl<Theme> Clone for Style<Theme> {
fn clone(&self) -> Self {
*self
}
} }
impl Style for Theme { impl<Theme> Copy for Style<Theme> {}
fn style() -> fn(&Self, Status) -> Appearance {
primary impl Default for Style<Theme> {
fn default() -> Self {
Style(primary)
}
}
impl<Theme> From<fn(&Theme, Status) -> Appearance> for Style<Theme> {
fn from(f: fn(&Theme, Status) -> Appearance) -> Self {
Style(f)
} }
} }

View file

@ -1,5 +1,4 @@
//! Display a dropdown list of searchable and selectable options. //! Display a dropdown list of searchable and selectable options.
use crate::container;
use crate::core::event::{self, Event}; use crate::core::event::{self, Event};
use crate::core::keyboard; use crate::core::keyboard;
use crate::core::keyboard::key; use crate::core::keyboard::key;
@ -14,7 +13,6 @@ use crate::core::{
Clipboard, Element, Length, Padding, Rectangle, Shell, Size, Vector, Clipboard, Element, Length, Padding, Rectangle, Shell, Size, Vector,
}; };
use crate::overlay::menu; use crate::overlay::menu;
use crate::scrollable;
use crate::style::Theme; use crate::style::Theme;
use crate::text::LineHeight; use crate::text::LineHeight;
use crate::text_input::{self, TextInput}; use crate::text_input::{self, TextInput};
@ -65,14 +63,16 @@ where
on_selected: impl Fn(T) -> Message + 'static, on_selected: impl Fn(T) -> Message + 'static,
) -> Self ) -> Self
where where
Theme: text_input::Style,
Style<Theme>: Default, Style<Theme>: Default,
{ {
let style = Style::<Theme>::default(); let style = Style::<Theme>::default();
let text_input = TextInput::new(placeholder, &state.value()) let text_input = TextInput::with_style(
.on_input(TextInputEvent::TextChanged) placeholder,
.style(style.text_input); &state.value(),
style.text_input,
)
.on_input(TextInputEvent::TextChanged);
let selection = selection.map(T::to_string).unwrap_or_default(); let selection = selection.map(T::to_string).unwrap_or_default();
@ -294,7 +294,6 @@ impl<'a, T, Message, Theme, Renderer> Widget<Message, Theme, Renderer>
where where
T: Display + Clone + 'static, T: Display + Clone + 'static,
Message: Clone, Message: Clone,
Theme: scrollable::Style + container::Style,
Renderer: text::Renderer, Renderer: text::Renderer,
{ {
fn size(&self) -> Size<Length> { fn size(&self) -> Size<Length> {
@ -711,7 +710,7 @@ impl<'a, T, Message, Theme, Renderer>
where where
T: Display + Clone + 'static, T: Display + Clone + 'static,
Message: Clone + 'a, Message: Clone + 'a,
Theme: scrollable::Style + container::Style + 'a, Theme: 'a,
Renderer: text::Renderer + 'a, Renderer: text::Renderer + 'a,
{ {
fn from(combo_box: ComboBox<'a, T, Message, Theme, Renderer>) -> Self { fn from(combo_box: ComboBox<'a, T, Message, Theme, Renderer>) -> Self {

View file

@ -34,21 +34,30 @@ pub struct Container<
max_height: f32, max_height: f32,
horizontal_alignment: alignment::Horizontal, horizontal_alignment: alignment::Horizontal,
vertical_alignment: alignment::Vertical, vertical_alignment: alignment::Vertical,
style: fn(&Theme, Status) -> Appearance,
clip: bool, clip: bool,
content: Element<'a, Message, Theme, Renderer>, content: Element<'a, Message, Theme, Renderer>,
style: Style<Theme>,
} }
impl<'a, Message, Theme, Renderer> Container<'a, Message, Theme, Renderer> impl<'a, Message, Theme, Renderer> Container<'a, Message, Theme, Renderer>
where where
Renderer: crate::core::Renderer, Renderer: crate::core::Renderer,
{ {
/// Creates an empty [`Container`]. /// Creates a [`Container`] with the given content.
pub fn new<T>(content: T) -> Self pub fn new(
content: impl Into<Element<'a, Message, Theme, Renderer>>,
) -> Self
where where
Theme: Style, Style<Theme>: Default,
T: Into<Element<'a, Message, Theme, Renderer>>,
{ {
Self::with_style(content, Style::default().0)
}
/// Creates a [`Container`] with the given content and style.
pub fn with_style(
content: impl Into<Element<'a, Message, Theme, Renderer>>,
style: fn(&Theme, Status) -> Appearance,
) -> Self {
let content = content.into(); let content = content.into();
let size = content.as_widget().size_hint(); let size = content.as_widget().size_hint();
@ -61,9 +70,9 @@ where
max_height: f32::INFINITY, max_height: f32::INFINITY,
horizontal_alignment: alignment::Horizontal::Left, horizontal_alignment: alignment::Horizontal::Left,
vertical_alignment: alignment::Vertical::Top, vertical_alignment: alignment::Vertical::Top,
style: Theme::style(),
clip: false, clip: false,
content, content,
style: Style(style),
} }
} }
@ -129,7 +138,7 @@ where
/// Sets the style of the [`Container`]. /// Sets the style of the [`Container`].
pub fn style(mut self, style: fn(&Theme, Status) -> Appearance) -> Self { pub fn style(mut self, style: fn(&Theme, Status) -> Appearance) -> Self {
self.style = style; self.style = Style(style);
self self
} }
@ -267,7 +276,7 @@ where
Status::Idle Status::Idle
}; };
let style = (self.style)(theme, status); let style = (self.style.0)(theme, status);
if let Some(clipped_viewport) = bounds.intersection(viewport) { if let Some(clipped_viewport) = bounds.intersection(viewport) {
draw_background(renderer, &style, bounds); draw_background(renderer, &style, bounds);
@ -537,26 +546,46 @@ pub enum Status {
Hovered, Hovered,
} }
/// The style of a [`Container`] for a theme. /// The style of a [`Container`].
pub trait Style { #[derive(Debug, PartialEq, Eq)]
/// The default style of a [`Container`]. pub struct Style<Theme>(fn(&Theme, Status) -> Appearance);
fn style() -> fn(&Self, Status) -> Appearance;
}
impl Style for Theme { impl<Theme> Style<Theme> {
fn style() -> fn(&Self, Status) -> Appearance { /// Resolves the [`Style`] with the given `Theme` and [`Status`] to
transparent /// produce an [`Appearance`].
pub fn resolve(self, theme: &Theme, status: Status) -> Appearance {
(self.0)(theme, status)
} }
} }
impl Style for Appearance { impl<Theme> Clone for Style<Theme> {
fn style() -> fn(&Self, Status) -> Appearance { fn clone(&self) -> Self {
|appearance, _status| *appearance *self
}
}
impl<Theme> Copy for Style<Theme> {}
impl Default for Style<Theme> {
fn default() -> Self {
Style(transparent)
}
}
impl Default for Style<Appearance> {
fn default() -> Self {
Style(|appearance, _status| *appearance)
}
}
impl<Theme> From<fn(&Theme, Status) -> Appearance> for Style<Theme> {
fn from(f: fn(&Theme, Status) -> Appearance) -> Self {
Style(f)
} }
} }
/// A transparent [`Container`]. /// A transparent [`Container`].
pub fn transparent(_theme: &Theme, _status: Status) -> Appearance { pub fn transparent<Theme>(_theme: &Theme, _status: Status) -> Appearance {
Appearance::default() Appearance::default()
} }

View file

@ -14,7 +14,7 @@ use crate::rule::{self, Rule};
use crate::runtime::Command; use crate::runtime::Command;
use crate::scrollable::{self, Scrollable}; use crate::scrollable::{self, Scrollable};
use crate::slider::{self, Slider}; use crate::slider::{self, Slider};
use crate::text::{self, Text}; use crate::text::Text;
use crate::text_editor::{self, TextEditor}; use crate::text_editor::{self, TextEditor};
use crate::text_input::{self, TextInput}; use crate::text_input::{self, TextInput};
use crate::toggler::{self, Toggler}; use crate::toggler::{self, Toggler};
@ -58,8 +58,8 @@ pub fn container<'a, Message, Theme, Renderer>(
content: impl Into<Element<'a, Message, Theme, Renderer>>, content: impl Into<Element<'a, Message, Theme, Renderer>>,
) -> Container<'a, Message, Theme, Renderer> ) -> Container<'a, Message, Theme, Renderer>
where where
Theme: container::Style,
Renderer: core::Renderer, Renderer: core::Renderer,
container::Style<Theme>: Default,
{ {
Container::new(content) Container::new(content)
} }
@ -104,8 +104,8 @@ pub fn scrollable<'a, Message, Theme, Renderer>(
content: impl Into<Element<'a, Message, Theme, Renderer>>, content: impl Into<Element<'a, Message, Theme, Renderer>>,
) -> Scrollable<'a, Message, Theme, Renderer> ) -> Scrollable<'a, Message, Theme, Renderer>
where where
Theme: scrollable::Style,
Renderer: core::Renderer, Renderer: core::Renderer,
scrollable::Style<Theme>: Default,
{ {
Scrollable::new(content) Scrollable::new(content)
} }
@ -118,7 +118,7 @@ pub fn button<'a, Message, Theme, Renderer>(
) -> Button<'a, Message, Theme, Renderer> ) -> Button<'a, Message, Theme, Renderer>
where where
Renderer: core::Renderer, Renderer: core::Renderer,
Theme: button::Style, button::Style<Theme>: Default,
{ {
Button::new(content) Button::new(content)
} }
@ -134,8 +134,8 @@ pub fn tooltip<'a, Message, Theme, Renderer>(
position: tooltip::Position, position: tooltip::Position,
) -> crate::Tooltip<'a, Message, Theme, Renderer> ) -> crate::Tooltip<'a, Message, Theme, Renderer>
where where
Theme: container::Style + text::StyleSheet,
Renderer: core::text::Renderer, Renderer: core::text::Renderer,
container::Style<Theme>: Default,
{ {
Tooltip::new(content, tooltip, position) Tooltip::new(content, tooltip, position)
} }
@ -147,7 +147,6 @@ pub fn text<'a, Theme, Renderer>(
text: impl ToString, text: impl ToString,
) -> Text<'a, Theme, Renderer> ) -> Text<'a, Theme, Renderer>
where where
Theme: text::StyleSheet,
Renderer: core::text::Renderer, Renderer: core::text::Renderer,
{ {
Text::new(text.to_string()) Text::new(text.to_string())
@ -161,8 +160,8 @@ pub fn checkbox<'a, Message, Theme, Renderer>(
is_checked: bool, is_checked: bool,
) -> Checkbox<'a, Message, Theme, Renderer> ) -> Checkbox<'a, Message, Theme, Renderer>
where where
Theme: checkbox::Style,
Renderer: core::text::Renderer, Renderer: core::text::Renderer,
checkbox::Style<Theme>: Default,
{ {
Checkbox::new(label, is_checked) Checkbox::new(label, is_checked)
} }
@ -178,9 +177,9 @@ pub fn radio<Message, Theme, Renderer, V>(
) -> Radio<Message, Theme, Renderer> ) -> Radio<Message, Theme, Renderer>
where where
Message: Clone, Message: Clone,
Theme: radio::Style,
Renderer: core::text::Renderer, Renderer: core::text::Renderer,
V: Copy + Eq, V: Copy + Eq,
radio::Style<Theme>: Default,
{ {
Radio::new(label, value, selected, on_click) Radio::new(label, value, selected, on_click)
} }
@ -195,7 +194,7 @@ pub fn toggler<'a, Message, Theme, Renderer>(
) -> Toggler<'a, Message, Theme, Renderer> ) -> Toggler<'a, Message, Theme, Renderer>
where where
Renderer: core::text::Renderer, Renderer: core::text::Renderer,
Theme: toggler::Style, toggler::Style<Theme>: Default,
{ {
Toggler::new(label, is_checked, f) Toggler::new(label, is_checked, f)
} }
@ -209,8 +208,8 @@ pub fn text_input<'a, Message, Theme, Renderer>(
) -> TextInput<'a, Message, Theme, Renderer> ) -> TextInput<'a, Message, Theme, Renderer>
where where
Message: Clone, Message: Clone,
Theme: text_input::Style,
Renderer: core::text::Renderer, Renderer: core::text::Renderer,
text_input::Style<Theme>: Default,
{ {
TextInput::new(placeholder, value) TextInput::new(placeholder, value)
} }
@ -223,8 +222,8 @@ pub fn text_editor<Message, Theme, Renderer>(
) -> TextEditor<'_, core::text::highlighter::PlainText, Message, Theme, Renderer> ) -> TextEditor<'_, core::text::highlighter::PlainText, Message, Theme, Renderer>
where where
Message: Clone, Message: Clone,
Theme: text_editor::Style,
Renderer: core::text::Renderer, Renderer: core::text::Renderer,
text_editor::Style<Theme>: Default,
{ {
TextEditor::new(content) TextEditor::new(content)
} }
@ -240,7 +239,7 @@ pub fn slider<'a, T, Message, Theme>(
where where
T: Copy + From<u8> + std::cmp::PartialOrd, T: Copy + From<u8> + std::cmp::PartialOrd,
Message: Clone, Message: Clone,
Theme: slider::Style, slider::Style<Theme>: Default,
{ {
Slider::new(range, value, on_change) Slider::new(range, value, on_change)
} }
@ -256,7 +255,7 @@ pub fn vertical_slider<'a, T, Message, Theme>(
where where
T: Copy + From<u8> + std::cmp::PartialOrd, T: Copy + From<u8> + std::cmp::PartialOrd,
Message: Clone, Message: Clone,
Theme: vertical_slider::Style, vertical_slider::Style<Theme>: Default,
{ {
VerticalSlider::new(range, value, on_change) VerticalSlider::new(range, value, on_change)
} }
@ -291,7 +290,6 @@ pub fn combo_box<'a, T, Message, Theme, Renderer>(
) -> ComboBox<'a, T, Message, Theme, Renderer> ) -> ComboBox<'a, T, Message, Theme, Renderer>
where where
T: std::fmt::Display + Clone, T: std::fmt::Display + Clone,
Theme: text_input::Style,
Renderer: core::text::Renderer, Renderer: core::text::Renderer,
combo_box::Style<Theme>: Default, combo_box::Style<Theme>: Default,
{ {
@ -319,7 +317,7 @@ pub fn vertical_space() -> Space {
/// [`Rule`]: crate::Rule /// [`Rule`]: crate::Rule
pub fn horizontal_rule<Theme>(height: impl Into<Pixels>) -> Rule<Theme> pub fn horizontal_rule<Theme>(height: impl Into<Pixels>) -> Rule<Theme>
where where
Theme: rule::Style, rule::Style<Theme>: Default,
{ {
Rule::horizontal(height) Rule::horizontal(height)
} }
@ -329,7 +327,7 @@ where
/// [`Rule`]: crate::Rule /// [`Rule`]: crate::Rule
pub fn vertical_rule<Theme>(width: impl Into<Pixels>) -> Rule<Theme> pub fn vertical_rule<Theme>(width: impl Into<Pixels>) -> Rule<Theme>
where where
Theme: rule::Style, rule::Style<Theme>: Default,
{ {
Rule::vertical(width) Rule::vertical(width)
} }
@ -346,7 +344,7 @@ pub fn progress_bar<Theme>(
value: f32, value: f32,
) -> ProgressBar<Theme> ) -> ProgressBar<Theme>
where where
Theme: progress_bar::Style, progress_bar::Style<Theme>: Default,
{ {
ProgressBar::new(range, value) ProgressBar::new(range, value)
} }
@ -392,7 +390,7 @@ where
#[cfg(feature = "qr_code")] #[cfg(feature = "qr_code")]
pub fn qr_code<Theme>(data: &crate::qr_code::Data) -> crate::QRCode<'_, Theme> pub fn qr_code<Theme>(data: &crate::qr_code::Data) -> crate::QRCode<'_, Theme>
where where
Theme: crate::qr_code::Style, crate::qr_code::Style<Theme>: Default,
{ {
crate::QRCode::new(data) crate::QRCode::new(data)
} }
@ -435,13 +433,20 @@ where
} }
/// A widget that applies any `Theme` to its contents. /// A widget that applies any `Theme` to its contents.
pub fn themer<'a, Message, OldTheme, NewTheme, F, Renderer>( pub fn themer<'a, Message, OldTheme, NewTheme, Renderer>(
to_theme: F, new_theme: NewTheme,
content: impl Into<Element<'a, Message, NewTheme, Renderer>>, content: impl Into<Element<'a, Message, NewTheme, Renderer>>,
) -> Themer<'a, Message, OldTheme, NewTheme, F, Renderer> ) -> Themer<
'a,
Message,
OldTheme,
NewTheme,
impl Fn(&OldTheme) -> NewTheme,
Renderer,
>
where where
F: Fn(&OldTheme) -> NewTheme,
Renderer: core::Renderer, Renderer: core::Renderer,
NewTheme: Clone,
{ {
Themer::new(to_theme, content) Themer::new(move |_| new_theme.clone(), content)
} }

View file

@ -46,7 +46,7 @@ impl<'a, T, Message, Theme, Renderer> Menu<'a, T, Message, Theme, Renderer>
where where
T: ToString + Clone, T: ToString + Clone,
Message: 'a, Message: 'a,
Theme: container::Style + scrollable::Style + 'a, Theme: 'a,
Renderer: text::Renderer + 'a, Renderer: text::Renderer + 'a,
{ {
/// Creates a new [`Menu`] with the given [`State`], a list of options, and /// Creates a new [`Menu`] with the given [`State`], a list of options, and
@ -197,7 +197,7 @@ where
impl<'a, Message, Theme, Renderer> Overlay<'a, Message, Theme, Renderer> impl<'a, Message, Theme, Renderer> Overlay<'a, Message, Theme, Renderer>
where where
Message: 'a, Message: 'a,
Theme: container::Style + scrollable::Style + 'a, Theme: 'a,
Renderer: text::Renderer + 'a, Renderer: text::Renderer + 'a,
{ {
pub fn new<T>( pub fn new<T>(
@ -223,20 +223,24 @@ where
style, style,
} = menu; } = menu;
let container = Container::new( let container = Container::with_style(
Scrollable::new(List { Scrollable::with_direction_and_style(
options, List {
hovered_option, options,
on_selected, hovered_option,
on_option_hovered, on_selected,
font, on_option_hovered,
text_size, font,
text_line_height, text_size,
text_shaping, text_line_height,
padding, text_shaping,
style: style.menu, padding,
}) style: style.menu,
.style(style.scrollable), },
scrollable::Direction::default(),
style.scrollable,
),
container::transparent,
); );
state.tree.diff(&container as &dyn Widget<_, _, _>); state.tree.diff(&container as &dyn Widget<_, _, _>);

View file

@ -112,7 +112,7 @@ pub struct PaneGrid<
on_click: Option<Box<dyn Fn(Pane) -> Message + 'a>>, on_click: Option<Box<dyn Fn(Pane) -> Message + 'a>>,
on_drag: Option<Box<dyn Fn(DragEvent) -> Message + 'a>>, on_drag: Option<Box<dyn Fn(DragEvent) -> Message + 'a>>,
on_resize: Option<(f32, Box<dyn Fn(ResizeEvent) -> Message + 'a>)>, on_resize: Option<(f32, Box<dyn Fn(ResizeEvent) -> Message + 'a>)>,
style: fn(&Theme) -> Appearance, style: Style<Theme>,
} }
impl<'a, Message, Theme, Renderer> PaneGrid<'a, Message, Theme, Renderer> impl<'a, Message, Theme, Renderer> PaneGrid<'a, Message, Theme, Renderer>
@ -128,7 +128,7 @@ where
view: impl Fn(Pane, &'a T, bool) -> Content<'a, Message, Theme, Renderer>, view: impl Fn(Pane, &'a T, bool) -> Content<'a, Message, Theme, Renderer>,
) -> Self ) -> Self
where where
Theme: Style, Style<Theme>: Default,
{ {
let contents = if let Some((pane, pane_state)) = let contents = if let Some((pane, pane_state)) =
state.maximized.and_then(|pane| { state.maximized.and_then(|pane| {
@ -160,7 +160,7 @@ where
on_click: None, on_click: None,
on_drag: None, on_drag: None,
on_resize: None, on_resize: None,
style: Theme::style(), style: Style::default(),
} }
} }
@ -221,7 +221,7 @@ where
/// Sets the style of the [`PaneGrid`]. /// Sets the style of the [`PaneGrid`].
pub fn style(mut self, style: fn(&Theme) -> Appearance) -> Self { pub fn style(mut self, style: fn(&Theme) -> Appearance) -> Self {
self.style = style; self.style = Style(style);
self self
} }
@ -679,7 +679,7 @@ where
None None
}; };
let appearance = (self.style)(theme); let appearance = (self.style.0)(theme);
for ((id, (content, tree)), pane_layout) in for ((id, (content, tree)), pane_layout) in
contents.zip(layout.children()) contents.zip(layout.children())
@ -1147,15 +1147,27 @@ pub struct Line {
pub width: f32, pub width: f32,
} }
/// The definiton of the default style of a [`PaneGrid`]. /// The style of a [`PaneGrid`].
pub trait Style { #[derive(Debug, PartialEq, Eq)]
/// Returns the default style of a [`PaneGrid`]. pub struct Style<Theme>(fn(&Theme) -> Appearance);
fn style() -> fn(&Self) -> Appearance;
impl<Theme> Clone for Style<Theme> {
fn clone(&self) -> Self {
*self
}
} }
impl Style for Theme { impl<Theme> Copy for Style<Theme> {}
fn style() -> fn(&Self) -> Appearance {
default impl Default for Style<Theme> {
fn default() -> Self {
Style(default)
}
}
impl<Theme> From<fn(&Theme) -> Appearance> for Style<Theme> {
fn from(f: fn(&Theme) -> Appearance) -> Self {
Style(f)
} }
} }

View file

@ -24,7 +24,7 @@ pub struct Content<
{ {
title_bar: Option<TitleBar<'a, Message, Theme, Renderer>>, title_bar: Option<TitleBar<'a, Message, Theme, Renderer>>,
body: Element<'a, Message, Theme, Renderer>, body: Element<'a, Message, Theme, Renderer>,
style: fn(&Theme, container::Status) -> container::Appearance, style: container::Style<Theme>,
} }
impl<'a, Message, Theme, Renderer> Content<'a, Message, Theme, Renderer> impl<'a, Message, Theme, Renderer> Content<'a, Message, Theme, Renderer>
@ -34,12 +34,12 @@ where
/// Creates a new [`Content`] with the provided body. /// Creates a new [`Content`] with the provided body.
pub fn new(body: impl Into<Element<'a, Message, Theme, Renderer>>) -> Self pub fn new(body: impl Into<Element<'a, Message, Theme, Renderer>>) -> Self
where where
Theme: container::Style, container::Style<Theme>: Default,
{ {
Self { Self {
title_bar: None, title_bar: None,
body: body.into(), body: body.into(),
style: Theme::style(), style: container::Style::default(),
} }
} }
@ -57,7 +57,7 @@ where
mut self, mut self,
style: fn(&Theme, container::Status) -> container::Appearance, style: fn(&Theme, container::Status) -> container::Appearance,
) -> Self { ) -> Self {
self.style = style; self.style = style.into();
self self
} }
} }
@ -114,7 +114,7 @@ where
container::Status::Idle container::Status::Idle
}; };
(self.style)(theme, status) self.style.resolve(theme, status)
}; };
container::draw_background(renderer, &style, bounds); container::draw_background(renderer, &style, bounds);
@ -403,8 +403,8 @@ impl<'a, T, Message, Theme, Renderer> From<T>
for Content<'a, Message, Theme, Renderer> for Content<'a, Message, Theme, Renderer>
where where
T: Into<Element<'a, Message, Theme, Renderer>>, T: Into<Element<'a, Message, Theme, Renderer>>,
Theme: container::Style,
Renderer: crate::core::Renderer, Renderer: crate::core::Renderer,
container::Style<Theme>: Default,
{ {
fn from(element: T) -> Self { fn from(element: T) -> Self {
Self::new(element) Self::new(element)

View file

@ -25,7 +25,7 @@ pub struct TitleBar<
controls: Option<Element<'a, Message, Theme, Renderer>>, controls: Option<Element<'a, Message, Theme, Renderer>>,
padding: Padding, padding: Padding,
always_show_controls: bool, always_show_controls: bool,
style: fn(&Theme, container::Status) -> container::Appearance, style: container::Style<Theme>,
} }
impl<'a, Message, Theme, Renderer> TitleBar<'a, Message, Theme, Renderer> impl<'a, Message, Theme, Renderer> TitleBar<'a, Message, Theme, Renderer>
@ -33,17 +33,18 @@ where
Renderer: crate::core::Renderer, Renderer: crate::core::Renderer,
{ {
/// Creates a new [`TitleBar`] with the given content. /// Creates a new [`TitleBar`] with the given content.
pub fn new<E>(content: E) -> Self pub fn new(
content: impl Into<Element<'a, Message, Theme, Renderer>>,
) -> Self
where where
Theme: container::Style, container::Style<Theme>: Default,
E: Into<Element<'a, Message, Theme, Renderer>>,
{ {
Self { Self {
content: content.into(), content: content.into(),
controls: None, controls: None,
padding: Padding::ZERO, padding: Padding::ZERO,
always_show_controls: false, always_show_controls: false,
style: Theme::style(), style: container::Style::default(),
} }
} }
@ -67,7 +68,7 @@ where
mut self, mut self,
style: fn(&Theme, container::Status) -> container::Appearance, style: fn(&Theme, container::Status) -> container::Appearance,
) -> Self { ) -> Self {
self.style = style; self.style = style.into();
self self
} }
@ -137,7 +138,7 @@ where
container::Status::Idle container::Status::Idle
}; };
(self.style)(theme, status) self.style.resolve(theme, status)
}; };
let inherited_style = renderer::Style { let inherited_style = renderer::Style {

View file

@ -1,5 +1,4 @@
//! Display a dropdown list of selectable values. //! Display a dropdown list of selectable values.
use crate::container;
use crate::core::alignment; use crate::core::alignment;
use crate::core::event::{self, Event}; use crate::core::event::{self, Event};
use crate::core::keyboard; use crate::core::keyboard;
@ -15,7 +14,6 @@ use crate::core::{
Pixels, Point, Rectangle, Shell, Size, Vector, Widget, Pixels, Point, Rectangle, Shell, Size, Vector, Widget,
}; };
use crate::overlay::menu::{self, Menu}; use crate::overlay::menu::{self, Menu};
use crate::scrollable;
use crate::style::Theme; use crate::style::Theme;
use std::borrow::Borrow; use std::borrow::Borrow;
@ -169,7 +167,6 @@ where
L: Borrow<[T]>, L: Borrow<[T]>,
V: Borrow<T>, V: Borrow<T>,
Message: Clone + 'a, Message: Clone + 'a,
Theme: scrollable::Style + container::Style,
Renderer: text::Renderer + 'a, Renderer: text::Renderer + 'a,
{ {
fn tag(&self) -> tree::Tag { fn tag(&self) -> tree::Tag {
@ -426,7 +423,7 @@ where
L: Borrow<[T]> + 'a, L: Borrow<[T]> + 'a,
V: Borrow<T> + 'a, V: Borrow<T> + 'a,
Message: Clone + 'a, Message: Clone + 'a,
Theme: scrollable::Style + container::Style + 'a, Theme: 'a,
Renderer: text::Renderer + 'a, Renderer: text::Renderer + 'a,
{ {
fn from( fn from(

View file

@ -28,7 +28,7 @@ pub struct ProgressBar<Theme = crate::Theme> {
value: f32, value: f32,
width: Length, width: Length,
height: Option<Length>, height: Option<Length>,
style: fn(&Theme) -> Appearance, style: Style<Theme>,
} }
impl<Theme> ProgressBar<Theme> { impl<Theme> ProgressBar<Theme> {
@ -42,14 +42,14 @@ impl<Theme> ProgressBar<Theme> {
/// * the current value of the [`ProgressBar`] /// * the current value of the [`ProgressBar`]
pub fn new(range: RangeInclusive<f32>, value: f32) -> Self pub fn new(range: RangeInclusive<f32>, value: f32) -> Self
where where
Theme: Style, Style<Theme>: Default,
{ {
ProgressBar { ProgressBar {
value: value.clamp(*range.start(), *range.end()), value: value.clamp(*range.start(), *range.end()),
range, range,
width: Length::Fill, width: Length::Fill,
height: None, height: None,
style: Theme::style(), style: Style::default(),
} }
} }
@ -67,7 +67,7 @@ impl<Theme> ProgressBar<Theme> {
/// Sets the style of the [`ProgressBar`]. /// Sets the style of the [`ProgressBar`].
pub fn style(mut self, style: fn(&Theme) -> Appearance) -> Self { pub fn style(mut self, style: fn(&Theme) -> Appearance) -> Self {
self.style = style; self.style = style.into();
self self
} }
} }
@ -117,7 +117,7 @@ where
/ (range_end - range_start) / (range_end - range_start)
}; };
let appearance = (self.style)(theme); let appearance = (self.style.0)(theme);
renderer.fill_quad( renderer.fill_quad(
renderer::Quad { renderer::Quad {
@ -169,15 +169,27 @@ pub struct Appearance {
pub border: Border, pub border: Border,
} }
/// The definiton of the default style of a [`ProgressBar`]. /// The style of a [`ProgressBar`].
pub trait Style { #[derive(Debug, PartialEq, Eq)]
/// Returns the default style of a [`ProgressBar`]. pub struct Style<Theme>(fn(&Theme) -> Appearance);
fn style() -> fn(&Self) -> Appearance;
impl<Theme> Clone for Style<Theme> {
fn clone(&self) -> Self {
*self
}
} }
impl Style for Theme { impl<Theme> Copy for Style<Theme> {}
fn style() -> fn(&Self) -> Appearance {
primary impl Default for Style<Theme> {
fn default() -> Self {
Style(primary)
}
}
impl<Theme> From<fn(&Theme) -> Appearance> for Style<Theme> {
fn from(f: fn(&Theme) -> Appearance) -> Self {
Style(f)
} }
} }

View file

@ -23,19 +23,19 @@ const QUIET_ZONE: usize = 2;
pub struct QRCode<'a, Theme = crate::Theme> { pub struct QRCode<'a, Theme = crate::Theme> {
data: &'a Data, data: &'a Data,
cell_size: u16, cell_size: u16,
style: fn(&Theme) -> Appearance, style: Style<Theme>,
} }
impl<'a, Theme> QRCode<'a, Theme> { impl<'a, Theme> QRCode<'a, Theme> {
/// Creates a new [`QRCode`] with the provided [`Data`]. /// Creates a new [`QRCode`] with the provided [`Data`].
pub fn new(data: &'a Data) -> Self pub fn new(data: &'a Data) -> Self
where where
Theme: Style, Style<Theme>: Default,
{ {
Self { Self {
data, data,
cell_size: DEFAULT_CELL_SIZE, cell_size: DEFAULT_CELL_SIZE,
style: Theme::style(), style: Style::default(),
} }
} }
@ -47,7 +47,7 @@ impl<'a, Theme> QRCode<'a, Theme> {
/// Sets the style of the [`QRCode`]. /// Sets the style of the [`QRCode`].
pub fn style(mut self, style: fn(&Theme) -> Appearance) -> Self { pub fn style(mut self, style: fn(&Theme) -> Appearance) -> Self {
self.style = style; self.style = style.into();
self self
} }
} }
@ -97,7 +97,7 @@ impl<'a, Message, Theme> Widget<Message, Theme, Renderer>
let bounds = layout.bounds(); let bounds = layout.bounds();
let side_length = self.data.width + 2 * QUIET_ZONE; let side_length = self.data.width + 2 * QUIET_ZONE;
let appearance = (self.style)(theme); let appearance = (self.style.0)(theme);
let mut last_appearance = state.last_appearance.borrow_mut(); let mut last_appearance = state.last_appearance.borrow_mut();
if Some(appearance) != *last_appearance { if Some(appearance) != *last_appearance {
@ -335,15 +335,27 @@ pub struct Appearance {
pub background: Color, pub background: Color,
} }
/// The definiton of the default style of a [`QRCode`]. /// The style of a [`QRCode`].
pub trait Style { #[derive(Debug, PartialEq, Eq)]
/// Returns the default style of a [`QRCode`]. pub struct Style<Theme>(fn(&Theme) -> Appearance);
fn style() -> fn(&Self) -> Appearance;
impl<Theme> Clone for Style<Theme> {
fn clone(&self) -> Self {
*self
}
} }
impl Style for Theme { impl<Theme> Copy for Style<Theme> {}
fn style() -> fn(&Self) -> Appearance {
default impl Default for Style<Theme> {
fn default() -> Self {
Style(default)
}
}
impl<Theme> From<fn(&Theme) -> Appearance> for Style<Theme> {
fn from(f: fn(&Theme) -> Appearance) -> Self {
Style(f)
} }
} }

View file

@ -82,7 +82,7 @@ where
text_line_height: text::LineHeight, text_line_height: text::LineHeight,
text_shaping: text::Shaping, text_shaping: text::Shaping,
font: Option<Renderer::Font>, font: Option<Renderer::Font>,
style: fn(&Theme, Status) -> Appearance, style: Style<Theme>,
} }
impl<Message, Theme, Renderer> Radio<Message, Theme, Renderer> impl<Message, Theme, Renderer> Radio<Message, Theme, Renderer>
@ -111,7 +111,7 @@ where
f: F, f: F,
) -> Self ) -> Self
where where
Theme: Style, Style<Theme>: Default,
V: Eq + Copy, V: Eq + Copy,
F: FnOnce(V) -> Message, F: FnOnce(V) -> Message,
{ {
@ -126,7 +126,7 @@ where
text_line_height: text::LineHeight::default(), text_line_height: text::LineHeight::default(),
text_shaping: text::Shaping::Basic, text_shaping: text::Shaping::Basic,
font: None, font: None,
style: Theme::style(), style: Style::default(),
} }
} }
@ -177,7 +177,7 @@ where
/// Sets the style of the [`Radio`] button. /// Sets the style of the [`Radio`] button.
pub fn style(mut self, style: fn(&Theme, Status) -> Appearance) -> Self { pub fn style(mut self, style: fn(&Theme, Status) -> Appearance) -> Self {
self.style = style.into(); self.style = Style(style);
self self
} }
} }
@ -298,7 +298,7 @@ where
Status::Active { is_selected } Status::Active { is_selected }
}; };
let appearance = (self.style)(theme, status); let appearance = (self.style.0)(theme, status);
{ {
let layout = children.next().unwrap(); let layout = children.next().unwrap();
@ -398,15 +398,27 @@ pub struct Appearance {
pub text_color: Option<Color>, pub text_color: Option<Color>,
} }
/// The definiton of the default style of a [`Radio`] button. /// The style of a [`Radio`] button.
pub trait Style { #[derive(Debug, PartialEq, Eq)]
/// Returns the default style of a [`Radio`] button. pub struct Style<Theme>(fn(&Theme, Status) -> Appearance);
fn style() -> fn(&Self, Status) -> Appearance;
impl<Theme> Clone for Style<Theme> {
fn clone(&self) -> Self {
*self
}
} }
impl Style for Theme { impl<Theme> Copy for Style<Theme> {}
fn style() -> fn(&Self, Status) -> Appearance {
default impl Default for Style<Theme> {
fn default() -> Self {
Style(default)
}
}
impl<Theme> From<fn(&Theme, Status) -> Appearance> for Style<Theme> {
fn from(f: fn(&Theme, Status) -> Appearance) -> Self {
Style(f)
} }
} }

View file

@ -15,39 +15,39 @@ pub struct Rule<Theme = crate::Theme> {
width: Length, width: Length,
height: Length, height: Length,
is_horizontal: bool, is_horizontal: bool,
style: fn(&Theme) -> Appearance, style: Style<Theme>,
} }
impl<Theme> Rule<Theme> { impl<Theme> Rule<Theme> {
/// Creates a horizontal [`Rule`] with the given height. /// Creates a horizontal [`Rule`] with the given height.
pub fn horizontal(height: impl Into<Pixels>) -> Self pub fn horizontal(height: impl Into<Pixels>) -> Self
where where
Theme: Style, Style<Theme>: Default,
{ {
Rule { Rule {
width: Length::Fill, width: Length::Fill,
height: Length::Fixed(height.into().0), height: Length::Fixed(height.into().0),
is_horizontal: true, is_horizontal: true,
style: Theme::style(), style: Style::default(),
} }
} }
/// Creates a vertical [`Rule`] with the given width. /// Creates a vertical [`Rule`] with the given width.
pub fn vertical(width: impl Into<Pixels>) -> Self pub fn vertical(width: impl Into<Pixels>) -> Self
where where
Theme: Style, Style<Theme>: Default,
{ {
Rule { Rule {
width: Length::Fixed(width.into().0), width: Length::Fixed(width.into().0),
height: Length::Fill, height: Length::Fill,
is_horizontal: false, is_horizontal: false,
style: Theme::style(), style: Style::default(),
} }
} }
/// Sets the style of the [`Rule`]. /// Sets the style of the [`Rule`].
pub fn style(mut self, style: fn(&Theme) -> Appearance) -> Self { pub fn style(mut self, style: fn(&Theme) -> Appearance) -> Self {
self.style = style; self.style = Style(style);
self self
} }
} }
@ -83,7 +83,7 @@ where
_viewport: &Rectangle, _viewport: &Rectangle,
) { ) {
let bounds = layout.bounds(); let bounds = layout.bounds();
let appearance = (self.style)(theme); let appearance = (self.style.0)(theme);
let bounds = if self.is_horizontal { let bounds = if self.is_horizontal {
let line_y = (bounds.y + (bounds.height / 2.0) let line_y = (bounds.y + (bounds.height / 2.0)
@ -216,15 +216,27 @@ impl FillMode {
} }
} }
/// The definiton of the default style of a [`Rule`]. /// The style of a [`Rule`].
pub trait Style { #[derive(Debug, PartialEq, Eq)]
/// Returns the default style of a [`Rule`]. pub struct Style<Theme>(fn(&Theme) -> Appearance);
fn style() -> fn(&Self) -> Appearance;
impl<Theme> Clone for Style<Theme> {
fn clone(&self) -> Self {
*self
}
} }
impl Style for Theme { impl<Theme> Copy for Style<Theme> {}
fn style() -> fn(&Self) -> Appearance {
default impl Default for Style<Theme> {
fn default() -> Self {
Style(default)
}
}
impl<Theme> From<fn(&Theme) -> Appearance> for Style<Theme> {
fn from(f: fn(&Theme) -> Appearance) -> Self {
Style(f)
} }
} }

View file

@ -37,7 +37,7 @@ pub struct Scrollable<
direction: Direction, direction: Direction,
content: Element<'a, Message, Theme, Renderer>, content: Element<'a, Message, Theme, Renderer>,
on_scroll: Option<Box<dyn Fn(Viewport) -> Message + 'a>>, on_scroll: Option<Box<dyn Fn(Viewport) -> Message + 'a>>,
style: fn(&Theme, Status) -> Appearance, style: Style<Theme>,
} }
impl<'a, Message, Theme, Renderer> Scrollable<'a, Message, Theme, Renderer> impl<'a, Message, Theme, Renderer> Scrollable<'a, Message, Theme, Renderer>
@ -49,7 +49,7 @@ where
content: impl Into<Element<'a, Message, Theme, Renderer>>, content: impl Into<Element<'a, Message, Theme, Renderer>>,
) -> Self ) -> Self
where where
Theme: Style, Style<Theme>: Default,
{ {
Self::with_direction(content, Direction::default()) Self::with_direction(content, Direction::default())
} }
@ -60,8 +60,17 @@ where
direction: Direction, direction: Direction,
) -> Self ) -> Self
where where
Theme: Style, Style<Theme>: Default,
{ {
Self::with_direction_and_style(content, direction, Style::default().0)
}
/// Creates a new [`Scrollable`] with the given [`Direction`] and style.
pub fn with_direction_and_style(
content: impl Into<Element<'a, Message, Theme, Renderer>>,
direction: Direction,
style: fn(&Theme, Status) -> Appearance,
) -> Self {
let content = content.into(); let content = content.into();
debug_assert!( debug_assert!(
@ -83,7 +92,7 @@ where
direction, direction,
content, content,
on_scroll: None, on_scroll: None,
style: Theme::style(), style: style.into(),
} }
} }
@ -115,7 +124,7 @@ where
/// Sets the style of the [`Scrollable`] . /// Sets the style of the [`Scrollable`] .
pub fn style(mut self, style: fn(&Theme, Status) -> Appearance) -> Self { pub fn style(mut self, style: fn(&Theme, Status) -> Appearance) -> Self {
self.style = style; self.style = style.into();
self self
} }
} }
@ -399,7 +408,7 @@ where
Status::Active Status::Active
}; };
let appearance = (self.style)(theme, status); let appearance = (self.style.0)(theme, status);
container::draw_background( container::draw_background(
renderer, renderer,
@ -1653,15 +1662,27 @@ pub struct Scroller {
pub border: Border, pub border: Border,
} }
/// The definition of the default style of a [`Scrollable`]. /// The style of a [`Scrollable`].
pub trait Style { #[derive(Debug, PartialEq, Eq)]
/// Returns the default style of a [`Scrollable`]. pub struct Style<Theme>(fn(&Theme, Status) -> Appearance);
fn style() -> fn(&Self, Status) -> Appearance;
impl<Theme> Clone for Style<Theme> {
fn clone(&self) -> Self {
*self
}
} }
impl Style for Theme { impl<Theme> Copy for Style<Theme> {}
fn style() -> fn(&Self, Status) -> Appearance {
default impl Default for Style<Theme> {
fn default() -> Self {
Style(default)
}
}
impl<Theme> From<fn(&Theme, Status) -> Appearance> for Style<Theme> {
fn from(f: fn(&Theme, Status) -> Appearance) -> Self {
Style(f)
} }
} }

View file

@ -53,7 +53,7 @@ pub struct Slider<'a, T, Message, Theme = crate::Theme> {
on_release: Option<Message>, on_release: Option<Message>,
width: Length, width: Length,
height: f32, height: f32,
style: fn(&Theme, Status) -> Appearance, style: Style<Theme>,
} }
impl<'a, T, Message, Theme> Slider<'a, T, Message, Theme> impl<'a, T, Message, Theme> Slider<'a, T, Message, Theme>
@ -74,7 +74,7 @@ where
/// `Message`. /// `Message`.
pub fn new<F>(range: RangeInclusive<T>, value: T, on_change: F) -> Self pub fn new<F>(range: RangeInclusive<T>, value: T, on_change: F) -> Self
where where
Theme: Style, Style<Theme>: Default,
F: 'a + Fn(T) -> Message, F: 'a + Fn(T) -> Message,
{ {
let value = if value >= *range.start() { let value = if value >= *range.start() {
@ -99,7 +99,7 @@ where
on_release: None, on_release: None,
width: Length::Fill, width: Length::Fill,
height: Self::DEFAULT_HEIGHT, height: Self::DEFAULT_HEIGHT,
style: Theme::style(), style: Style::default(),
} }
} }
@ -136,7 +136,7 @@ where
/// Sets the style of the [`Slider`]. /// Sets the style of the [`Slider`].
pub fn style(mut self, style: fn(&Theme, Status) -> Appearance) -> Self { pub fn style(mut self, style: fn(&Theme, Status) -> Appearance) -> Self {
self.style = style; self.style = style.into();
self self
} }
@ -350,7 +350,7 @@ where
let bounds = layout.bounds(); let bounds = layout.bounds();
let is_mouse_over = cursor.is_over(bounds); let is_mouse_over = cursor.is_over(bounds);
let style = (self.style)( let style = (self.style.0)(
theme, theme,
if state.is_dragging { if state.is_dragging {
Status::Dragged Status::Dragged
@ -550,15 +550,27 @@ pub enum HandleShape {
}, },
} }
/// The definiton of the default style of a [`TextInput`]. /// The style of a [`Slider`].
pub trait Style { #[derive(Debug, PartialEq, Eq)]
/// Returns the default style of a [`TextInput`]. pub struct Style<Theme>(pub(crate) fn(&Theme, Status) -> Appearance);
fn style() -> fn(&Self, Status) -> Appearance;
impl<Theme> Clone for Style<Theme> {
fn clone(&self) -> Self {
*self
}
} }
impl Style for Theme { impl<Theme> Copy for Style<Theme> {}
fn style() -> fn(&Self, Status) -> Appearance {
default impl Default for Style<Theme> {
fn default() -> Self {
Style(default)
}
}
impl<Theme> From<fn(&Theme, Status) -> Appearance> for Style<Theme> {
fn from(f: fn(&Theme, Status) -> Appearance) -> Self {
Style(f)
} }
} }

View file

@ -81,7 +81,7 @@ impl<Theme> Svg<Theme> {
/// Sets the style variant of this [`Svg`]. /// Sets the style variant of this [`Svg`].
#[must_use] #[must_use]
pub fn style(mut self, style: fn(&Theme, Status) -> Appearance) -> Self { pub fn style(mut self, style: fn(&Theme, Status) -> Appearance) -> Self {
self.style = style.into(); self.style = Style(style);
self self
} }
} }

View file

@ -42,7 +42,7 @@ pub struct TextEditor<
width: Length, width: Length,
height: Length, height: Length,
padding: Padding, padding: Padding,
style: fn(&Theme, Status) -> Appearance, style: Style<Theme>,
on_edit: Option<Box<dyn Fn(Action) -> Message + 'a>>, on_edit: Option<Box<dyn Fn(Action) -> Message + 'a>>,
highlighter_settings: Highlighter::Settings, highlighter_settings: Highlighter::Settings,
highlighter_format: fn( highlighter_format: fn(
@ -59,7 +59,7 @@ where
/// Creates new [`TextEditor`] with the given [`Content`]. /// Creates new [`TextEditor`] with the given [`Content`].
pub fn new(content: &'a Content<Renderer>) -> Self pub fn new(content: &'a Content<Renderer>) -> Self
where where
Theme: Style, Style<Theme>: Default,
{ {
Self { Self {
content, content,
@ -69,7 +69,7 @@ where
width: Length::Fill, width: Length::Fill,
height: Length::Shrink, height: Length::Shrink,
padding: Padding::new(5.0), padding: Padding::new(5.0),
style: Theme::style(), style: Style::default(),
on_edit: None, on_edit: None,
highlighter_settings: (), highlighter_settings: (),
highlighter_format: |_highlight, _theme| { highlighter_format: |_highlight, _theme| {
@ -144,7 +144,7 @@ where
/// Sets the style of the [`TextEditor`]. /// Sets the style of the [`TextEditor`].
pub fn style(mut self, style: fn(&Theme, Status) -> Appearance) -> Self { pub fn style(mut self, style: fn(&Theme, Status) -> Appearance) -> Self {
self.style = style; self.style = style.into();
self self
} }
} }
@ -506,7 +506,7 @@ where
Status::Active Status::Active
}; };
let appearance = (self.style)(theme, status); let appearance = (self.style.0)(theme, status);
renderer.fill_quad( renderer.fill_quad(
renderer::Quad { renderer::Quad {
@ -809,15 +809,27 @@ pub struct Appearance {
pub selection: Color, pub selection: Color,
} }
/// The definiton of the default style of a [`TextInput`]. /// The style of a [`TextEditor`].
pub trait Style { #[derive(Debug, PartialEq, Eq)]
/// Returns the default style of a [`TextInput`]. pub struct Style<Theme>(fn(&Theme, Status) -> Appearance);
fn style() -> fn(&Self, Status) -> Appearance;
impl<Theme> Clone for Style<Theme> {
fn clone(&self) -> Self {
*self
}
} }
impl Style for Theme { impl<Theme> Copy for Style<Theme> {}
fn style() -> fn(&Self, Status) -> Appearance {
default impl Default for Style<Theme> {
fn default() -> Self {
Style(default)
}
}
impl<Theme> From<fn(&Theme, Status) -> Appearance> for Style<Theme> {
fn from(f: fn(&Theme, Status) -> Appearance) -> Self {
Style(f)
} }
} }

View file

@ -77,7 +77,7 @@ pub struct TextInput<
on_paste: Option<Box<dyn Fn(String) -> Message + 'a>>, on_paste: Option<Box<dyn Fn(String) -> Message + 'a>>,
on_submit: Option<Message>, on_submit: Option<Message>,
icon: Option<Icon<Renderer::Font>>, icon: Option<Icon<Renderer::Font>>,
style: fn(&Theme, Status) -> Appearance, style: Style<Theme>,
} }
/// The default [`Padding`] of a [`TextInput`]. /// The default [`Padding`] of a [`TextInput`].
@ -88,15 +88,22 @@ where
Message: Clone, Message: Clone,
Renderer: text::Renderer, Renderer: text::Renderer,
{ {
/// Creates a new [`TextInput`]. /// Creates a new [`TextInput`] with the given placeholder and
/// /// its current value.
/// It expects:
/// - a placeholder,
/// - the current value
pub fn new(placeholder: &str, value: &str) -> Self pub fn new(placeholder: &str, value: &str) -> Self
where where
Theme: Style, Style<Theme>: Default,
{ {
Self::with_style(placeholder, value, Style::default().0)
}
/// Creates a new [`TextInput`] with the given placeholder,
/// its current value, and its style.
pub fn with_style(
placeholder: &str,
value: &str,
style: fn(&Theme, Status) -> Appearance,
) -> Self {
TextInput { TextInput {
id: None, id: None,
placeholder: String::from(placeholder), placeholder: String::from(placeholder),
@ -111,7 +118,7 @@ where
on_paste: None, on_paste: None,
on_submit: None, on_submit: None,
icon: None, icon: None,
style: Theme::style(), style: style.into(),
} }
} }
@ -199,7 +206,7 @@ where
/// Sets the style of the [`TextInput`]. /// Sets the style of the [`TextInput`].
pub fn style(mut self, style: fn(&Theme, Status) -> Appearance) -> Self { pub fn style(mut self, style: fn(&Theme, Status) -> Appearance) -> Self {
self.style = style; self.style = style.into();
self self
} }
@ -337,7 +344,7 @@ where
Status::Active Status::Active
}; };
let appearance = (self.style)(theme, status); let appearance = (self.style.0)(theme, status);
renderer.fill_quad( renderer.fill_quad(
renderer::Quad { renderer::Quad {
@ -1406,15 +1413,27 @@ pub struct Appearance {
pub selection: Color, pub selection: Color,
} }
/// The definiton of the default style of a [`TextInput`]. /// The style of a [`TextInput`].
pub trait Style { #[derive(Debug, PartialEq, Eq)]
/// Returns the default style of a [`TextInput`]. pub struct Style<Theme>(fn(&Theme, Status) -> Appearance);
fn style() -> fn(&Self, Status) -> Appearance;
impl<Theme> Clone for Style<Theme> {
fn clone(&self) -> Self {
*self
}
} }
impl Style for Theme { impl<Theme> Copy for Style<Theme> {}
fn style() -> fn(&Self, Status) -> Appearance {
default impl Default for Style<Theme> {
fn default() -> Self {
Style(default)
}
}
impl<Theme> From<fn(&Theme, Status) -> Appearance> for Style<Theme> {
fn from(f: fn(&Theme, Status) -> Appearance) -> Self {
Style(f)
} }
} }

View file

@ -50,7 +50,7 @@ pub struct Toggler<
text_shaping: text::Shaping, text_shaping: text::Shaping,
spacing: f32, spacing: f32,
font: Option<Renderer::Font>, font: Option<Renderer::Font>,
style: fn(&Theme, Status) -> Appearance, style: Style<Theme>,
} }
impl<'a, Message, Theme, Renderer> Toggler<'a, Message, Theme, Renderer> impl<'a, Message, Theme, Renderer> Toggler<'a, Message, Theme, Renderer>
@ -74,7 +74,7 @@ where
f: F, f: F,
) -> Self ) -> Self
where where
Theme: Style, Style<Theme>: Default,
F: 'a + Fn(bool) -> Message, F: 'a + Fn(bool) -> Message,
{ {
Toggler { Toggler {
@ -89,7 +89,7 @@ where
text_shaping: text::Shaping::Basic, text_shaping: text::Shaping::Basic,
spacing: Self::DEFAULT_SIZE / 2.0, spacing: Self::DEFAULT_SIZE / 2.0,
font: None, font: None,
style: Theme::style(), style: Style::default(),
} }
} }
@ -301,7 +301,7 @@ where
} }
}; };
let appearance = (self.style)(theme, status); let appearance = (self.style.0)(theme, status);
let border_radius = bounds.height / BORDER_RADIUS_RATIO; let border_radius = bounds.height / BORDER_RADIUS_RATIO;
let space = SPACE_RATIO * bounds.height; let space = SPACE_RATIO * bounds.height;
@ -399,15 +399,27 @@ pub struct Appearance {
pub foreground_border_color: Color, pub foreground_border_color: Color,
} }
/// The definiton of the default style of a [`Toggler`]. /// The style of a [`Toggler`].
pub trait Style { #[derive(Debug, PartialEq, Eq)]
/// Returns the default style of a [`Toggler`]. pub struct Style<Theme>(fn(&Theme, Status) -> Appearance);
fn style() -> fn(&Self, Status) -> Appearance;
impl<Theme> Clone for Style<Theme> {
fn clone(&self) -> Self {
*self
}
} }
impl Style for Theme { impl<Theme> Copy for Style<Theme> {}
fn style() -> fn(&Self, Status) -> Appearance {
default impl Default for Style<Theme> {
fn default() -> Self {
Style(default)
}
}
impl<Theme> From<fn(&Theme, Status) -> Appearance> for Style<Theme> {
fn from(f: fn(&Theme, Status) -> Appearance) -> Self {
Style(f)
} }
} }

View file

@ -28,7 +28,7 @@ pub struct Tooltip<
gap: f32, gap: f32,
padding: f32, padding: f32,
snap_within_viewport: bool, snap_within_viewport: bool,
style: fn(&Theme, container::Status) -> container::Appearance, style: container::Style<Theme>,
} }
impl<'a, Message, Theme, Renderer> Tooltip<'a, Message, Theme, Renderer> impl<'a, Message, Theme, Renderer> Tooltip<'a, Message, Theme, Renderer>
@ -47,7 +47,7 @@ where
position: Position, position: Position,
) -> Self ) -> Self
where where
Theme: container::Style, container::Style<Theme>: Default,
{ {
Tooltip { Tooltip {
content: content.into(), content: content.into(),
@ -56,7 +56,7 @@ where
gap: 0.0, gap: 0.0,
padding: Self::DEFAULT_PADDING, padding: Self::DEFAULT_PADDING,
snap_within_viewport: true, snap_within_viewport: true,
style: Theme::style(), style: container::Style::default(),
} }
} }
@ -83,7 +83,7 @@ where
mut self, mut self,
style: fn(&Theme, container::Status) -> container::Appearance, style: fn(&Theme, container::Status) -> container::Appearance,
) -> Self { ) -> Self {
self.style = style; self.style = style.into();
self self
} }
} }
@ -309,7 +309,7 @@ where
positioning: Position, positioning: Position,
gap: f32, gap: f32,
padding: f32, padding: f32,
style: fn(&Theme, container::Status) -> container::Appearance, style: container::Style<Theme>,
} }
impl<'a, 'b, Message, Theme, Renderer> impl<'a, 'b, Message, Theme, Renderer>
@ -424,7 +424,7 @@ where
layout: Layout<'_>, layout: Layout<'_>,
cursor_position: mouse::Cursor, cursor_position: mouse::Cursor,
) { ) {
let style = (self.style)(theme, container::Status::Idle); let style = self.style.resolve(theme, container::Status::Idle);
container::draw_background(renderer, &style, layout.bounds()); container::draw_background(renderer, &style, layout.bounds());

View file

@ -54,7 +54,7 @@ pub struct VerticalSlider<'a, T, Message, Theme = crate::Theme> {
on_release: Option<Message>, on_release: Option<Message>,
width: f32, width: f32,
height: Length, height: Length,
style: fn(&Theme, Status) -> Appearance, style: Style<Theme>,
} }
impl<'a, T, Message, Theme> VerticalSlider<'a, T, Message, Theme> impl<'a, T, Message, Theme> VerticalSlider<'a, T, Message, Theme>
@ -75,7 +75,7 @@ where
/// `Message`. /// `Message`.
pub fn new<F>(range: RangeInclusive<T>, value: T, on_change: F) -> Self pub fn new<F>(range: RangeInclusive<T>, value: T, on_change: F) -> Self
where where
Theme: Style, Style<Theme>: Default,
F: 'a + Fn(T) -> Message, F: 'a + Fn(T) -> Message,
{ {
let value = if value >= *range.start() { let value = if value >= *range.start() {
@ -100,7 +100,7 @@ where
on_release: None, on_release: None,
width: Self::DEFAULT_WIDTH, width: Self::DEFAULT_WIDTH,
height: Length::Fill, height: Length::Fill,
style: Theme::style(), style: Style::default(),
} }
} }
@ -136,10 +136,7 @@ where
} }
/// Sets the style of the [`VerticalSlider`]. /// Sets the style of the [`VerticalSlider`].
pub fn style( pub fn style(mut self, style: fn(&Theme, Status) -> Appearance) -> Self {
mut self,
style: impl Into<fn(&Theme, Status) -> Appearance>,
) -> Self {
self.style = style.into(); self.style = style.into();
self self
} }
@ -357,7 +354,7 @@ where
let bounds = layout.bounds(); let bounds = layout.bounds();
let is_mouse_over = cursor.is_over(bounds); let is_mouse_over = cursor.is_over(bounds);
let style = (self.style)( let style = (self.style.0)(
theme, theme,
if state.is_dragging { if state.is_dragging {
Status::Dragged Status::Dragged