summaryrefslogtreecommitdiffstats
path: root/native
diff options
context:
space:
mode:
Diffstat (limited to 'native')
-rw-r--r--native/src/layout/limits.rs23
-rw-r--r--native/src/widget/checkbox.rs4
-rw-r--r--native/src/widget/helpers.rs4
-rw-r--r--native/src/widget/operation/focusable.rs4
-rw-r--r--native/src/widget/pick_list.rs97
-rw-r--r--native/src/widget/text_input.rs12
-rw-r--r--native/src/widget/toggler.rs18
-rw-r--r--native/src/window.rs2
-rw-r--r--native/src/window/action.rs43
-rw-r--r--native/src/window/user_attention.rs21
10 files changed, 184 insertions, 44 deletions
diff --git a/native/src/layout/limits.rs b/native/src/layout/limits.rs
index 33a452d0..4cbb970d 100644
--- a/native/src/layout/limits.rs
+++ b/native/src/layout/limits.rs
@@ -1,3 +1,4 @@
+#![allow(clippy::manual_clamp)]
use crate::{Length, Padding, Size};
/// A set of size constraints for layouting.
@@ -51,7 +52,7 @@ impl Limits {
}
Length::Units(units) => {
let new_width =
- (units as f32).clamp(self.min.width, self.max.width);
+ (units as f32).min(self.max.width).max(self.min.width);
self.min.width = new_width;
self.max.width = new_width;
@@ -73,7 +74,7 @@ impl Limits {
}
Length::Units(units) => {
let new_height =
- (units as f32).clamp(self.min.height, self.max.height);
+ (units as f32).min(self.max.height).max(self.min.height);
self.min.height = new_height;
self.max.height = new_height;
@@ -86,14 +87,16 @@ impl Limits {
/// Applies a minimum width constraint to the current [`Limits`].
pub fn min_width(mut self, min_width: u32) -> Limits {
- self.min.width = self.min.width.clamp(min_width as f32, self.max.width);
+ self.min.width =
+ self.min.width.max(min_width as f32).min(self.max.width);
self
}
/// Applies a maximum width constraint to the current [`Limits`].
pub fn max_width(mut self, max_width: u32) -> Limits {
- self.max.width = self.max.width.clamp(self.min.width, max_width as f32);
+ self.max.width =
+ self.max.width.min(max_width as f32).max(self.min.width);
self
}
@@ -101,7 +104,7 @@ impl Limits {
/// Applies a minimum height constraint to the current [`Limits`].
pub fn min_height(mut self, min_height: u32) -> Limits {
self.min.height =
- self.min.height.clamp(min_height as f32, self.max.height);
+ self.min.height.max(min_height as f32).min(self.max.height);
self
}
@@ -109,7 +112,7 @@ impl Limits {
/// Applies a maximum height constraint to the current [`Limits`].
pub fn max_height(mut self, max_height: u32) -> Limits {
self.max.height =
- self.max.height.clamp(self.min.height, max_height as f32);
+ self.max.height.min(max_height as f32).max(self.min.height);
self
}
@@ -155,10 +158,14 @@ impl Limits {
/// intrinsic size of some content.
pub fn resolve(&self, intrinsic_size: Size) -> Size {
Size::new(
- intrinsic_size.width.clamp(self.fill.width, self.max.width),
+ intrinsic_size
+ .width
+ .min(self.max.width)
+ .max(self.fill.width),
intrinsic_size
.height
- .clamp(self.fill.height, self.max.height),
+ .min(self.max.height)
+ .max(self.fill.height),
)
}
}
diff --git a/native/src/widget/checkbox.rs b/native/src/widget/checkbox.rs
index bec5c448..b46433c2 100644
--- a/native/src/widget/checkbox.rs
+++ b/native/src/widget/checkbox.rs
@@ -27,7 +27,7 @@ pub use iced_style::checkbox::{Appearance, StyleSheet};
///
/// let is_checked = true;
///
-/// Checkbox::new(is_checked, "Toggle me!", Message::CheckboxToggled);
+/// Checkbox::new("Toggle me!", is_checked, Message::CheckboxToggled);
/// ```
///
/// ![Checkbox drawn by `iced_wgpu`](https://github.com/iced-rs/iced/blob/7760618fb112074bc40b148944521f312152012a/docs/images/checkbox.png?raw=true)
@@ -67,7 +67,7 @@ where
/// * a function that will be called when the [`Checkbox`] is toggled. It
/// will receive the new state of the [`Checkbox`] and must produce a
/// `Message`.
- pub fn new<F>(is_checked: bool, label: impl Into<String>, f: F) -> Self
+ pub fn new<F>(label: impl Into<String>, is_checked: bool, f: F) -> Self
where
F: 'a + Fn(bool) -> Message,
{
diff --git a/native/src/widget/helpers.rs b/native/src/widget/helpers.rs
index 8cc1ae82..dfd949f6 100644
--- a/native/src/widget/helpers.rs
+++ b/native/src/widget/helpers.rs
@@ -129,7 +129,7 @@ where
Renderer: crate::text::Renderer,
Renderer::Theme: widget::checkbox::StyleSheet + widget::text::StyleSheet,
{
- widget::Checkbox::new(is_checked, label, f)
+ widget::Checkbox::new(label, is_checked, f)
}
/// Creates a new [`Radio`].
@@ -162,7 +162,7 @@ where
Renderer: crate::text::Renderer,
Renderer::Theme: widget::toggler::StyleSheet,
{
- widget::Toggler::new(is_checked, label, f)
+ widget::Toggler::new(label, is_checked, f)
}
/// Creates a new [`TextInput`].
diff --git a/native/src/widget/operation/focusable.rs b/native/src/widget/operation/focusable.rs
index 0067006b..312e4894 100644
--- a/native/src/widget/operation/focusable.rs
+++ b/native/src/widget/operation/focusable.rs
@@ -18,10 +18,10 @@ pub trait Focusable {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Count {
/// The index of the current focused widget, if any.
- focused: Option<usize>,
+ pub focused: Option<usize>,
/// The total amount of focusable widgets.
- total: usize,
+ pub total: usize,
}
/// Produces an [`Operation`] that focuses the widget with the given [`Id`].
diff --git a/native/src/widget/pick_list.rs b/native/src/widget/pick_list.rs
index 52cb1ad1..c2853314 100644
--- a/native/src/widget/pick_list.rs
+++ b/native/src/widget/pick_list.rs
@@ -20,6 +20,60 @@ 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>
@@ -36,6 +90,7 @@ where
padding: Padding,
text_size: Option<u16>,
font: Renderer::Font,
+ handle: Handle<Renderer>,
style: <Renderer::Theme as StyleSheet>::Style,
}
@@ -67,9 +122,10 @@ where
placeholder: None,
selected,
width: Length::Shrink,
- text_size: None,
padding: Self::DEFAULT_PADDING,
+ text_size: None,
font: Default::default(),
+ handle: Default::default(),
style: Default::default(),
}
}
@@ -104,6 +160,12 @@ where
self
}
+ /// Sets the [`Handle`] of the [`PickList`].
+ pub fn handle(mut self, handle: Handle<Renderer>) -> Self {
+ self.handle = handle;
+ self
+ }
+
/// Sets the style of the [`PickList`].
pub fn style(
mut self,
@@ -214,6 +276,7 @@ where
&self.font,
self.placeholder.as_deref(),
self.selected.as_ref(),
+ &self.handle,
&self.style,
)
}
@@ -515,6 +578,7 @@ pub fn draw<T, Renderer>(
font: &Renderer::Font,
placeholder: Option<&str>,
selected: Option<&T>,
+ handle: &Handle<Renderer>,
style: &<Renderer::Theme as StyleSheet>::Style,
) where
Renderer: text::Renderer,
@@ -541,19 +605,24 @@ pub fn draw<T, Renderer>(
style.background,
);
- renderer.fill_text(Text {
- content: &Renderer::ARROW_DOWN_ICON.to_string(),
- font: Renderer::ICON_FONT,
- size: bounds.height * style.icon_size,
- bounds: Rectangle {
- x: bounds.x + bounds.width - f32::from(padding.horizontal()),
- y: bounds.center_y(),
- ..bounds
- },
- color: style.text_color,
- horizontal_alignment: alignment::Horizontal::Right,
- vertical_alignment: alignment::Vertical::Center,
- });
+ if let Some((font, text, size)) = handle.content() {
+ let size = f32::from(size.unwrap_or_else(|| renderer.default_size()));
+
+ renderer.fill_text(Text {
+ content: &text,
+ size,
+ font,
+ color: style.handle_color,
+ bounds: Rectangle {
+ x: bounds.x + bounds.width - f32::from(padding.horizontal()),
+ y: bounds.center_y() - size / 2.0,
+ height: size,
+ ..bounds
+ },
+ horizontal_alignment: alignment::Horizontal::Right,
+ vertical_alignment: alignment::Vertical::Top,
+ });
+ }
let label = selected.map(ToString::to_string);
diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs
index 05b47ff9..8b4514e3 100644
--- a/native/src/widget/text_input.rs
+++ b/native/src/widget/text_input.rs
@@ -454,9 +454,17 @@ where
)
} else {
None
- };
+ }
+ .unwrap_or(0);
- state.cursor.move_to(position.unwrap_or(0));
+ if state.keyboard_modifiers.shift() {
+ state.cursor.select_range(
+ state.cursor.start(value),
+ position,
+ );
+ } else {
+ state.cursor.move_to(position);
+ }
state.is_dragging = true;
}
click::Kind::Double => {
diff --git a/native/src/widget/toggler.rs b/native/src/widget/toggler.rs
index 1ae65ba6..f0a944a3 100644
--- a/native/src/widget/toggler.rs
+++ b/native/src/widget/toggler.rs
@@ -24,9 +24,9 @@ pub use iced_style::toggler::{Appearance, StyleSheet};
/// TogglerToggled(bool),
/// }
///
-/// let is_active = true;
+/// let is_toggled = true;
///
-/// Toggler::new(is_active, String::from("Toggle me!"), |b| Message::TogglerToggled(b));
+/// Toggler::new(String::from("Toggle me!"), is_toggled, |b| Message::TogglerToggled(b));
/// ```
#[allow(missing_debug_implementations)]
pub struct Toggler<'a, Message, Renderer>
@@ -34,7 +34,7 @@ where
Renderer: text::Renderer,
Renderer::Theme: StyleSheet,
{
- is_active: bool,
+ is_toggled: bool,
on_toggle: Box<dyn Fn(bool) -> Message + 'a>,
label: Option<String>,
width: Length,
@@ -63,15 +63,15 @@ where
/// will receive the new state of the [`Toggler`] and must produce a
/// `Message`.
pub fn new<F>(
- is_active: bool,
label: impl Into<Option<String>>,
+ is_toggled: bool,
f: F,
) -> Self
where
F: 'a + Fn(bool) -> Message,
{
Toggler {
- is_active,
+ is_toggled,
on_toggle: Box::new(f),
label: label.into(),
width: Length::Fill,
@@ -193,7 +193,7 @@ where
let mouse_over = layout.bounds().contains(cursor_position);
if mouse_over {
- shell.publish((self.on_toggle)(!self.is_active));
+ shell.publish((self.on_toggle)(!self.is_toggled));
event::Status::Captured
} else {
@@ -260,9 +260,9 @@ where
let is_mouse_over = bounds.contains(cursor_position);
let style = if is_mouse_over {
- theme.hovered(&self.style, self.is_active)
+ theme.hovered(&self.style, self.is_toggled)
} else {
- theme.active(&self.style, self.is_active)
+ theme.active(&self.style, self.is_toggled)
};
let border_radius = bounds.height / BORDER_RADIUS_RATIO;
@@ -289,7 +289,7 @@ where
let toggler_foreground_bounds = Rectangle {
x: bounds.x
- + if self.is_active {
+ + if self.is_toggled {
bounds.width - 2.0 * space - (bounds.height - (4.0 * space))
} else {
2.0 * space
diff --git a/native/src/window.rs b/native/src/window.rs
index f910b8f2..1b97e655 100644
--- a/native/src/window.rs
+++ b/native/src/window.rs
@@ -2,7 +2,9 @@
mod action;
mod event;
mod mode;
+mod user_attention;
pub use action::Action;
pub use event::Event;
pub use mode::Mode;
+pub use user_attention::UserAttention;
diff --git a/native/src/window/action.rs b/native/src/window/action.rs
index da307e97..37fcc273 100644
--- a/native/src/window/action.rs
+++ b/native/src/window/action.rs
@@ -1,4 +1,4 @@
-use crate::window::Mode;
+use crate::window::{Mode, UserAttention};
use iced_futures::MaybeSend;
use std::fmt;
@@ -35,6 +35,8 @@ pub enum Action<T> {
},
/// Set the [`Mode`] of the window.
SetMode(Mode),
+ /// Fetch the current [`Mode`] of the window.
+ FetchMode(Box<dyn FnOnce(Mode) -> T + 'static>),
/// Sets the window to maximized or back
ToggleMaximize,
/// Toggles whether window has decorations
@@ -42,8 +44,31 @@ pub enum Action<T> {
/// - **X11:** Not implemented.
/// - **Web:** Unsupported.
ToggleDecorations,
- /// Fetch the current [`Mode`] of the window.
- FetchMode(Box<dyn FnOnce(Mode) -> T + 'static>),
+ /// Requests 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.
+ ///
+ /// 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.
+ ///
+ /// ## Platform-specific
+ ///
+ /// - **iOS / Android / Web:** Unsupported.
+ /// - **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<UserAttention>),
+ /// Brings 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
+ /// you are certain that's what the user wants. Focus stealing can cause an extremely disruptive
+ /// user experience.
+ ///
+ /// ## Platform-specific
+ ///
+ /// - **Web / Wayland:** Unsupported.
+ GainFocus,
}
impl<T> Action<T> {
@@ -63,9 +88,13 @@ impl<T> Action<T> {
Self::Minimize(bool) => Action::Minimize(bool),
Self::Move { x, y } => Action::Move { x, y },
Self::SetMode(mode) => Action::SetMode(mode),
+ Self::FetchMode(o) => Action::FetchMode(Box::new(move |s| f(o(s)))),
Self::ToggleMaximize => Action::ToggleMaximize,
Self::ToggleDecorations => Action::ToggleDecorations,
- Self::FetchMode(o) => Action::FetchMode(Box::new(move |s| f(o(s)))),
+ Self::RequestUserAttention(attention_type) => {
+ Action::RequestUserAttention(attention_type)
+ }
+ Self::GainFocus => Action::GainFocus,
}
}
}
@@ -86,9 +115,13 @@ impl<T> fmt::Debug for Action<T> {
write!(f, "Action::Move {{ x: {}, y: {} }}", x, y)
}
Self::SetMode(mode) => write!(f, "Action::SetMode({:?})", mode),
+ Self::FetchMode(_) => write!(f, "Action::FetchMode"),
Self::ToggleMaximize => write!(f, "Action::ToggleMaximize"),
Self::ToggleDecorations => write!(f, "Action::ToggleDecorations"),
- Self::FetchMode(_) => write!(f, "Action::FetchMode"),
+ Self::RequestUserAttention(_) => {
+ write!(f, "Action::RequestUserAttention")
+ }
+ Self::GainFocus => write!(f, "Action::GainFocus"),
}
}
}
diff --git a/native/src/window/user_attention.rs b/native/src/window/user_attention.rs
new file mode 100644
index 00000000..b03dfeef
--- /dev/null
+++ b/native/src/window/user_attention.rs
@@ -0,0 +1,21 @@
+/// The type of user attention to request.
+///
+/// ## Platform-specific
+///
+/// - **X11:** Sets the WM's `XUrgencyHint`. No distinction between [`Critical`] and [`Informational`].
+///
+/// [`Critical`]: Self::Critical
+/// [`Informational`]: Self::Informational
+#[derive(Debug, Clone, Copy)]
+pub enum UserAttention {
+ /// ## Platform-specific
+ ///
+ /// - **macOS:** Bounces the dock icon until the application is in focus.
+ /// - **Windows:** Flashes both the window and the taskbar button until the application is in focus.
+ Critical,
+ /// ## Platform-specific
+ ///
+ /// - **macOS:** Bounces the dock icon once.
+ /// - **Windows:** Flashes the taskbar button until the application is in focus.
+ Informational,
+}