From f0ae9a0c38c2532220a7460916604914db94c078 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 24 Mar 2024 05:03:09 +0100 Subject: Use `Catalog` approach for all widgets --- widget/src/text_editor.rs | 94 +++++++++++++++++++++++++++-------------------- 1 file changed, 55 insertions(+), 39 deletions(-) (limited to 'widget/src/text_editor.rs') diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index 5b8f6a1b..a00df3c7 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -32,6 +32,7 @@ pub struct TextEditor< Renderer = crate::Renderer, > where Highlighter: text::Highlighter, + Theme: Catalog, Renderer: text::Renderer, { content: &'a Content, @@ -41,7 +42,7 @@ pub struct TextEditor< width: Length, height: Length, padding: Padding, - style: Style<'a, Theme>, + class: Theme::Class<'a>, on_edit: Option Message + 'a>>, highlighter_settings: Highlighter::Settings, highlighter_format: fn( @@ -53,13 +54,11 @@ pub struct TextEditor< impl<'a, Message, Theme, Renderer> TextEditor<'a, highlighter::PlainText, Message, Theme, Renderer> where + Theme: Catalog, Renderer: text::Renderer, { /// Creates new [`TextEditor`] with the given [`Content`]. - pub fn new(content: &'a Content) -> Self - where - Theme: DefaultStyle + 'a, - { + pub fn new(content: &'a Content) -> Self { Self { content, font: None, @@ -68,7 +67,7 @@ where width: Length::Fill, height: Length::Shrink, padding: Padding::new(5.0), - style: Box::new(Theme::default_style), + class: Theme::default(), on_edit: None, highlighter_settings: (), highlighter_format: |_highlight, _theme| { @@ -82,6 +81,7 @@ impl<'a, Highlighter, Message, Theme, Renderer> TextEditor<'a, Highlighter, Message, Theme, Renderer> where Highlighter: text::Highlighter, + Theme: Catalog, Renderer: text::Renderer, { /// Sets the height of the [`TextEditor`]. @@ -134,7 +134,7 @@ where width: self.width, height: self.height, padding: self.padding, - style: self.style, + class: self.class, on_edit: self.on_edit, highlighter_settings: settings, highlighter_format: to_format, @@ -142,11 +142,20 @@ where } /// Sets the style of the [`TextEditor`]. - pub fn style( - mut self, - style: impl Fn(&Theme, Status) -> Appearance + 'a, - ) -> Self { - self.style = Box::new(style); + #[must_use] + pub fn style(mut self, style: impl Fn(&Theme, Status) -> Style + 'a) -> Self + where + Theme::Class<'a>: From>, + { + self.class = (Box::new(style) as StyleFn<'a, Theme>).into(); + self + } + + /// Sets the style class of the [`TextEditor`]. + #[cfg(feature = "advanced")] + #[must_use] + pub fn class(mut self, class: impl Into>) -> Self { + self.class = class.into(); self } } @@ -309,6 +318,7 @@ impl<'a, Highlighter, Message, Theme, Renderer> Widget for TextEditor<'a, Highlighter, Message, Theme, Renderer> where Highlighter: text::Highlighter, + Theme: Catalog, Renderer: text::Renderer, { fn tag(&self) -> widget::tree::Tag { @@ -479,7 +489,7 @@ where tree: &widget::Tree, renderer: &mut Renderer, theme: &Theme, - style: &renderer::Style, + defaults: &renderer::Style, layout: Layout<'_>, cursor: mouse::Cursor, viewport: &Rectangle, @@ -508,22 +518,22 @@ where Status::Active }; - let appearance = (self.style)(theme, status); + let style = theme.style(&self.class, status); renderer.fill_quad( renderer::Quad { bounds, - border: appearance.border, + border: style.border, ..renderer::Quad::default() }, - appearance.background, + style.background, ); renderer.fill_editor( &internal.editor, bounds.position() + Vector::new(self.padding.left, self.padding.top), - style.text_color, + defaults.text_color, *viewport, ); @@ -555,7 +565,7 @@ where }, ..renderer::Quad::default() }, - appearance.value, + style.value, ); } } @@ -568,7 +578,7 @@ where bounds: range, ..renderer::Quad::default() }, - appearance.selection, + style.selection, ); } } @@ -604,7 +614,7 @@ impl<'a, Highlighter, Message, Theme, Renderer> where Highlighter: text::Highlighter, Message: 'a, - Theme: 'a, + Theme: Catalog + 'a, Renderer: text::Renderer, { fn from( @@ -796,7 +806,7 @@ pub enum Status { /// The appearance of a text input. #[derive(Debug, Clone, Copy)] -pub struct Appearance { +pub struct Style { /// The [`Background`] of the text input. pub background: Background, /// The [`Border`] of the text input. @@ -811,32 +821,38 @@ pub struct Appearance { pub selection: Color, } -/// The style of a [`TextEditor`]. -pub type Style<'a, Theme> = Box Appearance + 'a>; +/// The theme catalog of a [`TextEditor`]. +pub trait Catalog { + /// The item class of the [`Catalog`]. + type Class<'a>; -/// The default style of a [`TextEditor`]. -pub trait DefaultStyle { - /// Returns the default style of a [`TextEditor`]. - fn default_style(&self, status: Status) -> 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<'_>, status: Status) -> Style; } -impl DefaultStyle for Theme { - fn default_style(&self, status: Status) -> Appearance { - default(self, status) +/// A styling function for a [`TextEditor`]. +pub type StyleFn<'a, Theme> = Box 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, _status: Status) -> Appearance { - *self + fn style(&self, class: &Self::Class<'_>, status: Status) -> Style { + class(self, status) } } /// The default style of a [`TextEditor`]. -pub fn default(theme: &Theme, status: Status) -> Appearance { +pub fn default(theme: &Theme, status: Status) -> Style { let palette = theme.extended_palette(); - let active = Appearance { + let active = Style { background: Background::Color(palette.background.base.color), border: Border { radius: 2.0.into(), @@ -851,21 +867,21 @@ pub fn default(theme: &Theme, status: Status) -> Appearance { match status { Status::Active => active, - Status::Hovered => Appearance { + Status::Hovered => Style { border: Border { color: palette.background.base.text, ..active.border }, ..active }, - Status::Focused => Appearance { + Status::Focused => Style { border: Border { color: palette.primary.strong.color, ..active.border }, ..active }, - Status::Disabled => Appearance { + Status::Disabled => Style { background: Background::Color(palette.background.weak.color), value: active.placeholder, ..active -- cgit From 385a4490fb6a344873ba5154275a33acc011d080 Mon Sep 17 00:00:00 2001 From: Bingus Date: Fri, 29 Mar 2024 16:58:11 -0700 Subject: Expose line_height & text_size fields for the text_editor widget --- widget/src/text_editor.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'widget/src/text_editor.rs') diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index a00df3c7..92cdb251 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -110,6 +110,21 @@ where self } + /// Sets the text size of the [`TextEditor`]. + pub fn size(mut self, size: impl Into) -> Self { + self.text_size = Some(size.into()); + self + } + + /// Sets the [`text::LineHeight`] of the [`TextEditor`]. + pub fn line_height( + mut self, + line_height: impl Into, + ) -> Self { + self.line_height = line_height.into(); + self + } + /// Sets the [`Padding`] of the [`TextEditor`]. pub fn padding(mut self, padding: impl Into) -> Self { self.padding = padding.into(); -- cgit From f962c6ce0671b4b0b0e683148353fc3a3d1fc3c3 Mon Sep 17 00:00:00 2001 From: mtkennerly Date: Mon, 15 Apr 2024 08:41:29 -0400 Subject: Allow checking whether a TextEditor is focused --- widget/src/text_editor.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'widget/src/text_editor.rs') diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index 92cdb251..5b0b1b79 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -319,7 +319,9 @@ where } } -struct State { +/// The state of a [`TextEditor`]. +#[derive(Debug)] +pub struct State { is_focused: bool, last_click: Option, drag_click: Option, @@ -329,6 +331,13 @@ struct State { highlighter_format_address: usize, } +impl State { + /// Returns whether the [`TextEditor`] is currently focused or not. + pub fn is_focused(&self) -> bool { + self.is_focused + } +} + impl<'a, Highlighter, Message, Theme, Renderer> Widget for TextEditor<'a, Highlighter, Message, Theme, Renderer> where -- cgit From 73088a6fc1567f8bd557fdf479e16b395032b019 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 26 Apr 2024 15:17:35 +0200 Subject: Fix out of bounds caret in `TextEditor` in some circumstances --- widget/src/text_editor.rs | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) (limited to 'widget/src/text_editor.rs') diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index 5b0b1b79..7c0b98ea 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -569,23 +569,27 @@ where if state.is_focused { match internal.editor.cursor() { Cursor::Caret(position) => { - let position = position + translation; + let cursor = + Rectangle::new( + position + translation, + Size::new( + 1.0, + self.line_height + .to_absolute(self.text_size.unwrap_or_else( + || renderer.default_size(), + )) + .into(), + ), + ); - if bounds.contains(position) { + if let Some(clipped_cursor) = bounds.intersection(&cursor) { renderer.fill_quad( renderer::Quad { bounds: Rectangle { - x: position.x.floor(), - y: position.y, - width: 1.0, - height: self - .line_height - .to_absolute( - self.text_size.unwrap_or_else( - || renderer.default_size(), - ), - ) - .into(), + x: clipped_cursor.x.floor(), + y: clipped_cursor.y, + width: clipped_cursor.width, + height: clipped_cursor.height, }, ..renderer::Quad::default() }, -- cgit