diff options
Diffstat (limited to 'widget')
| -rw-r--r-- | widget/Cargo.toml | 4 | ||||
| -rw-r--r-- | widget/src/canvas.rs | 6 | ||||
| -rw-r--r-- | widget/src/checkbox.rs | 2 | ||||
| -rw-r--r-- | widget/src/column.rs | 9 | ||||
| -rw-r--r-- | widget/src/combo_box.rs | 65 | ||||
| -rw-r--r-- | widget/src/helpers.rs | 17 | ||||
| -rw-r--r-- | widget/src/keyed/column.rs | 46 | ||||
| -rw-r--r-- | widget/src/lazy.rs | 21 | ||||
| -rw-r--r-- | widget/src/lib.rs | 7 | ||||
| -rw-r--r-- | widget/src/overlay/menu.rs | 2 | ||||
| -rw-r--r-- | widget/src/pane_grid/state.rs | 8 | ||||
| -rw-r--r-- | widget/src/pick_list.rs | 4 | ||||
| -rw-r--r-- | widget/src/row.rs | 9 | ||||
| -rw-r--r-- | widget/src/scrollable.rs | 6 | ||||
| -rw-r--r-- | widget/src/shader.rs | 11 | ||||
| -rw-r--r-- | widget/src/shader/program.rs | 4 | ||||
| -rw-r--r-- | widget/src/text_editor.rs | 15 | ||||
| -rw-r--r-- | widget/src/text_input.rs | 21 | 
18 files changed, 176 insertions, 81 deletions
diff --git a/widget/Cargo.toml b/widget/Cargo.toml index a45f47ef..3c9f6a54 100644 --- a/widget/Cargo.toml +++ b/widget/Cargo.toml @@ -10,6 +10,9 @@ homepage.workspace = true  categories.workspace = true  keywords.workspace = true +[lints] +workspace = true +  [package.metadata.docs.rs]  rustdoc-args = ["--cfg", "docsrs"]  all-features = true @@ -28,6 +31,7 @@ iced_renderer.workspace = true  iced_runtime.workspace = true  num-traits.workspace = true +rustc-hash.workspace = true  thiserror.workspace = true  unicode-segmentation.workspace = true diff --git a/widget/src/canvas.rs b/widget/src/canvas.rs index 7a21895a..42f92de0 100644 --- a/widget/src/canvas.rs +++ b/widget/src/canvas.rs @@ -17,7 +17,7 @@ use crate::core::mouse;  use crate::core::renderer;  use crate::core::widget::tree::{self, Tree};  use crate::core::{ -    Clipboard, Element, Length, Rectangle, Shell, Size, Transformation, Widget, +    Clipboard, Element, Length, Rectangle, Shell, Size, Vector, Widget,  };  use crate::graphics::geometry; @@ -222,8 +222,8 @@ where          let state = tree.state.downcast_ref::<P::State>(); -        renderer.with_transformation( -            Transformation::translate(bounds.x, bounds.y), +        renderer.with_translation( +            Vector::new(bounds.x, bounds.y),              |renderer| {                  let layers =                      self.program.draw(state, renderer, theme, bounds, cursor); diff --git a/widget/src/checkbox.rs b/widget/src/checkbox.rs index 48f6abf6..225c316d 100644 --- a/widget/src/checkbox.rs +++ b/widget/src/checkbox.rs @@ -340,7 +340,7 @@ where              if self.is_checked {                  renderer.fill_text(                      text::Text { -                        content: &code_point.to_string(), +                        content: code_point.to_string(),                          font: *font,                          size,                          line_height: *line_height, diff --git a/widget/src/column.rs b/widget/src/column.rs index d37ef695..df7829b3 100644 --- a/widget/src/column.rs +++ b/widget/src/column.rs @@ -33,11 +33,18 @@ where          Self::from_vec(Vec::new())      } +    /// Creates a [`Column`] with the given capacity. +    pub fn with_capacity(capacity: usize) -> Self { +        Self::from_vec(Vec::with_capacity(capacity)) +    } +      /// Creates a [`Column`] with the given elements.      pub fn with_children(          children: impl IntoIterator<Item = Element<'a, Message, Theme, Renderer>>,      ) -> Self { -        Self::new().extend(children) +        let iterator = children.into_iter(); + +        Self::with_capacity(iterator.size_hint().0).extend(iterator)      }      /// Creates a [`Column`] from an already allocated [`Vec`]. diff --git a/widget/src/combo_box.rs b/widget/src/combo_box.rs index e4f4a41f..253850df 100644 --- a/widget/src/combo_box.rs +++ b/widget/src/combo_box.rs @@ -700,38 +700,47 @@ where                  ..              } = tree.state.downcast_mut::<Menu<T>>(); -            let bounds = layout.bounds(); -              self.state.sync_filtered_options(filtered_options); -            let mut menu = menu::Menu::new( -                menu, -                &filtered_options.options, -                hovered_option, -                |x| { -                    tree.children[0] -                        .state -                        .downcast_mut::<text_input::State<Renderer::Paragraph>>( -                        ) -                        .unfocus(); - -                    (self.on_selected)(x) -                }, -                self.on_option_hovered.as_deref(), -                &self.menu_class, -            ) -            .width(bounds.width) -            .padding(self.padding); - -            if let Some(font) = self.font { -                menu = menu.font(font); -            } +            if filtered_options.options.is_empty() { +                None +            } else { +                let bounds = layout.bounds(); + +                let mut menu = menu::Menu::new( +                    menu, +                    &filtered_options.options, +                    hovered_option, +                    |x| { +                        tree.children[0] +                    .state +                    .downcast_mut::<text_input::State<Renderer::Paragraph>>( +                    ) +                    .unfocus(); + +                        (self.on_selected)(x) +                    }, +                    self.on_option_hovered.as_deref(), +                    &self.menu_class, +                ) +                .width(bounds.width) +                .padding(self.padding); + +                if let Some(font) = self.font { +                    menu = menu.font(font); +                } -            if let Some(size) = self.size { -                menu = menu.text_size(size); -            } +                if let Some(size) = self.size { +                    menu = menu.text_size(size); +                } -            Some(menu.overlay(layout.position() + translation, bounds.height)) +                Some( +                    menu.overlay( +                        layout.position() + translation, +                        bounds.height, +                    ), +                ) +            }          } else {              None          } diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index 77b30882..61789c19 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -145,13 +145,26 @@ where  ///  /// [`Text`]: core::widget::Text  pub fn text<'a, Theme, Renderer>( -    text: impl ToString, +    text: impl text::IntoFragment<'a>,  ) -> Text<'a, Theme, Renderer>  where      Theme: text::Catalog + 'a,      Renderer: core::text::Renderer,  { -    Text::new(text.to_string()) +    Text::new(text) +} + +/// Creates a new [`Text`] widget that displays the provided value. +/// +/// [`Text`]: core::widget::Text +pub fn value<'a, Theme, Renderer>( +    value: impl ToString, +) -> Text<'a, Theme, Renderer> +where +    Theme: text::Catalog + 'a, +    Renderer: core::text::Renderer, +{ +    Text::new(value.to_string())  }  /// Creates a new [`Checkbox`]. diff --git a/widget/src/keyed/column.rs b/widget/src/keyed/column.rs index 8a8d5fe7..a34ce9e6 100644 --- a/widget/src/keyed/column.rs +++ b/widget/src/keyed/column.rs @@ -40,27 +40,49 @@ where  {      /// Creates an empty [`Column`].      pub fn new() -> Self { -        Column { +        Self::from_vecs(Vec::new(), Vec::new()) +    } + +    /// Creates a [`Column`] from already allocated [`Vec`]s. +    /// +    /// Keep in mind that the [`Column`] will not inspect the [`Vec`]s, which means +    /// it won't automatically adapt to the sizing strategy of its contents. +    /// +    /// If any of the children have a [`Length::Fill`] strategy, you will need to +    /// call [`Column::width`] or [`Column::height`] accordingly. +    pub fn from_vecs( +        keys: Vec<Key>, +        children: Vec<Element<'a, Message, Theme, Renderer>>, +    ) -> Self { +        Self {              spacing: 0.0,              padding: Padding::ZERO,              width: Length::Shrink,              height: Length::Shrink,              max_width: f32::INFINITY,              align_items: Alignment::Start, -            keys: Vec::new(), -            children: Vec::new(), +            keys, +            children,          }      } +    /// Creates a [`Column`] with the given capacity. +    pub fn with_capacity(capacity: usize) -> Self { +        Self::from_vecs( +            Vec::with_capacity(capacity), +            Vec::with_capacity(capacity), +        ) +    } +      /// Creates a [`Column`] with the given elements.      pub fn with_children(          children: impl IntoIterator<              Item = (Key, Element<'a, Message, Theme, Renderer>),          >,      ) -> Self { -        children -            .into_iter() -            .fold(Self::new(), |column, (key, child)| column.push(key, child)) +        let iterator = children.into_iter(); + +        Self::with_capacity(iterator.size_hint().0).extend(iterator)      }      /// Sets the vertical spacing _between_ elements. @@ -132,6 +154,18 @@ where              self          }      } + +    /// Extends the [`Column`] with the given children. +    pub fn extend( +        self, +        children: impl IntoIterator< +            Item = (Key, Element<'a, Message, Theme, Renderer>), +        >, +    ) -> Self { +        children +            .into_iter() +            .fold(self, |column, (key, child)| column.push(key, child)) +    }  }  impl<'a, Key, Message, Renderer> Default for Column<'a, Key, Message, Renderer> diff --git a/widget/src/lazy.rs b/widget/src/lazy.rs index eb663ea5..04783dbe 100644 --- a/widget/src/lazy.rs +++ b/widget/src/lazy.rs @@ -18,11 +18,12 @@ use crate::core::widget::tree::{self, Tree};  use crate::core::widget::{self, Widget};  use crate::core::Element;  use crate::core::{ -    self, Clipboard, Hasher, Length, Point, Rectangle, Shell, Size, Vector, +    self, Clipboard, Length, Point, Rectangle, Shell, Size, Vector,  };  use crate::runtime::overlay::Nested;  use ouroboros::self_referencing; +use rustc_hash::FxHasher;  use std::cell::RefCell;  use std::hash::{Hash, Hasher as H};  use std::rc::Rc; @@ -106,9 +107,12 @@ where      }      fn state(&self) -> tree::State { -        let mut hasher = Hasher::default(); -        self.dependency.hash(&mut hasher); -        let hash = hasher.finish(); +        let hash = { +            let mut hasher = FxHasher::default(); +            self.dependency.hash(&mut hasher); + +            hasher.finish() +        };          let element =              Rc::new(RefCell::new(Some((self.view)(&self.dependency).into()))); @@ -127,9 +131,12 @@ where              .state              .downcast_mut::<Internal<Message, Theme, Renderer>>(); -        let mut hasher = Hasher::default(); -        self.dependency.hash(&mut hasher); -        let new_hash = hasher.finish(); +        let new_hash = { +            let mut hasher = FxHasher::default(); +            self.dependency.hash(&mut hasher); + +            hasher.finish() +        };          if current.hash != new_hash {              current.hash = new_hash; diff --git a/widget/src/lib.rs b/widget/src/lib.rs index 209dfad9..1eeacbae 100644 --- a/widget/src/lib.rs +++ b/widget/src/lib.rs @@ -2,13 +2,6 @@  #![doc(      html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg"  )] -#![forbid(unsafe_code, rust_2018_idioms)] -#![deny( -    missing_debug_implementations, -    missing_docs, -    unused_results, -    rustdoc::broken_intra_doc_links -)]  #![cfg_attr(docsrs, feature(doc_auto_cfg))]  pub use iced_renderer as renderer;  pub use iced_renderer::graphics; diff --git a/widget/src/overlay/menu.rs b/widget/src/overlay/menu.rs index d76caa8a..98efe305 100644 --- a/widget/src/overlay/menu.rs +++ b/widget/src/overlay/menu.rs @@ -526,7 +526,7 @@ where              renderer.fill_text(                  Text { -                    content: &option.to_string(), +                    content: option.to_string(),                      bounds: Size::new(f32::INFINITY, bounds.height),                      size: text_size,                      line_height: self.text_line_height, diff --git a/widget/src/pane_grid/state.rs b/widget/src/pane_grid/state.rs index 481cd770..c20c3b9c 100644 --- a/widget/src/pane_grid/state.rs +++ b/widget/src/pane_grid/state.rs @@ -6,7 +6,7 @@ use crate::pane_grid::{      Axis, Configuration, Direction, Edge, Node, Pane, Region, Split, Target,  }; -use std::collections::HashMap; +use rustc_hash::FxHashMap;  /// The state of a [`PaneGrid`].  /// @@ -25,7 +25,7 @@ pub struct State<T> {      /// The panes of the [`PaneGrid`].      ///      /// [`PaneGrid`]: super::PaneGrid -    pub panes: HashMap<Pane, T>, +    pub panes: FxHashMap<Pane, T>,      /// The internal state of the [`PaneGrid`].      /// @@ -52,7 +52,7 @@ impl<T> State<T> {      /// Creates a new [`State`] with the given [`Configuration`].      pub fn with_configuration(config: impl Into<Configuration<T>>) -> Self { -        let mut panes = HashMap::new(); +        let mut panes = FxHashMap::default();          let internal =              Internal::from_configuration(&mut panes, config.into(), 0); @@ -353,7 +353,7 @@ impl Internal {      ///      /// [`PaneGrid`]: super::PaneGrid      pub fn from_configuration<T>( -        panes: &mut HashMap<Pane, T>, +        panes: &mut FxHashMap<Pane, T>,          content: Configuration<T>,          next_id: usize,      ) -> Self { diff --git a/widget/src/pick_list.rs b/widget/src/pick_list.rs index 801e792b..edccfdaa 100644 --- a/widget/src/pick_list.rs +++ b/widget/src/pick_list.rs @@ -479,7 +479,7 @@ where              renderer.fill_text(                  Text { -                    content: &code_point.to_string(), +                    content: code_point.to_string(),                      size,                      line_height,                      font, @@ -502,7 +502,7 @@ where          let label = selected.map(ToString::to_string); -        if let Some(label) = label.as_deref().or(self.placeholder.as_deref()) { +        if let Some(label) = label.or_else(|| self.placeholder.clone()) {              let text_size =                  self.text_size.unwrap_or_else(|| renderer.default_size()); diff --git a/widget/src/row.rs b/widget/src/row.rs index 47feff9c..fa352171 100644 --- a/widget/src/row.rs +++ b/widget/src/row.rs @@ -31,11 +31,18 @@ where          Self::from_vec(Vec::new())      } +    /// Creates a [`Row`] with the given capacity. +    pub fn with_capacity(capacity: usize) -> Self { +        Self::from_vec(Vec::with_capacity(capacity)) +    } +      /// Creates a [`Row`] with the given elements.      pub fn with_children(          children: impl IntoIterator<Item = Element<'a, Message, Theme, Renderer>>,      ) -> Self { -        Self::new().extend(children) +        let iterator = children.into_iter(); + +        Self::with_capacity(iterator.size_hint().0).extend(iterator)      }      /// Creates a [`Row`] from an already allocated [`Vec`]. diff --git a/widget/src/scrollable.rs b/widget/src/scrollable.rs index 84e9ac15..668c5372 100644 --- a/widget/src/scrollable.rs +++ b/widget/src/scrollable.rs @@ -651,7 +651,7 @@ where          defaults: &renderer::Style,          layout: Layout<'_>,          cursor: mouse::Cursor, -        _viewport: &Rectangle, +        viewport: &Rectangle,      ) {          let state = tree.state.downcast_ref::<State>(); @@ -767,8 +767,8 @@ where              renderer.with_layer(                  Rectangle { -                    width: bounds.width + 2.0, -                    height: bounds.height + 2.0, +                    width: (bounds.width + 2.0).min(viewport.width), +                    height: (bounds.height + 2.0).min(viewport.height),                      ..bounds                  },                  |renderer| { diff --git a/widget/src/shader.rs b/widget/src/shader.rs index 68112f83..fad2f4eb 100644 --- a/widget/src/shader.rs +++ b/widget/src/shader.rs @@ -13,12 +13,13 @@ use crate::core::widget::tree::{self, Tree};  use crate::core::widget::{self, Widget};  use crate::core::window;  use crate::core::{Clipboard, Element, Length, Rectangle, Shell, Size}; -use crate::renderer::wgpu::primitive::pipeline; +use crate::renderer::wgpu::primitive;  use std::marker::PhantomData; +pub use crate::graphics::Viewport;  pub use crate::renderer::wgpu::wgpu; -pub use pipeline::{Primitive, Storage}; +pub use primitive::{Primitive, Storage};  /// A widget which can render custom shaders with Iced's `wgpu` backend.  /// @@ -60,7 +61,7 @@ impl<P, Message, Theme, Renderer> Widget<Message, Theme, Renderer>      for Shader<Message, P>  where      P: Program<Message>, -    Renderer: pipeline::Renderer, +    Renderer: primitive::Renderer,  {      fn tag(&self) -> tree::Tag {          struct Tag<T>(T); @@ -160,7 +161,7 @@ where          let bounds = layout.bounds();          let state = tree.state.downcast_ref::<P::State>(); -        renderer.draw_pipeline_primitive( +        renderer.draw_primitive(              bounds,              self.program.draw(state, cursor_position, bounds),          ); @@ -171,7 +172,7 @@ impl<'a, Message, Theme, Renderer, P> From<Shader<Message, P>>      for Element<'a, Message, Theme, Renderer>  where      Message: 'a, -    Renderer: pipeline::Renderer, +    Renderer: primitive::Renderer,      P: Program<Message> + 'a,  {      fn from( diff --git a/widget/src/shader/program.rs b/widget/src/shader/program.rs index 6dd50404..902c7c3b 100644 --- a/widget/src/shader/program.rs +++ b/widget/src/shader/program.rs @@ -1,7 +1,7 @@  use crate::core::event;  use crate::core::mouse;  use crate::core::{Rectangle, Shell}; -use crate::renderer::wgpu::primitive::pipeline; +use crate::renderer::wgpu::Primitive;  use crate::shader;  /// The state and logic of a [`Shader`] widget. @@ -15,7 +15,7 @@ pub trait Program<Message> {      type State: Default + 'static;      /// The type of primitive this [`Program`] can draw. -    type Primitive: pipeline::Primitive + 'static; +    type Primitive: Primitive + 'static;      /// Update the internal [`State`] of the [`Program`]. This can be used to reflect state changes      /// based on mouse & other events. You can use the [`Shell`] to publish messages, request a 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<Pixels>) -> 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<text::LineHeight>, +    ) -> Self { +        self.line_height = line_height.into(); +        self +    } +      /// Sets the [`Padding`] of the [`TextEditor`].      pub fn padding(mut self, padding: impl Into<Padding>) -> Self {          self.padding = padding.into(); diff --git a/widget/src/text_input.rs b/widget/src/text_input.rs index dafe2fca..e9f07838 100644 --- a/widget/src/text_input.rs +++ b/widget/src/text_input.rs @@ -232,7 +232,7 @@ where          let placeholder_text = Text {              font,              line_height: self.line_height, -            content: &self.placeholder, +            content: self.placeholder.as_str(),              bounds: Size::new(f32::INFINITY, text_bounds.height),              size: text_size,              horizontal_alignment: alignment::Horizontal::Left, @@ -251,9 +251,11 @@ where          });          if let Some(icon) = &self.icon { +            let mut content = [0; 4]; +              let icon_text = Text {                  line_height: self.line_height, -                content: &icon.code_point.to_string(), +                content: icon.code_point.encode_utf8(&mut content) as &_,                  font: icon.font,                  size: icon.size.unwrap_or_else(|| renderer.default_size()),                  bounds: Size::new(f32::INFINITY, text_bounds.height), @@ -366,7 +368,7 @@ where          let text = value.to_string(); -        let (cursor, offset) = if let Some(focus) = state +        let (cursor, offset, is_selecting) = if let Some(focus) = state              .is_focused              .as_ref()              .filter(|focus| focus.is_window_focused) @@ -404,7 +406,7 @@ where                          None                      }; -                    (cursor, offset) +                    (cursor, offset, false)                  }                  cursor::State::Selection { start, end } => {                      let left = start.min(end); @@ -444,11 +446,12 @@ where                          } else {                              left_offset                          }, +                        true,                      )                  }              }          } else { -            (None, 0.0) +            (None, 0.0, false)          };          let draw = |renderer: &mut Renderer, viewport| { @@ -480,7 +483,7 @@ where              );          }; -        if cursor.is_some() { +        if is_selecting {              renderer                  .with_layer(text_bounds, |renderer| draw(renderer, *viewport));          } else { @@ -710,7 +713,8 @@ where                      match key.as_ref() {                          keyboard::Key::Character("c") -                            if state.keyboard_modifiers.command() => +                            if state.keyboard_modifiers.command() +                                && !self.is_secure =>                          {                              if let Some((start, end)) =                                  state.cursor.selection(&self.value) @@ -724,7 +728,8 @@ where                              return event::Status::Captured;                          }                          keyboard::Key::Character("x") -                            if state.keyboard_modifiers.command() => +                            if state.keyboard_modifiers.command() +                                && !self.is_secure =>                          {                              if let Some((start, end)) =                                  state.cursor.selection(&self.value)  | 
