summaryrefslogtreecommitdiffstats
path: root/native
diff options
context:
space:
mode:
Diffstat (limited to 'native')
-rw-r--r--native/src/lib.rs4
-rw-r--r--native/src/program.rs2
-rw-r--r--native/src/widget/image/viewer.rs25
-rw-r--r--native/src/widget/pane_grid/title_bar.rs18
-rw-r--r--native/src/widget/pick_list.rs96
-rw-r--r--native/src/widget/text_input.rs40
-rw-r--r--native/src/window/event.rs8
7 files changed, 147 insertions, 46 deletions
diff --git a/native/src/lib.rs b/native/src/lib.rs
index cd214e36..cbb02506 100644
--- a/native/src/lib.rs
+++ b/native/src/lib.rs
@@ -61,8 +61,8 @@ mod debug;
mod debug;
pub use iced_core::{
- Align, Background, Color, Font, HorizontalAlignment, Length, Padding,
- Point, Rectangle, Size, Vector, VerticalAlignment,
+ menu, Align, Background, Color, Font, HorizontalAlignment, Length, Menu,
+ Padding, Point, Rectangle, Size, Vector, VerticalAlignment,
};
pub use iced_futures::{executor, futures, Command};
diff --git a/native/src/program.rs b/native/src/program.rs
index 066c29d8..75fab094 100644
--- a/native/src/program.rs
+++ b/native/src/program.rs
@@ -11,7 +11,7 @@ pub trait Program: Sized {
type Renderer: Renderer;
/// The type of __messages__ your [`Program`] will produce.
- type Message: std::fmt::Debug + Send;
+ type Message: std::fmt::Debug + Clone + Send;
/// The type of [`Clipboard`] your [`Program`] will use.
type Clipboard: Clipboard;
diff --git a/native/src/widget/image/viewer.rs b/native/src/widget/image/viewer.rs
index a006c0af..405daf00 100644
--- a/native/src/widget/image/viewer.rs
+++ b/native/src/widget/image/viewer.rs
@@ -132,19 +132,30 @@ where
) -> layout::Node {
let (width, height) = renderer.dimensions(&self.handle);
- let aspect_ratio = width as f32 / height as f32;
-
let mut size = limits
.width(self.width)
.height(self.height)
.resolve(Size::new(width as f32, height as f32));
- let viewport_aspect_ratio = size.width / size.height;
-
- if viewport_aspect_ratio > aspect_ratio {
- size.width = width as f32 * size.height / height as f32;
+ let expansion_size = if height > width {
+ self.width
} else {
- size.height = height as f32 * size.width / width as f32;
+ self.height
+ };
+
+ // Only calculate viewport sizes if the images are constrained to a limited space.
+ // If they are Fill|Portion let them expand within their alotted space.
+ match expansion_size {
+ Length::Shrink | Length::Units(_) => {
+ let aspect_ratio = width as f32 / height as f32;
+ let viewport_aspect_ratio = size.width / size.height;
+ if viewport_aspect_ratio > aspect_ratio {
+ size.width = width as f32 * size.height / height as f32;
+ } else {
+ size.height = height as f32 * size.width / width as f32;
+ }
+ }
+ Length::Fill | Length::FillPortion(_) => {}
}
layout::Node::new(size)
diff --git a/native/src/widget/pane_grid/title_bar.rs b/native/src/widget/pane_grid/title_bar.rs
index efdc1e54..070010f8 100644
--- a/native/src/widget/pane_grid/title_bar.rs
+++ b/native/src/widget/pane_grid/title_bar.rs
@@ -248,6 +248,22 @@ where
&mut self,
layout: Layout<'_>,
) -> Option<overlay::Element<'_, Message, Renderer>> {
- self.content.overlay(layout)
+ let mut children = layout.children();
+ let padded = children.next()?;
+
+ let mut children = padded.children();
+ let title_layout = children.next()?;
+
+ let Self {
+ content, controls, ..
+ } = self;
+
+ content.overlay(title_layout).or_else(move || {
+ controls.as_mut().and_then(|controls| {
+ let controls_layout = children.next()?;
+
+ controls.overlay(controls_layout)
+ })
+ })
}
}
diff --git a/native/src/widget/pick_list.rs b/native/src/widget/pick_list.rs
index 92c183f3..d7792000 100644
--- a/native/src/widget/pick_list.rs
+++ b/native/src/widget/pick_list.rs
@@ -1,5 +1,6 @@
//! Display a dropdown list of selectable values.
use crate::event::{self, Event};
+use crate::keyboard;
use crate::layout;
use crate::mouse;
use crate::overlay;
@@ -20,11 +21,13 @@ where
[T]: ToOwned<Owned = Vec<T>>,
{
menu: &'a mut menu::State,
+ keyboard_modifiers: &'a mut keyboard::Modifiers,
is_open: &'a mut bool,
hovered_option: &'a mut Option<usize>,
last_selection: &'a mut Option<T>,
on_selected: Box<dyn Fn(T) -> Message>,
options: Cow<'a, [T]>,
+ placeholder: Option<String>,
selected: Option<T>,
width: Length,
padding: Padding,
@@ -37,6 +40,7 @@ where
#[derive(Debug, Clone)]
pub struct State<T> {
menu: menu::State,
+ keyboard_modifiers: keyboard::Modifiers,
is_open: bool,
hovered_option: Option<usize>,
last_selection: Option<T>,
@@ -46,6 +50,7 @@ impl<T> Default for State<T> {
fn default() -> Self {
Self {
menu: menu::State::default(),
+ keyboard_modifiers: keyboard::Modifiers::default(),
is_open: bool::default(),
hovered_option: Option::default(),
last_selection: Option::default(),
@@ -70,6 +75,7 @@ where
) -> Self {
let State {
menu,
+ keyboard_modifiers,
is_open,
hovered_option,
last_selection,
@@ -77,11 +83,13 @@ where
Self {
menu,
+ keyboard_modifiers,
is_open,
hovered_option,
last_selection,
on_selected: Box::new(on_selected),
options: options.into(),
+ placeholder: None,
selected,
width: Length::Shrink,
text_size: None,
@@ -91,6 +99,12 @@ where
}
}
+ /// Sets the placeholder of the [`PickList`].
+ pub fn placeholder(mut self, placeholder: impl Into<String>) -> Self {
+ self.placeholder = Some(placeholder.into());
+ self
+ }
+
/// Sets the width of the [`PickList`].
pub fn width(mut self, width: Length) -> Self {
self.width = width;
@@ -154,24 +168,34 @@ where
.pad(self.padding);
let text_size = self.text_size.unwrap_or(renderer.default_size());
+ let font = self.font;
let max_width = match self.width {
Length::Shrink => {
+ let measure = |label: &str| -> u32 {
+ let (width, _) = renderer.measure(
+ label,
+ text_size,
+ font,
+ Size::new(f32::INFINITY, f32::INFINITY),
+ );
+
+ width.round() as u32
+ };
+
let labels = self.options.iter().map(ToString::to_string);
- labels
- .map(|label| {
- let (width, _) = renderer.measure(
- &label,
- text_size,
- self.font,
- Size::new(f32::INFINITY, f32::INFINITY),
- );
-
- width.round() as u32
- })
- .max()
- .unwrap_or(100)
+ let labels_width =
+ labels.map(|label| measure(&label)).max().unwrap_or(100);
+
+ let placeholder_width = self
+ .placeholder
+ .as_ref()
+ .map(String::as_str)
+ .map(measure)
+ .unwrap_or(100);
+
+ labels_width.max(placeholder_width)
}
_ => 0,
};
@@ -195,6 +219,8 @@ where
match self.width {
Length::Shrink => {
+ self.placeholder.hash(state);
+
self.options
.iter()
.map(ToString::to_string)
@@ -248,6 +274,48 @@ where
event_status
}
}
+ Event::Mouse(mouse::Event::WheelScrolled {
+ delta: mouse::ScrollDelta::Lines { y, .. },
+ }) if self.keyboard_modifiers.command()
+ && layout.bounds().contains(cursor_position)
+ && !*self.is_open =>
+ {
+ fn find_next<'a, T: PartialEq>(
+ selected: &'a T,
+ mut options: impl Iterator<Item = &'a T>,
+ ) -> Option<&'a T> {
+ let _ = options.find(|&option| option == selected);
+
+ options.next()
+ }
+
+ let next_option = if y < 0.0 {
+ if let Some(selected) = self.selected.as_ref() {
+ find_next(selected, self.options.iter())
+ } else {
+ self.options.first()
+ }
+ } else if y > 0.0 {
+ if let Some(selected) = self.selected.as_ref() {
+ find_next(selected, self.options.iter().rev())
+ } else {
+ self.options.last()
+ }
+ } else {
+ None
+ };
+
+ if let Some(next_option) = next_option {
+ messages.push((self.on_selected)(next_option.clone()));
+ }
+
+ event::Status::Captured
+ }
+ Event::Keyboard(keyboard::Event::ModifiersChanged(modifiers)) => {
+ *self.keyboard_modifiers = modifiers;
+
+ event::Status::Ignored
+ }
_ => event::Status::Ignored,
}
}
@@ -265,6 +333,7 @@ where
layout.bounds(),
cursor_position,
self.selected.as_ref().map(ToString::to_string),
+ self.placeholder.as_ref().map(String::as_str),
self.padding,
self.text_size.unwrap_or(renderer.default_size()),
self.font,
@@ -325,6 +394,7 @@ pub trait Renderer: text::Renderer + menu::Renderer {
bounds: Rectangle,
cursor_position: Point,
selected: Option<String>,
+ placeholder: Option<&str>,
padding: Padding,
text_size: u16,
font: Self::Font,
diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs
index 20117fa0..cec1e485 100644
--- a/native/src/widget/text_input.rs
+++ b/native/src/widget/text_input.rs
@@ -362,7 +362,7 @@ where
Event::Keyboard(keyboard::Event::CharacterReceived(c))
if self.state.is_focused
&& self.state.is_pasting.is_none()
- && !self.state.keyboard_modifiers.is_command_pressed()
+ && !self.state.keyboard_modifiers.command()
&& !c.is_control() =>
{
let mut editor =
@@ -450,7 +450,7 @@ where
if platform::is_jump_modifier_pressed(modifiers)
&& !self.is_secure
{
- if modifiers.shift {
+ if modifiers.shift() {
self.state
.cursor
.select_left_by_words(&self.value);
@@ -459,7 +459,7 @@ where
.cursor
.move_left_by_words(&self.value);
}
- } else if modifiers.shift {
+ } else if modifiers.shift() {
self.state.cursor.select_left(&self.value)
} else {
self.state.cursor.move_left(&self.value);
@@ -469,7 +469,7 @@ where
if platform::is_jump_modifier_pressed(modifiers)
&& !self.is_secure
{
- if modifiers.shift {
+ if modifiers.shift() {
self.state
.cursor
.select_right_by_words(&self.value);
@@ -478,14 +478,14 @@ where
.cursor
.move_right_by_words(&self.value);
}
- } else if modifiers.shift {
+ } else if modifiers.shift() {
self.state.cursor.select_right(&self.value)
} else {
self.state.cursor.move_right(&self.value);
}
}
keyboard::KeyCode::Home => {
- if modifiers.shift {
+ if modifiers.shift() {
self.state.cursor.select_range(
self.state.cursor.start(&self.value),
0,
@@ -495,7 +495,7 @@ where
}
}
keyboard::KeyCode::End => {
- if modifiers.shift {
+ if modifiers.shift() {
self.state.cursor.select_range(
self.state.cursor.start(&self.value),
self.value.len(),
@@ -505,10 +505,7 @@ where
}
}
keyboard::KeyCode::C
- if self
- .state
- .keyboard_modifiers
- .is_command_pressed() =>
+ if self.state.keyboard_modifiers.command() =>
{
match self.state.cursor.selection(&self.value) {
Some((start, end)) => {
@@ -520,10 +517,7 @@ where
}
}
keyboard::KeyCode::X
- if self
- .state
- .keyboard_modifiers
- .is_command_pressed() =>
+ if self.state.keyboard_modifiers.command() =>
{
match self.state.cursor.selection(&self.value) {
Some((start, end)) => {
@@ -545,7 +539,7 @@ where
messages.push(message);
}
keyboard::KeyCode::V => {
- if self.state.keyboard_modifiers.is_command_pressed() {
+ if self.state.keyboard_modifiers.command() {
let content = match self.state.is_pasting.take() {
Some(content) => content,
None => {
@@ -576,10 +570,7 @@ where
}
}
keyboard::KeyCode::A
- if self
- .state
- .keyboard_modifiers
- .is_command_pressed() =>
+ if self.state.keyboard_modifiers.command() =>
{
self.state.cursor.select_all(&self.value);
}
@@ -795,6 +786,11 @@ impl State {
pub fn move_cursor_to(&mut self, position: usize) {
self.cursor.move_to(position);
}
+
+ /// Selects all the content of the [`TextInput`].
+ pub fn select_all(&mut self) {
+ self.cursor.select_range(0, usize::MAX);
+ }
}
// TODO: Reduce allocations
@@ -858,9 +854,9 @@ mod platform {
pub fn is_jump_modifier_pressed(modifiers: keyboard::Modifiers) -> bool {
if cfg!(target_os = "macos") {
- modifiers.alt
+ modifiers.alt()
} else {
- modifiers.control
+ modifiers.control()
}
}
}
diff --git a/native/src/window/event.rs b/native/src/window/event.rs
index 3aa1ab0b..64f2b8d8 100644
--- a/native/src/window/event.rs
+++ b/native/src/window/event.rs
@@ -3,6 +3,14 @@ use std::path::PathBuf;
/// A window-related event.
#[derive(PartialEq, Clone, Debug)]
pub enum Event {
+ /// A window was moved.
+ Moved {
+ /// The new logical x location of the window
+ x: i32,
+ /// The new logical y location of the window
+ y: i32,
+ },
+
/// A window was resized.
Resized {
/// The new width of the window (in units)