diff options
author | 2023-02-15 14:55:02 -0800 | |
---|---|---|
committer | 2023-02-15 14:55:02 -0800 | |
commit | 63fb608d8bea8a653bf011f5f9cffc88525576e0 (patch) | |
tree | 938c15b85fd9d27ee3a82806cf83bc211e7b94dd /native | |
parent | 64e0e817c27d720dc954ee94de58ded35b3f9f9a (diff) | |
parent | 0cb72f69716adc82ad85a0ee7120edb6e653b0c0 (diff) | |
download | iced-63fb608d8bea8a653bf011f5f9cffc88525576e0.tar.gz iced-63fb608d8bea8a653bf011f5f9cffc88525576e0.tar.bz2 iced-63fb608d8bea8a653bf011f5f9cffc88525576e0.zip |
Merge remote-tracking branch 'origin/master' into feat/multi-window-support
# Conflicts:
# native/src/command/action.rs
# native/src/window/action.rs
# winit/src/window.rs
Diffstat (limited to '')
-rw-r--r-- | native/src/command.rs | 1 | ||||
-rw-r--r-- | native/src/command/action.rs | 6 | ||||
-rw-r--r-- | native/src/debug/basic.rs | 8 | ||||
-rw-r--r-- | native/src/image.rs | 4 | ||||
-rw-r--r-- | native/src/overlay.rs | 18 | ||||
-rw-r--r-- | native/src/overlay/element.rs | 19 | ||||
-rw-r--r-- | native/src/overlay/group.rs | 174 | ||||
-rw-r--r-- | native/src/overlay/menu.rs | 5 | ||||
-rw-r--r-- | native/src/renderer.rs | 4 | ||||
-rw-r--r-- | native/src/shell.rs | 5 | ||||
-rw-r--r-- | native/src/svg.rs | 2 | ||||
-rw-r--r-- | native/src/user_interface.rs | 37 | ||||
-rw-r--r-- | native/src/widget/action.rs | 12 | ||||
-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/text_input.rs | 2 | ||||
-rw-r--r-- | native/src/window/action.rs | 34 |
19 files changed, 394 insertions, 151 deletions
diff --git a/native/src/command.rs b/native/src/command.rs index 89ee7375..ca9d0b64 100644 --- a/native/src/command.rs +++ b/native/src/command.rs @@ -11,6 +11,7 @@ use std::fmt; use std::future::Future; /// A set of asynchronous actions to be performed by some runtime. +#[must_use = "`Command` must be returned to runtime to take effect"] pub struct Command<T>(iced_futures::Command<Action<T>>); impl<T> Command<T> { diff --git a/native/src/command/action.rs b/native/src/command/action.rs index 924f95e6..dcb79672 100644 --- a/native/src/command/action.rs +++ b/native/src/command/action.rs @@ -58,12 +58,12 @@ impl<T> fmt::Debug for Action<T> { match self { Self::Future(_) => write!(f, "Action::Future"), Self::Clipboard(action) => { - write!(f, "Action::Clipboard({:?})", action) + write!(f, "Action::Clipboard({action:?})") } Self::Window(id, action) => { - write!(f, "Action::Window({:?}, {:?})", id, action) + write!(f, "Action::Window({id:?}, {action:?})") } - Self::System(action) => write!(f, "Action::System({:?})", action), + Self::System(action) => write!(f, "Action::System({action:?})"), Self::Widget(_action) => write!(f, "Action::Widget"), } } diff --git a/native/src/debug/basic.rs b/native/src/debug/basic.rs index 603f2fd5..92f614da 100644 --- a/native/src/debug/basic.rs +++ b/native/src/debug/basic.rs @@ -133,7 +133,7 @@ impl Debug { } pub fn log_message<Message: std::fmt::Debug>(&mut self, message: &Message) { - self.last_messages.push_back(format!("{:?}", message)); + self.last_messages.push_back(format!("{message:?}")); if self.last_messages.len() > 10 { let _ = self.last_messages.pop_front(); @@ -150,7 +150,7 @@ impl Debug { let mut lines = Vec::new(); fn key_value<T: std::fmt::Debug>(key: &str, value: T) -> String { - format!("{} {:?}", key, value) + format!("{key} {value:?}") } lines.push(format!( @@ -176,9 +176,9 @@ impl Debug { lines.push(String::from("Last messages:")); lines.extend(self.last_messages.iter().map(|msg| { if msg.len() <= 100 { - format!(" {}", msg) + format!(" {msg}") } else { - format!(" {:.100}...", msg) + format!(" {msg:.100}...") } })); diff --git a/native/src/image.rs b/native/src/image.rs index 06fd7ae6..5d2843c9 100644 --- a/native/src/image.rs +++ b/native/src/image.rs @@ -107,10 +107,10 @@ pub enum Data { impl std::fmt::Debug for Data { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Data::Path(path) => write!(f, "Path({:?})", path), + Data::Path(path) => write!(f, "Path({path:?})"), Data::Bytes(_) => write!(f, "Bytes(...)"), Data::Rgba { width, height, .. } => { - write!(f, "Pixels({} * {})", width, height) + write!(f, "Pixels({width} * {height})") } } } diff --git a/native/src/overlay.rs b/native/src/overlay.rs index 22f8b6ec..6cada416 100644 --- a/native/src/overlay.rs +++ b/native/src/overlay.rs @@ -1,9 +1,11 @@ //! Display interactive elements on top of other widgets. mod element; +mod group; pub mod menu; pub use element::Element; +pub use group::Group; pub use menu::Menu; use crate::event::{self, Event}; @@ -87,9 +89,17 @@ where ) -> mouse::Interaction { mouse::Interaction::Idle } + + /// Returns true if the cursor is over the [`Overlay`]. + /// + /// By default, it returns true if the bounds of the `layout` contain + /// the `cursor_position`. + fn is_over(&self, layout: Layout<'_>, cursor_position: Point) -> bool { + layout.bounds().contains(cursor_position) + } } -/// Obtains the first overlay [`Element`] found in the given children. +/// Returns a [`Group`] of overlay [`Element`] children. /// /// This method will generally only be used by advanced users that are /// implementing the [`Widget`](crate::Widget) trait. @@ -102,12 +112,14 @@ pub fn from_children<'a, Message, Renderer>( where Renderer: crate::Renderer, { - children + let children = children .iter_mut() .zip(&mut tree.children) .zip(layout.children()) .filter_map(|((child, state), layout)| { child.as_widget_mut().overlay(state, layout, renderer) }) - .next() + .collect::<Vec<_>>(); + + (!children.is_empty()).then(|| Group::with_children(children).overlay()) } diff --git a/native/src/overlay/element.rs b/native/src/overlay/element.rs index 41a8a597..237d25d1 100644 --- a/native/src/overlay/element.rs +++ b/native/src/overlay/element.rs @@ -53,8 +53,14 @@ where } /// Computes the layout of the [`Element`] in the given bounds. - pub fn layout(&self, renderer: &Renderer, bounds: Size) -> layout::Node { - self.overlay.layout(renderer, bounds, self.position) + pub fn layout( + &self, + renderer: &Renderer, + bounds: Size, + translation: Vector, + ) -> layout::Node { + self.overlay + .layout(renderer, bounds, self.position + translation) } /// Processes a runtime [`Event`]. @@ -115,6 +121,11 @@ where ) { self.overlay.operate(layout, renderer, operation); } + + /// Returns true if the cursor is over the [`Element`]. + pub fn is_over(&self, layout: Layout<'_>, cursor_position: Point) -> bool { + self.overlay.is_over(layout, cursor_position) + } } struct Map<'a, A, B, Renderer> { @@ -252,4 +263,8 @@ where self.content .draw(renderer, theme, style, layout, cursor_position) } + + fn is_over(&self, layout: Layout<'_>, cursor_position: Point) -> bool { + self.content.is_over(layout, cursor_position) + } } diff --git a/native/src/overlay/group.rs b/native/src/overlay/group.rs new file mode 100644 index 00000000..1126f0cf --- /dev/null +++ b/native/src/overlay/group.rs @@ -0,0 +1,174 @@ +use iced_core::{Point, Rectangle, Size}; + +use crate::event; +use crate::layout; +use crate::mouse; +use crate::overlay; +use crate::renderer; +use crate::widget; +use crate::{Clipboard, Event, Layout, Overlay, Shell}; + +/// An [`Overlay`] container that displays multiple overlay [`overlay::Element`] +/// children. +#[allow(missing_debug_implementations)] +pub struct Group<'a, Message, Renderer> { + children: Vec<overlay::Element<'a, Message, Renderer>>, +} + +impl<'a, Message, Renderer> Group<'a, Message, Renderer> +where + Renderer: 'a + crate::Renderer, + Message: 'a, +{ + /// Creates an empty [`Group`]. + pub fn new() -> Self { + Self::default() + } + + /// Creates a [`Group`] with the given elements. + pub fn with_children( + children: Vec<overlay::Element<'a, Message, Renderer>>, + ) -> Self { + Group { children } + } + + /// Adds an [`overlay::Element`] to the [`Group`]. + pub fn push( + mut self, + child: impl Into<overlay::Element<'a, Message, Renderer>>, + ) -> Self { + self.children.push(child.into()); + self + } + + /// Turns the [`Group`] into an overlay [`overlay::Element`]. + pub fn overlay(self) -> overlay::Element<'a, Message, Renderer> { + overlay::Element::new(Point::ORIGIN, Box::new(self)) + } +} + +impl<'a, Message, Renderer> Default for Group<'a, Message, Renderer> +where + Renderer: 'a + crate::Renderer, + Message: 'a, +{ + fn default() -> Self { + Self::with_children(Vec::new()) + } +} + +impl<'a, Message, Renderer> Overlay<Message, Renderer> + for Group<'a, Message, Renderer> +where + Renderer: crate::Renderer, +{ + fn layout( + &self, + renderer: &Renderer, + bounds: Size, + position: Point, + ) -> layout::Node { + let translation = position - Point::ORIGIN; + + layout::Node::with_children( + bounds, + self.children + .iter() + .map(|child| child.layout(renderer, bounds, translation)) + .collect(), + ) + } + + fn on_event( + &mut self, + event: Event, + layout: Layout<'_>, + cursor_position: Point, + renderer: &Renderer, + clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + ) -> event::Status { + self.children + .iter_mut() + .zip(layout.children()) + .map(|(child, layout)| { + child.on_event( + event.clone(), + layout, + cursor_position, + renderer, + clipboard, + shell, + ) + }) + .fold(event::Status::Ignored, event::Status::merge) + } + + fn draw( + &self, + renderer: &mut Renderer, + theme: &<Renderer as crate::Renderer>::Theme, + style: &renderer::Style, + layout: Layout<'_>, + cursor_position: Point, + ) { + for (child, layout) in self.children.iter().zip(layout.children()) { + child.draw(renderer, theme, style, layout, cursor_position); + } + } + + fn mouse_interaction( + &self, + layout: Layout<'_>, + cursor_position: Point, + viewport: &Rectangle, + renderer: &Renderer, + ) -> mouse::Interaction { + self.children + .iter() + .zip(layout.children()) + .map(|(child, layout)| { + child.mouse_interaction( + layout, + cursor_position, + viewport, + renderer, + ) + }) + .max() + .unwrap_or_default() + } + + fn operate( + &mut self, + layout: Layout<'_>, + renderer: &Renderer, + operation: &mut dyn widget::Operation<Message>, + ) { + operation.container(None, &mut |operation| { + self.children.iter_mut().zip(layout.children()).for_each( + |(child, layout)| { + child.operate(layout, renderer, operation); + }, + ) + }); + } + + fn is_over(&self, layout: Layout<'_>, cursor_position: Point) -> bool { + self.children + .iter() + .zip(layout.children()) + .any(|(child, layout)| child.is_over(layout, cursor_position)) + } +} + +impl<'a, Message, Renderer> From<Group<'a, Message, Renderer>> + for overlay::Element<'a, Message, Renderer> +where + Renderer: 'a + crate::Renderer, + Message: 'a, +{ + fn from(group: Group<'a, Message, Renderer>) -> Self { + group.overlay() + } +} diff --git a/native/src/overlay/menu.rs b/native/src/overlay/menu.rs index 099b1a97..9e37380f 100644 --- a/native/src/overlay/menu.rs +++ b/native/src/overlay/menu.rs @@ -281,10 +281,7 @@ where renderer.fill_quad( renderer::Quad { - bounds: Rectangle { - width: bounds.width - 1.0, - ..bounds - }, + bounds, border_color: appearance.border_color, border_width: appearance.border_width, border_radius: appearance.border_radius.into(), diff --git a/native/src/renderer.rs b/native/src/renderer.rs index d5329acd..2ac78982 100644 --- a/native/src/renderer.rs +++ b/native/src/renderer.rs @@ -16,9 +16,9 @@ pub trait Renderer: Sized { /// /// You should override this if you need to perform any operations before or /// after layouting. For instance, trimming the measurements cache. - fn layout<'a, Message>( + fn layout<Message>( &mut self, - element: &Element<'a, Message, Self>, + element: &Element<'_, Message, Self>, limits: &layout::Limits, ) -> layout::Node { element.as_widget().layout(self, limits) diff --git a/native/src/shell.rs b/native/src/shell.rs index f1ddb48e..74a5c616 100644 --- a/native/src/shell.rs +++ b/native/src/shell.rs @@ -25,6 +25,11 @@ impl<'a, Message> Shell<'a, Message> { } } + /// Returns true if the [`Shell`] contains no published messages + pub fn is_empty(&self) -> bool { + self.messages.is_empty() + } + /// Publish the given `Message` for an application to process it. pub fn publish(&mut self, message: Message) { self.messages.push(message); diff --git a/native/src/svg.rs b/native/src/svg.rs index 2168e409..9b98877a 100644 --- a/native/src/svg.rs +++ b/native/src/svg.rs @@ -71,7 +71,7 @@ pub enum Data { impl std::fmt::Debug for Data { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Data::Path(path) => write!(f, "Path({:?})", path), + Data::Path(path) => write!(f, "Path({path:?})"), Data::Bytes(_) => write!(f, "Bytes(...)"), } } diff --git a/native/src/user_interface.rs b/native/src/user_interface.rs index 29cc3472..2358bff1 100644 --- a/native/src/user_interface.rs +++ b/native/src/user_interface.rs @@ -6,7 +6,9 @@ use crate::mouse; use crate::renderer; use crate::widget; use crate::window; -use crate::{Clipboard, Element, Layout, Point, Rectangle, Shell, Size}; +use crate::{ + Clipboard, Element, Layout, Point, Rectangle, Shell, Size, Vector, +}; /// A set of interactive graphical elements with a specific [`Layout`]. /// @@ -203,7 +205,7 @@ where let bounds = self.bounds; let mut overlay = manual_overlay.as_mut().unwrap(); - let mut layout = overlay.layout(renderer, bounds); + let mut layout = overlay.layout(renderer, bounds, Vector::ZERO); let mut event_statuses = Vec::new(); for event in events.iter().cloned() { @@ -252,7 +254,7 @@ where overlay = manual_overlay.as_mut().unwrap(); shell.revalidate_layout(|| { - layout = overlay.layout(renderer, bounds); + layout = overlay.layout(renderer, bounds, Vector::ZERO); }); } @@ -261,12 +263,16 @@ where } } - let base_cursor = if layout.bounds().contains(cursor_position) { - // TODO: Type-safe cursor availability - Point::new(-1.0, -1.0) - } else { - cursor_position - }; + let base_cursor = manual_overlay + .as_ref() + .filter(|overlay| { + overlay.is_over(Layout::new(&layout), cursor_position) + }) + .map(|_| { + // TODO: Type-safe cursor availability + Point::new(-1.0, -1.0) + }) + .unwrap_or(cursor_position); self.overlay = Some(layout); @@ -430,10 +436,9 @@ where .as_widget_mut() .overlay(&mut self.state, Layout::new(&self.base), renderer) { - let overlay_layout = self - .overlay - .take() - .unwrap_or_else(|| overlay.layout(renderer, self.bounds)); + let overlay_layout = self.overlay.take().unwrap_or_else(|| { + overlay.layout(renderer, self.bounds, Vector::ZERO) + }); let new_cursor_position = if overlay_layout.bounds().contains(cursor_position) { @@ -504,7 +509,8 @@ where ); }); - if overlay_bounds.contains(cursor_position) { + if overlay.is_over(Layout::new(layout), cursor_position) + { overlay_interaction } else { base_interaction @@ -533,7 +539,8 @@ where renderer, ) { if self.overlay.is_none() { - self.overlay = Some(overlay.layout(renderer, self.bounds)); + self.overlay = + Some(overlay.layout(renderer, self.bounds, Vector::ZERO)); } overlay.operate( 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/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/text_input.rs b/native/src/widget/text_input.rs index a62d9f35..bf87e817 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)); diff --git a/native/src/window/action.rs b/native/src/window/action.rs index 929663ec..63858bc8 100644 --- a/native/src/window/action.rs +++ b/native/src/window/action.rs @@ -1,4 +1,4 @@ -use crate::window; +use crate::window::{Mode, UserAttention}; use iced_futures::MaybeSend; use std::fmt; @@ -38,20 +38,21 @@ pub enum Action<T> { /// The new logical y location of the window y: i32, }, - /// Set the [`Mode`] of the window. - SetMode(window::Mode), + /// Change the [`Mode`] of the window. + ChangeMode(Mode), /// Fetch the current [`Mode`] of the window. - FetchMode(Box<dyn FnOnce(window::Mode) -> T + 'static>), - /// Sets the window to maximized or back + FetchMode(Box<dyn FnOnce(Mode) -> T + 'static>), + /// Toggle the window to maximized or back ToggleMaximize, - /// Toggles whether window has decorations + /// Toggle whether window has decorations. + /// /// ## Platform-specific /// - **X11:** Not implemented. /// - **Web:** Unsupported. ToggleDecorations, - /// Requests user attention to the window, this has no effect if the application + /// Request user attention to the window, this has no effect if the application /// is already focused. How requesting for user attention manifests is platform dependent, - /// see [`UserAttentionType`] for details. + /// see [`UserAttention`] for details. /// /// Providing `None` will unset the request for user attention. Unsetting the request for /// user attention might not be done automatically by the WM when the window receives input. @@ -62,8 +63,8 @@ pub enum Action<T> { /// - **macOS:** `None` has no effect. /// - **X11:** Requests for user attention must be manually cleared. /// - **Wayland:** Requires `xdg_activation_v1` protocol, `None` has no effect. - RequestUserAttention(Option<window::UserAttention>), - /// Brings the window to the front and sets input focus. Has no effect if the window is + RequestUserAttention(Option<UserAttention>), + /// Bring the window to the front and sets input focus. Has no effect if the window is /// already in focus, minimized, or not visible. /// /// This method steals input focus from other applications. Do not use this method unless @@ -93,7 +94,7 @@ impl<T> Action<T> { Self::Maximize(bool) => Action::Maximize(bool), Self::Minimize(bool) => Action::Minimize(bool), Self::Move { x, y } => Action::Move { x, y }, - Self::SetMode(mode) => Action::SetMode(mode), + Self::ChangeMode(mode) => Action::ChangeMode(mode), Self::FetchMode(o) => Action::FetchMode(Box::new(move |s| f(o(s)))), Self::ToggleMaximize => Action::ToggleMaximize, Self::ToggleDecorations => Action::ToggleDecorations, @@ -115,15 +116,14 @@ impl<T> fmt::Debug for Action<T> { } Self::Resize { width, height } => write!( f, - "Action::Resize {{ widget: {}, height: {} }}", - width, height + "Action::Resize {{ widget: {width}, height: {height} }}" ), - Self::Maximize(value) => write!(f, "Action::Maximize({})", value), - Self::Minimize(value) => write!(f, "Action::Minimize({}", value), + Self::Maximize(value) => write!(f, "Action::Maximize({value})"), + Self::Minimize(value) => write!(f, "Action::Minimize({value}"), Self::Move { x, y } => { - write!(f, "Action::Move {{ x: {}, y: {} }}", x, y) + write!(f, "Action::Move {{ x: {x}, y: {y} }}") } - Self::SetMode(mode) => write!(f, "Action::SetMode({:?})", mode), + Self::ChangeMode(mode) => write!(f, "Action::SetMode({mode:?})"), Self::FetchMode(_) => write!(f, "Action::FetchMode"), Self::ToggleMaximize => write!(f, "Action::ToggleMaximize"), Self::ToggleDecorations => write!(f, "Action::ToggleDecorations"), |