summaryrefslogtreecommitdiffstats
path: root/core/src/widget
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2024-05-23 13:29:45 +0200
committerLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2024-05-23 13:29:45 +0200
commitd8ba6b0673a33724a177f3a1ba59705527280142 (patch)
tree89482c8d1e3a03e00b3a8151abbb81e30ae5898c /core/src/widget
parent72ed8bcc8def9956e25f3720a3095fc96bb2eef0 (diff)
parent468794d918eb06c1dbebb33c32b10017ad335f05 (diff)
downloadiced-d8ba6b0673a33724a177f3a1ba59705527280142.tar.gz
iced-d8ba6b0673a33724a177f3a1ba59705527280142.tar.bz2
iced-d8ba6b0673a33724a177f3a1ba59705527280142.zip
Merge branch 'master' into feat/text-macro
Diffstat (limited to 'core/src/widget')
-rw-r--r--core/src/widget/text.rs199
1 files changed, 151 insertions, 48 deletions
diff --git a/core/src/widget/text.rs b/core/src/widget/text.rs
index 66e2d066..f1f0b345 100644
--- a/core/src/widget/text.rs
+++ b/core/src/widget/text.rs
@@ -18,9 +18,10 @@ pub use text::{LineHeight, Shaping};
#[allow(missing_debug_implementations)]
pub struct Text<'a, Theme, Renderer>
where
+ Theme: Catalog,
Renderer: text::Renderer,
{
- content: Cow<'a, str>,
+ fragment: Fragment<'a>,
size: Option<Pixels>,
line_height: LineHeight,
width: Length,
@@ -29,20 +30,18 @@ where
vertical_alignment: alignment::Vertical,
font: Option<Renderer::Font>,
shaping: Shaping,
- style: Style<'a, Theme>,
+ class: Theme::Class<'a>,
}
impl<'a, Theme, Renderer> Text<'a, Theme, Renderer>
where
+ Theme: Catalog,
Renderer: text::Renderer,
{
/// Create a new fragment of [`Text`] with the given contents.
- pub fn new(content: impl Into<Cow<'a, str>>) -> Self
- where
- Theme: DefaultStyle + 'a,
- {
+ pub fn new(fragment: impl IntoFragment<'a>) -> Self {
Text {
- content: content.into(),
+ fragment: fragment.into_fragment(),
size: None,
line_height: LineHeight::default(),
font: None,
@@ -51,7 +50,7 @@ where
horizontal_alignment: alignment::Horizontal::Left,
vertical_alignment: alignment::Vertical::Top,
shaping: Shaping::Basic,
- style: Box::new(Theme::default_style),
+ class: Theme::default(),
}
}
@@ -75,25 +74,6 @@ where
self
}
- /// Sets the style of the [`Text`].
- pub fn style(mut self, style: impl Fn(&Theme) -> Appearance + 'a) -> Self {
- self.style = Box::new(style);
- self
- }
-
- /// Sets the [`Color`] of the [`Text`].
- pub fn color(self, color: impl Into<Color>) -> Self {
- self.color_maybe(Some(color))
- }
-
- /// Sets the [`Color`] of the [`Text`], if `Some`.
- pub fn color_maybe(mut self, color: Option<impl Into<Color>>) -> Self {
- let color = color.map(Into::into);
-
- self.style = Box::new(move |_theme| Appearance { color });
- self
- }
-
/// Sets the width of the [`Text`] boundaries.
pub fn width(mut self, width: impl Into<Length>) -> Self {
self.width = width.into();
@@ -129,6 +109,42 @@ where
self.shaping = shaping;
self
}
+
+ /// Sets the style of the [`Text`].
+ #[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 [`Color`] of the [`Text`].
+ pub fn color(self, color: impl Into<Color>) -> Self
+ where
+ Theme::Class<'a>: From<StyleFn<'a, Theme>>,
+ {
+ self.color_maybe(Some(color))
+ }
+
+ /// Sets the [`Color`] of the [`Text`], if `Some`.
+ pub fn color_maybe(self, color: Option<impl Into<Color>>) -> Self
+ where
+ Theme::Class<'a>: From<StyleFn<'a, Theme>>,
+ {
+ let color = color.map(Into::into);
+
+ self.style(move |_theme| Style { color })
+ }
+
+ /// Sets the style class of the [`Text`].
+ #[cfg(feature = "advanced")]
+ #[must_use]
+ pub fn class(mut self, class: impl Into<Theme::Class<'a>>) -> Self {
+ self.class = class.into();
+ self
+ }
}
/// The internal state of a [`Text`] widget.
@@ -138,6 +154,7 @@ pub struct State<P: Paragraph>(P);
impl<'a, Message, Theme, Renderer> Widget<Message, Theme, Renderer>
for Text<'a, Theme, Renderer>
where
+ Theme: Catalog,
Renderer: text::Renderer,
{
fn tag(&self) -> tree::Tag {
@@ -167,7 +184,7 @@ where
limits,
self.width,
self.height,
- &self.content,
+ &self.fragment,
self.line_height,
self.size,
self.font,
@@ -182,15 +199,15 @@ where
tree: &Tree,
renderer: &mut Renderer,
theme: &Theme,
- style: &renderer::Style,
+ defaults: &renderer::Style,
layout: Layout<'_>,
_cursor_position: mouse::Cursor,
viewport: &Rectangle,
) {
let state = tree.state.downcast_ref::<State<Renderer::Paragraph>>();
- let appearance = (self.style)(theme);
+ let style = theme.style(&self.class);
- draw(renderer, style, layout, state, appearance, viewport);
+ draw(renderer, defaults, layout, state, style, viewport);
}
}
@@ -250,7 +267,7 @@ pub fn draw<Renderer>(
style: &renderer::Style,
layout: Layout<'_>,
state: &State<Renderer::Paragraph>,
- appearance: Appearance,
+ appearance: Style,
viewport: &Rectangle,
) where
Renderer: text::Renderer,
@@ -281,7 +298,7 @@ pub fn draw<Renderer>(
impl<'a, Message, Theme, Renderer> From<Text<'a, Theme, Renderer>>
for Element<'a, Message, Theme, Renderer>
where
- Theme: 'a,
+ Theme: Catalog + 'a,
Renderer: text::Renderer + 'a,
{
fn from(
@@ -293,7 +310,7 @@ where
impl<'a, Theme, Renderer> From<&'a str> for Text<'a, Theme, Renderer>
where
- Theme: DefaultStyle + 'a,
+ Theme: Catalog + 'a,
Renderer: text::Renderer,
{
fn from(content: &'a str) -> Self {
@@ -304,7 +321,7 @@ where
impl<'a, Message, Theme, Renderer> From<&'a str>
for Element<'a, Message, Theme, Renderer>
where
- Theme: DefaultStyle + 'a,
+ Theme: Catalog + 'a,
Renderer: text::Renderer + 'a,
{
fn from(content: &'a str) -> Self {
@@ -314,30 +331,116 @@ where
/// The appearance of some text.
#[derive(Debug, Clone, Copy, Default)]
-pub struct Appearance {
+pub struct Style {
/// The [`Color`] of the text.
///
/// The default, `None`, means using the inherited color.
pub color: Option<Color>,
}
-/// The style of some [`Text`].
-pub type Style<'a, Theme> = Box<dyn Fn(&Theme) -> Appearance + 'a>;
+/// The theme catalog of a [`Text`].
+pub trait Catalog: Sized {
+ /// The item class of this [`Catalog`].
+ type Class<'a>;
+
+ /// The default class produced by this [`Catalog`].
+ fn default<'a>() -> Self::Class<'a>;
-/// The default style of some [`Text`].
-pub trait DefaultStyle {
- /// Returns the default style of some [`Text`].
- fn default_style(&self) -> Appearance;
+ /// The [`Style`] of a class with the given status.
+ fn style(&self, item: &Self::Class<'_>) -> Style;
}
-impl DefaultStyle for Theme {
- fn default_style(&self) -> Appearance {
- Appearance::default()
+/// A styling function for a [`Text`].
+///
+/// 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(|_theme| Style::default())
+ }
+
+ fn style(&self, class: &Self::Class<'_>) -> Style {
+ class(self)
+ }
+}
+
+/// A fragment of [`Text`].
+///
+/// This is just an alias to a string that may be either
+/// borrowed or owned.
+pub type Fragment<'a> = Cow<'a, str>;
+
+/// A trait for converting a value to some text [`Fragment`].
+pub trait IntoFragment<'a> {
+ /// Converts the value to some text [`Fragment`].
+ fn into_fragment(self) -> Fragment<'a>;
+}
+
+impl<'a> IntoFragment<'a> for Fragment<'a> {
+ fn into_fragment(self) -> Fragment<'a> {
+ self
}
}
-impl DefaultStyle for Color {
- fn default_style(&self) -> Appearance {
- Appearance { color: Some(*self) }
+impl<'a, 'b> IntoFragment<'a> for &'a Fragment<'b> {
+ fn into_fragment(self) -> Fragment<'a> {
+ Fragment::Borrowed(self)
}
}
+
+impl<'a> IntoFragment<'a> for &'a str {
+ fn into_fragment(self) -> Fragment<'a> {
+ Fragment::Borrowed(self)
+ }
+}
+
+impl<'a> IntoFragment<'a> for &'a String {
+ fn into_fragment(self) -> Fragment<'a> {
+ Fragment::Borrowed(self.as_str())
+ }
+}
+
+impl<'a> IntoFragment<'a> for String {
+ fn into_fragment(self) -> Fragment<'a> {
+ Fragment::Owned(self)
+ }
+}
+
+macro_rules! into_fragment {
+ ($type:ty) => {
+ impl<'a> IntoFragment<'a> for $type {
+ fn into_fragment(self) -> Fragment<'a> {
+ Fragment::Owned(self.to_string())
+ }
+ }
+
+ impl<'a> IntoFragment<'a> for &$type {
+ fn into_fragment(self) -> Fragment<'a> {
+ Fragment::Owned(self.to_string())
+ }
+ }
+ };
+}
+
+into_fragment!(char);
+into_fragment!(bool);
+
+into_fragment!(u8);
+into_fragment!(u16);
+into_fragment!(u32);
+into_fragment!(u64);
+into_fragment!(u128);
+into_fragment!(usize);
+
+into_fragment!(i8);
+into_fragment!(i16);
+into_fragment!(i32);
+into_fragment!(i64);
+into_fragment!(i128);
+into_fragment!(isize);
+
+into_fragment!(f32);
+into_fragment!(f64);