diff options
Diffstat (limited to 'native/src/widget')
| -rw-r--r-- | native/src/widget/action.rs | 12 | ||||
| -rw-r--r-- | native/src/widget/checkbox.rs | 38 | ||||
| -rw-r--r-- | native/src/widget/container.rs | 53 | ||||
| -rw-r--r-- | native/src/widget/image.rs | 68 | ||||
| -rw-r--r-- | native/src/widget/operation.rs | 2 | ||||
| -rw-r--r-- | native/src/widget/pane_grid.rs | 13 | ||||
| -rw-r--r-- | native/src/widget/pick_list.rs | 131 | ||||
| -rw-r--r-- | native/src/widget/scrollable.rs | 19 | ||||
| -rw-r--r-- | native/src/widget/text_input.rs | 2 | 
9 files changed, 219 insertions, 119 deletions
| diff --git a/native/src/widget/action.rs b/native/src/widget/action.rs index 1e21ff38..3f1b6b6c 100644 --- a/native/src/widget/action.rs +++ b/native/src/widget/action.rs @@ -1,4 +1,6 @@ -use crate::widget::operation::{self, Focusable, Operation, Scrollable}; +use crate::widget::operation::{ +    self, Focusable, Operation, Scrollable, TextInput, +};  use crate::widget::Id;  use iced_futures::MaybeSend; @@ -86,6 +88,14 @@ where                  self.operation.focusable(state, id);              } +            fn text_input( +                &mut self, +                state: &mut dyn TextInput, +                id: Option<&Id>, +            ) { +                self.operation.text_input(state, id); +            } +              fn custom(&mut self, state: &mut dyn Any, id: Option<&Id>) {                  self.operation.custom(state, id);              } diff --git a/native/src/widget/checkbox.rs b/native/src/widget/checkbox.rs index b46433c2..f6298a8c 100644 --- a/native/src/widget/checkbox.rs +++ b/native/src/widget/checkbox.rs @@ -14,6 +14,17 @@ use crate::{  pub use iced_style::checkbox::{Appearance, StyleSheet}; +/// The icon in a [`Checkbox`]. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Icon<Font> { +    /// Font that will be used to display the `code_point`, +    pub font: Font, +    /// The unicode code point that will be used as the icon. +    pub code_point: char, +    /// Font size of the content. +    pub size: Option<u16>, +} +  /// A box that can be checked.  ///  /// # Example @@ -45,6 +56,7 @@ where      spacing: u16,      text_size: Option<u16>,      font: Renderer::Font, +    icon: Icon<Renderer::Font>,      style: <Renderer::Theme as StyleSheet>::Style,  } @@ -80,6 +92,11 @@ where              spacing: Self::DEFAULT_SPACING,              text_size: None,              font: Renderer::Font::default(), +            icon: Icon { +                font: Renderer::ICON_FONT, +                code_point: Renderer::CHECKMARK_ICON, +                size: None, +            },              style: Default::default(),          }      } @@ -116,6 +133,12 @@ where          self      } +    /// Sets the [`Icon`] of the [`Checkbox`]. +    pub fn icon(mut self, icon: Icon<Renderer::Font>) -> Self { +        self.icon = icon; +        self +    } +      /// Sets the style of the [`Checkbox`].      pub fn style(          mut self, @@ -243,17 +266,24 @@ where                  custom_style.background,              ); +            let Icon { +                font, +                code_point, +                size, +            } = &self.icon; +            let size = size.map(f32::from).unwrap_or(bounds.height * 0.7); +              if self.is_checked {                  renderer.fill_text(text::Text { -                    content: &Renderer::CHECKMARK_ICON.to_string(), -                    font: Renderer::ICON_FONT, -                    size: bounds.height * 0.7, +                    content: &code_point.to_string(), +                    font: font.clone(), +                    size,                      bounds: Rectangle {                          x: bounds.center_x(),                          y: bounds.center_y(),                          ..bounds                      }, -                    color: custom_style.checkmark_color, +                    color: custom_style.icon_color,                      horizontal_alignment: alignment::Horizontal::Center,                      vertical_alignment: alignment::Vertical::Center,                  }); diff --git a/native/src/widget/container.rs b/native/src/widget/container.rs index cdf1c859..c82b8be2 100644 --- a/native/src/widget/container.rs +++ b/native/src/widget/container.rs @@ -5,7 +5,7 @@ use crate::layout;  use crate::mouse;  use crate::overlay;  use crate::renderer; -use crate::widget::{Operation, Tree}; +use crate::widget::{self, Operation, Tree};  use crate::{      Background, Clipboard, Color, Element, Layout, Length, Padding, Point,      Rectangle, Shell, Widget, @@ -24,6 +24,7 @@ where      Renderer: crate::Renderer,      Renderer::Theme: StyleSheet,  { +    id: Option<Id>,      padding: Padding,      width: Length,      height: Length, @@ -46,6 +47,7 @@ where          T: Into<Element<'a, Message, Renderer>>,      {          Container { +            id: None,              padding: Padding::ZERO,              width: Length::Shrink,              height: Length::Shrink, @@ -58,6 +60,12 @@ where          }      } +    /// Sets the [`Id`] of the [`Container`]. +    pub fn id(mut self, id: Id) -> Self { +        self.id = Some(id); +        self +    } +      /// Sets the [`Padding`] of the [`Container`].      pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {          self.padding = padding.into(); @@ -172,14 +180,17 @@ where          renderer: &Renderer,          operation: &mut dyn Operation<Message>,      ) { -        operation.container(None, &mut |operation| { -            self.content.as_widget().operate( -                &mut tree.children[0], -                layout.children().next().unwrap(), -                renderer, -                operation, -            ); -        }); +        operation.container( +            self.id.as_ref().map(|id| &id.0), +            &mut |operation| { +                self.content.as_widget().operate( +                    &mut tree.children[0], +                    layout.children().next().unwrap(), +                    renderer, +                    operation, +                ); +            }, +        );      }      fn on_event( @@ -333,3 +344,27 @@ pub fn draw_background<Renderer>(          );      }  } + +/// The identifier of a [`Container`]. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct Id(widget::Id); + +impl Id { +    /// Creates a custom [`Id`]. +    pub fn new(id: impl Into<std::borrow::Cow<'static, str>>) -> Self { +        Self(widget::Id::new(id)) +    } + +    /// Creates a unique [`Id`]. +    /// +    /// This function produces a different [`Id`] every time it is called. +    pub fn unique() -> Self { +        Self(widget::Id::unique()) +    } +} + +impl From<Id> for widget::Id { +    fn from(id: Id) -> Self { +        id.0 +    } +} diff --git a/native/src/widget/image.rs b/native/src/widget/image.rs index 8bd8ca1e..3ff06a76 100644 --- a/native/src/widget/image.rs +++ b/native/src/widget/image.rs @@ -111,6 +111,45 @@ where      layout::Node::new(final_size)  } +/// Draws an [`Image`] +pub fn draw<Renderer, Handle>( +    renderer: &mut Renderer, +    layout: Layout<'_>, +    handle: &Handle, +    content_fit: ContentFit, +) where +    Renderer: image::Renderer<Handle = Handle>, +    Handle: Clone + Hash, +{ +    let Size { width, height } = renderer.dimensions(handle); +    let image_size = Size::new(width as f32, height as f32); + +    let bounds = layout.bounds(); +    let adjusted_fit = content_fit.fit(image_size, bounds.size()); + +    let render = |renderer: &mut Renderer| { +        let offset = Vector::new( +            (bounds.width - adjusted_fit.width).max(0.0) / 2.0, +            (bounds.height - adjusted_fit.height).max(0.0) / 2.0, +        ); + +        let drawing_bounds = Rectangle { +            width: adjusted_fit.width, +            height: adjusted_fit.height, +            ..bounds +        }; + +        renderer.draw(handle.clone(), drawing_bounds + offset) +    }; + +    if adjusted_fit.width > bounds.width || adjusted_fit.height > bounds.height +    { +        renderer.with_layer(bounds, render); +    } else { +        render(renderer) +    } +} +  impl<Message, Renderer, Handle> Widget<Message, Renderer> for Image<Handle>  where      Renderer: image::Renderer<Handle = Handle>, @@ -149,34 +188,7 @@ where          _cursor_position: Point,          _viewport: &Rectangle,      ) { -        let Size { width, height } = renderer.dimensions(&self.handle); -        let image_size = Size::new(width as f32, height as f32); - -        let bounds = layout.bounds(); -        let adjusted_fit = self.content_fit.fit(image_size, bounds.size()); - -        let render = |renderer: &mut Renderer| { -            let offset = Vector::new( -                (bounds.width - adjusted_fit.width).max(0.0) / 2.0, -                (bounds.height - adjusted_fit.height).max(0.0) / 2.0, -            ); - -            let drawing_bounds = Rectangle { -                width: adjusted_fit.width, -                height: adjusted_fit.height, -                ..bounds -            }; - -            renderer.draw(self.handle.clone(), drawing_bounds + offset) -        }; - -        if adjusted_fit.width > bounds.width -            || adjusted_fit.height > bounds.height -        { -            renderer.with_layer(bounds, render); -        } else { -            render(renderer) -        } +        draw(renderer, layout, &self.handle, self.content_fit)      }  } diff --git a/native/src/widget/operation.rs b/native/src/widget/operation.rs index 73e6a6b0..53688a21 100644 --- a/native/src/widget/operation.rs +++ b/native/src/widget/operation.rs @@ -62,7 +62,7 @@ where      fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {          match self {              Self::None => write!(f, "Outcome::None"), -            Self::Some(output) => write!(f, "Outcome::Some({:?})", output), +            Self::Some(output) => write!(f, "Outcome::Some({output:?})"),              Self::Chain(_) => write!(f, "Outcome::Chain(...)"),          }      } diff --git a/native/src/widget/pane_grid.rs b/native/src/widget/pane_grid.rs index 8dbd1825..eb04c0ba 100644 --- a/native/src/widget/pane_grid.rs +++ b/native/src/widget/pane_grid.rs @@ -35,7 +35,7 @@ pub use iced_style::pane_grid::{Line, StyleSheet};  use crate::event::{self, Event};  use crate::layout;  use crate::mouse; -use crate::overlay; +use crate::overlay::{self, Group};  use crate::renderer;  use crate::touch;  use crate::widget; @@ -450,14 +450,17 @@ where          layout: Layout<'_>,          renderer: &Renderer,      ) -> Option<overlay::Element<'_, Message, Renderer>> { -        self.contents +        let children = self +            .contents              .iter_mut()              .zip(&mut tree.children)              .zip(layout.children()) -            .filter_map(|(((_, pane), tree), layout)| { -                pane.overlay(tree, layout, renderer) +            .filter_map(|(((_, content), state), layout)| { +                content.overlay(state, layout, renderer)              }) -            .next() +            .collect::<Vec<_>>(); + +        (!children.is_empty()).then(|| Group::with_children(children).overlay())      }  } diff --git a/native/src/widget/pick_list.rs b/native/src/widget/pick_list.rs index c2853314..b1cdfad4 100644 --- a/native/src/widget/pick_list.rs +++ b/native/src/widget/pick_list.rs @@ -20,60 +20,6 @@ use std::borrow::Cow;  pub use iced_style::pick_list::{Appearance, StyleSheet}; -/// The handle to the right side of the [`PickList`]. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum Handle<Renderer> -where -    Renderer: text::Renderer, -{ -    /// Displays an arrow icon (▼). -    /// -    /// This is the default. -    Arrow { -        /// Font size of the content. -        size: Option<u16>, -    }, -    /// A custom handle. -    Custom { -        /// Font that will be used to display the `text`, -        font: Renderer::Font, -        /// Text that will be shown. -        text: String, -        /// Font size of the content. -        size: Option<u16>, -    }, -    /// No handle will be shown. -    None, -} - -impl<Renderer> Default for Handle<Renderer> -where -    Renderer: text::Renderer, -{ -    fn default() -> Self { -        Self::Arrow { size: None } -    } -} - -impl<Renderer> Handle<Renderer> -where -    Renderer: text::Renderer, -{ -    fn content(&self) -> Option<(Renderer::Font, String, Option<u16>)> { -        match self { -            Self::Arrow { size } => Some(( -                Renderer::ICON_FONT, -                Renderer::ARROW_DOWN_ICON.to_string(), -                *size, -            )), -            Self::Custom { font, text, size } => { -                Some((font.clone(), text.clone(), *size)) -            } -            Self::None => None, -        } -    } -} -  /// A widget for selecting a single value from a list of options.  #[allow(missing_debug_implementations)]  pub struct PickList<'a, T, Message, Renderer> @@ -90,7 +36,7 @@ where      padding: Padding,      text_size: Option<u16>,      font: Renderer::Font, -    handle: Handle<Renderer>, +    handle: Handle<Renderer::Font>,      style: <Renderer::Theme as StyleSheet>::Style,  } @@ -161,7 +107,7 @@ where      }      /// Sets the [`Handle`] of the [`PickList`]. -    pub fn handle(mut self, handle: Handle<Renderer>) -> Self { +    pub fn handle(mut self, handle: Handle<Renderer::Font>) -> Self {          self.handle = handle;          self      } @@ -258,7 +204,7 @@ where      fn draw(          &self, -        _tree: &Tree, +        tree: &Tree,          renderer: &mut Renderer,          theme: &Renderer::Theme,          _style: &renderer::Style, @@ -278,6 +224,7 @@ where              self.selected.as_ref(),              &self.handle,              &self.style, +            || tree.state.downcast_ref::<State<T>>(),          )      } @@ -349,6 +296,46 @@ impl<T> Default for State<T> {      }  } +/// The handle to the right side of the [`PickList`]. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Handle<Font> { +    /// Displays an arrow icon (▼). +    /// +    /// This is the default. +    Arrow { +        /// Font size of the content. +        size: Option<u16>, +    }, +    /// A custom static handle. +    Static(Icon<Font>), +    /// A custom dynamic handle. +    Dynamic { +        /// The [`Icon`] used when [`PickList`] is closed. +        closed: Icon<Font>, +        /// The [`Icon`] used when [`PickList`] is open. +        open: Icon<Font>, +    }, +    /// No handle will be shown. +    None, +} + +impl<Font> Default for Handle<Font> { +    fn default() -> Self { +        Self::Arrow { size: None } +    } +} + +/// The icon of a [`Handle`]. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Icon<Font> { +    /// Font that will be used to display the `code_point`, +    pub font: Font, +    /// The unicode code point that will be used as the icon. +    pub code_point: char, +    /// Font size of the content. +    pub size: Option<u16>, +} +  /// Computes the layout of a [`PickList`].  pub fn layout<Renderer, T>(      renderer: &Renderer, @@ -568,7 +555,7 @@ where  }  /// Draws a [`PickList`]. -pub fn draw<T, Renderer>( +pub fn draw<'a, T, Renderer>(      renderer: &mut Renderer,      theme: &Renderer::Theme,      layout: Layout<'_>, @@ -578,12 +565,13 @@ pub fn draw<T, Renderer>(      font: &Renderer::Font,      placeholder: Option<&str>,      selected: Option<&T>, -    handle: &Handle<Renderer>, +    handle: &Handle<Renderer::Font>,      style: &<Renderer::Theme as StyleSheet>::Style, +    state: impl FnOnce() -> &'a State<T>,  ) where      Renderer: text::Renderer,      Renderer::Theme: StyleSheet, -    T: ToString, +    T: ToString + 'a,  {      let bounds = layout.bounds();      let is_mouse_over = bounds.contains(cursor_position); @@ -605,11 +593,30 @@ pub fn draw<T, Renderer>(          style.background,      ); -    if let Some((font, text, size)) = handle.content() { +    let handle = match handle { +        Handle::Arrow { size } => { +            Some((Renderer::ICON_FONT, Renderer::ARROW_DOWN_ICON, *size)) +        } +        Handle::Static(Icon { +            font, +            code_point, +            size, +        }) => Some((font.clone(), *code_point, *size)), +        Handle::Dynamic { open, closed } => { +            if state().is_open { +                Some((open.font.clone(), open.code_point, open.size)) +            } else { +                Some((closed.font.clone(), closed.code_point, closed.size)) +            } +        } +        Handle::None => None, +    }; + +    if let Some((font, code_point, size)) = handle {          let size = f32::from(size.unwrap_or_else(|| renderer.default_size()));          renderer.fill_text(Text { -            content: &text, +            content: &code_point.to_string(),              size,              font,              color: style.handle_color, diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index 82286036..2de722e4 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -208,14 +208,17 @@ where          operation.scrollable(state, self.id.as_ref().map(|id| &id.0)); -        operation.container(None, &mut |operation| { -            self.content.as_widget().operate( -                &mut tree.children[0], -                layout.children().next().unwrap(), -                renderer, -                operation, -            ); -        }); +        operation.container( +            self.id.as_ref().map(|id| &id.0), +            &mut |operation| { +                self.content.as_widget().operate( +                    &mut tree.children[0], +                    layout.children().next().unwrap(), +                    renderer, +                    operation, +                ); +            }, +        );      }      fn on_event( diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 8755b85d..5bfc918c 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -389,8 +389,8 @@ where      let padding = padding.fit(Size::ZERO, limits.max());      let limits = limits -        .pad(padding)          .width(width) +        .pad(padding)          .height(Length::Units(text_size));      let mut text = layout::Node::new(limits.resolve(Size::ZERO)); | 
