From e695f7a04c16f786154f25a486b649ddbfd62939 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 21 Jan 2024 19:02:01 +0100 Subject: Introduce `themer` widget --- widget/src/helpers.rs | 13 ++- widget/src/lib.rs | 5 +- widget/src/themer.rs | 268 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 284 insertions(+), 2 deletions(-) create mode 100644 widget/src/themer.rs diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index 28fdbbb5..444eb4c2 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -20,7 +20,7 @@ use crate::text_editor::{self, TextEditor}; use crate::text_input::{self, TextInput}; use crate::toggler::{self, Toggler}; use crate::tooltip::{self, Tooltip}; -use crate::{Column, MouseArea, Row, Space, VerticalSlider}; +use crate::{Column, MouseArea, Row, Space, Themer, VerticalSlider}; use std::borrow::Cow; use std::ops::RangeInclusive; @@ -421,3 +421,14 @@ where { MouseArea::new(widget) } + +/// A widget that applies any `Theme` to its contents. +pub fn themer<'a, Message, Theme, Renderer>( + theme: Theme, + content: impl Into>, +) -> Themer<'a, Message, Theme, Renderer> +where + Renderer: core::Renderer, +{ + Themer::new(theme, content) +} diff --git a/widget/src/lib.rs b/widget/src/lib.rs index def1bde3..cefafdbe 100644 --- a/widget/src/lib.rs +++ b/widget/src/lib.rs @@ -19,6 +19,7 @@ pub use iced_style as style; mod column; mod mouse_area; mod row; +mod themer; pub mod button; pub mod checkbox; @@ -91,6 +92,8 @@ pub use text_editor::TextEditor; #[doc(no_inline)] pub use text_input::TextInput; #[doc(no_inline)] +pub use themer::Themer; +#[doc(no_inline)] pub use toggler::Toggler; #[doc(no_inline)] pub use tooltip::Tooltip; @@ -132,5 +135,5 @@ pub mod qr_code; #[doc(no_inline)] pub use qr_code::QRCode; -pub use crate::style::theme::{self, Theme}; pub use renderer::Renderer; +pub use style::theme::{self, Theme}; diff --git a/widget/src/themer.rs b/widget/src/themer.rs new file mode 100644 index 00000000..ee96a493 --- /dev/null +++ b/widget/src/themer.rs @@ -0,0 +1,268 @@ +use crate::core::event::{self, Event}; +use crate::core::layout; +use crate::core::mouse; +use crate::core::overlay; +use crate::core::renderer; +use crate::core::widget::tree::{self, Tree}; +use crate::core::widget::Operation; +use crate::core::{ + Clipboard, Element, Layout, Length, Point, Rectangle, Shell, Size, Vector, + Widget, +}; + +/// A widget that applies any `Theme` to its contents. +/// +/// This widget can be useful to leverage multiple `Theme` +/// types in an application. +#[allow(missing_debug_implementations)] +pub struct Themer<'a, Message, Theme, Renderer> +where + Renderer: crate::core::Renderer, +{ + content: Element<'a, Message, Theme, Renderer>, + theme: Theme, +} + +impl<'a, Message, Theme, Renderer> Themer<'a, Message, Theme, Renderer> +where + Renderer: crate::core::Renderer, +{ + /// Creates an empty [`Themer`] that applies the given `Theme` + /// to the provided `content`. + pub fn new(theme: Theme, content: T) -> Self + where + T: Into>, + { + Self { + theme, + content: content.into(), + } + } +} + +impl<'a, AnyTheme, Message, Theme, Renderer> Widget + for Themer<'a, Message, Theme, Renderer> +where + Renderer: crate::core::Renderer, +{ + fn tag(&self) -> tree::Tag { + self.content.as_widget().tag() + } + + fn state(&self) -> tree::State { + self.content.as_widget().state() + } + + fn children(&self) -> Vec { + self.content.as_widget().children() + } + + fn diff(&self, tree: &mut Tree) { + self.content.as_widget().diff(tree); + } + + fn size(&self) -> Size { + self.content.as_widget().size() + } + + fn layout( + &self, + tree: &mut Tree, + renderer: &Renderer, + limits: &layout::Limits, + ) -> layout::Node { + self.content.as_widget().layout(tree, renderer, limits) + } + + fn operate( + &self, + tree: &mut Tree, + layout: Layout<'_>, + renderer: &Renderer, + operation: &mut dyn Operation, + ) { + self.content + .as_widget() + .operate(tree, layout, renderer, operation); + } + + fn on_event( + &mut self, + tree: &mut Tree, + event: Event, + layout: Layout<'_>, + cursor: mouse::Cursor, + renderer: &Renderer, + clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + viewport: &Rectangle, + ) -> event::Status { + self.content.as_widget_mut().on_event( + tree, event, layout, cursor, renderer, clipboard, shell, viewport, + ) + } + + fn mouse_interaction( + &self, + tree: &Tree, + layout: Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + renderer: &Renderer, + ) -> mouse::Interaction { + self.content + .as_widget() + .mouse_interaction(tree, layout, cursor, viewport, renderer) + } + + fn draw( + &self, + tree: &Tree, + renderer: &mut Renderer, + _theme: &AnyTheme, + renderer_style: &renderer::Style, + layout: Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + ) { + self.content.as_widget().draw( + tree, + renderer, + &self.theme, + renderer_style, + layout, + cursor, + viewport, + ); + } + + fn overlay<'b>( + &'b mut self, + tree: &'b mut Tree, + layout: Layout<'_>, + renderer: &Renderer, + ) -> Option> { + struct Overlay<'a, Message, Theme, Renderer> { + theme: &'a Theme, + content: overlay::Element<'a, Message, Theme, Renderer>, + } + + impl<'a, AnyTheme, Message, Theme, Renderer> + overlay::Overlay + for Overlay<'a, Message, Theme, Renderer> + where + Renderer: crate::core::Renderer, + { + fn layout( + &mut self, + renderer: &Renderer, + bounds: Size, + position: Point, + translation: Vector, + ) -> layout::Node { + self.content.layout( + renderer, + bounds, + translation + (position - Point::ORIGIN), + ) + } + + fn draw( + &self, + renderer: &mut Renderer, + _theme: &AnyTheme, + style: &renderer::Style, + layout: Layout<'_>, + cursor: mouse::Cursor, + ) { + self.content + .draw(renderer, self.theme, style, layout, cursor); + } + + fn on_event( + &mut self, + event: Event, + layout: Layout<'_>, + cursor: mouse::Cursor, + renderer: &Renderer, + clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + ) -> event::Status { + self.content + .on_event(event, layout, cursor, renderer, clipboard, shell) + } + + fn operate( + &mut self, + layout: Layout<'_>, + renderer: &Renderer, + operation: &mut dyn Operation, + ) { + self.content.operate(layout, renderer, operation); + } + + fn mouse_interaction( + &self, + layout: Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + renderer: &Renderer, + ) -> mouse::Interaction { + self.content + .mouse_interaction(layout, cursor, viewport, renderer) + } + + fn is_over( + &self, + layout: Layout<'_>, + renderer: &Renderer, + cursor_position: Point, + ) -> bool { + self.content.is_over(layout, renderer, cursor_position) + } + + fn overlay<'b>( + &'b mut self, + layout: Layout<'_>, + renderer: &Renderer, + ) -> Option> + { + self.content + .overlay(layout, renderer) + .map(|content| Overlay { + theme: self.theme, + content, + }) + .map(|overlay| { + overlay::Element::new(Point::ORIGIN, Box::new(overlay)) + }) + } + } + + self.content + .as_widget_mut() + .overlay(tree, layout, renderer) + .map(|content| Overlay { + theme: &self.theme, + content, + }) + .map(|overlay| { + overlay::Element::new(Point::ORIGIN, Box::new(overlay)) + }) + } +} + +impl<'a, AnyTheme, Message, Theme, Renderer> + From> + for Element<'a, Message, AnyTheme, Renderer> +where + Message: 'a, + Theme: 'a, + Renderer: 'a + crate::core::Renderer, +{ + fn from( + themer: Themer<'a, Message, Theme, Renderer>, + ) -> Element<'a, Message, AnyTheme, Renderer> { + Element::new(themer) + } +} -- cgit