From 7d2d813343baae10c2667590463385d50abb784c Mon Sep 17 00:00:00 2001 From: Giuliano Bellini s294739 Date: Mon, 23 Jan 2023 16:57:24 +0100 Subject: [PATCH 01/85] added new style for scrollable, to be applied when mouse is over the scrollable area --- native/src/widget/scrollable.rs | 4 ++++ style/src/scrollable.rs | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index 822860364d..de6eacb551 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -856,6 +856,8 @@ pub fn draw( theme.dragging(style) } else if mouse_over_y_scrollbar { theme.hovered(style) + } else if mouse_over_scrollable { + theme.focused(style) } else { theme.active(style) }; @@ -869,6 +871,8 @@ pub fn draw( theme.dragging_horizontal(style) } else if mouse_over_x_scrollbar { theme.hovered_horizontal(style) + } else if mouse_over_scrollable { + theme.focused_horizontal(style) } else { theme.active_horizontal(style) }; diff --git a/style/src/scrollable.rs b/style/src/scrollable.rs index 64ed8462f4..a3f3db209f 100644 --- a/style/src/scrollable.rs +++ b/style/src/scrollable.rs @@ -45,6 +45,11 @@ pub trait StyleSheet { self.hovered(style) } + /// Produces the style of a scrollbar when mouse is over the scrollable area. + fn focused(&self, style: &Self::Style) -> Scrollbar { + self.active(style) + } + /// Produces the style of an active horizontal scrollbar. fn active_horizontal(&self, style: &Self::Style) -> Scrollbar { self.active(style) @@ -59,4 +64,9 @@ pub trait StyleSheet { fn dragging_horizontal(&self, style: &Self::Style) -> Scrollbar { self.hovered_horizontal(style) } + + /// Produces the style of a horizontal scrollbar when mouse is over the scrollable area. + fn focused_horizontal(&self, style: &Self::Style) -> Scrollbar { + self.active_horizontal(style) + } } From eaa2238600a0d3055b379592c7a3f8e6453b9dc7 Mon Sep 17 00:00:00 2001 From: Giuliano Bellini s294739 Date: Mon, 23 Jan 2023 17:32:08 +0100 Subject: [PATCH 02/85] debugging focused style not working --- native/src/widget/scrollable.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index de6eacb551..718140347b 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -859,7 +859,7 @@ pub fn draw( } else if mouse_over_scrollable { theme.focused(style) } else { - theme.active(style) + theme.focused(style) }; draw_scrollbar(renderer, style, &scrollbar); @@ -874,7 +874,7 @@ pub fn draw( } else if mouse_over_scrollable { theme.focused_horizontal(style) } else { - theme.active_horizontal(style) + theme.focused_horizontal(style) }; draw_scrollbar(renderer, style, &scrollbar); From 49e9a9a5379c1e9a9469045ca9a51ffb860ee620 Mon Sep 17 00:00:00 2001 From: Giuliano Bellini s294739 Date: Mon, 23 Jan 2023 17:56:39 +0100 Subject: [PATCH 03/85] added function focused and focused_horizontal to theme.rs --- native/src/widget/scrollable.rs | 4 ++-- style/src/theme.rs | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index 718140347b..de6eacb551 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -859,7 +859,7 @@ pub fn draw( } else if mouse_over_scrollable { theme.focused(style) } else { - theme.focused(style) + theme.active(style) }; draw_scrollbar(renderer, style, &scrollbar); @@ -874,7 +874,7 @@ pub fn draw( } else if mouse_over_scrollable { theme.focused_horizontal(style) } else { - theme.focused_horizontal(style) + theme.active_horizontal(style) }; draw_scrollbar(renderer, style, &scrollbar); diff --git a/style/src/theme.rs b/style/src/theme.rs index 55bfa4cab4..8d40bda113 100644 --- a/style/src/theme.rs +++ b/style/src/theme.rs @@ -935,6 +935,13 @@ impl scrollable::StyleSheet for Theme { } } + fn focused(&self, style: &Self::Style) -> scrollable::Scrollbar { + match style { + Scrollable::Default => self.active(style), + Scrollable::Custom(custom) => custom.focused(self), + } + } + fn active_horizontal(&self, style: &Self::Style) -> scrollable::Scrollbar { match style { Scrollable::Default => self.active(style), @@ -958,6 +965,16 @@ impl scrollable::StyleSheet for Theme { Scrollable::Custom(custom) => custom.dragging_horizontal(self), } } + + fn focused_horizontal( + &self, + style: &Self::Style, + ) -> scrollable::Scrollbar { + match style { + Scrollable::Default => self.active_horizontal(style), + Scrollable::Custom(custom) => custom.focused_horizontal(self), + } + } } /// The style of text. From 96c0bd65df5b85682a2d674b231a5ed420f106fb Mon Sep 17 00:00:00 2001 From: Casper Storm Date: Mon, 20 Feb 2023 12:24:31 +0100 Subject: [PATCH 04/85] Sliders no longer bleed over rail --- native/src/widget/slider.rs | 4 ++-- native/src/widget/vertical_slider.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/native/src/widget/slider.rs b/native/src/widget/slider.rs index 87030a4d09..f5251dfa7b 100644 --- a/native/src/widget/slider.rs +++ b/native/src/widget/slider.rs @@ -423,8 +423,8 @@ pub fn draw( let handle_offset = if range_start >= range_end { 0.0 } else { - bounds.width * (value - range_start) / (range_end - range_start) - - handle_width / 2.0 + (bounds.width - handle_width) * (value - range_start) + / (range_end - range_start) }; renderer.fill_quad( diff --git a/native/src/widget/vertical_slider.rs b/native/src/widget/vertical_slider.rs index 28e8405ccc..0302f3cf92 100644 --- a/native/src/widget/vertical_slider.rs +++ b/native/src/widget/vertical_slider.rs @@ -418,8 +418,8 @@ pub fn draw( let handle_offset = if range_start >= range_end { 0.0 } else { - bounds.height * (value - range_end) / (range_start - range_end) - - handle_width / 2.0 + (bounds.height - handle_width) * (value - range_end) + / (range_start - range_end) }; renderer.fill_quad( From 1fb413fd8014ad5911a95292bcfbeadb5a58d72f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 21 Feb 2023 20:56:10 +0100 Subject: [PATCH 05/85] Change `subscription::run` to take a function pointer ... and rename the old `run` to `run_with_id`. --- native/src/subscription.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/native/src/subscription.rs b/native/src/subscription.rs index b505f3cc1b..16e78e8296 100644 --- a/native/src/subscription.rs +++ b/native/src/subscription.rs @@ -100,11 +100,24 @@ where }) } +/// Returns a [`Subscription`] that will call the given function to create and +/// asynchronously run the given [`Stream`]. +pub fn run(builder: fn() -> S) -> Subscription +where + S: Stream + MaybeSend + 'static, + Message: 'static, +{ + Subscription::from_recipe(Runner { + id: builder, + spawn: move |_| builder(), + }) +} + /// Returns a [`Subscription`] that will create and asynchronously run the /// given [`Stream`]. /// /// The `id` will be used to uniquely identify the [`Subscription`]. -pub fn run(id: I, stream: S) -> Subscription +pub fn run_with_id(id: I, stream: S) -> Subscription where I: Hash + 'static, S: Stream + MaybeSend + 'static, @@ -199,7 +212,7 @@ where use futures::future::{self, FutureExt}; use futures::stream::StreamExt; - run( + run_with_id( id, futures::stream::unfold(initial, move |state| f(state).map(Some)) .filter_map(future::ready), From 07a7681dba5a9a40627689246d2a84414dfc48d3 Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Thu, 23 Feb 2023 09:31:48 -0800 Subject: [PATCH 06/85] Remove logging large bytes arrays --- glow/src/settings.rs | 2 +- glutin/src/application.rs | 2 +- src/window/icon.rs | 8 +++++++- wgpu/src/settings.rs | 18 +++++++++++++++++- winit/src/application.rs | 2 +- winit/src/settings.rs | 23 ++++++++++++++++++++++- 6 files changed, 49 insertions(+), 6 deletions(-) diff --git a/glow/src/settings.rs b/glow/src/settings.rs index 8ccffbad4f..6aaa0d5515 100644 --- a/glow/src/settings.rs +++ b/glow/src/settings.rs @@ -43,7 +43,7 @@ impl std::fmt::Debug for Settings { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Settings") // Instead of printing the font bytes, we simply show a `bool` indicating if using a default font or not. - .field("default_font", &self.default_font.is_none()) + .field("default_font", &self.default_font.is_some()) .field("default_text_size", &self.default_text_size) .field("text_multithreading", &self.text_multithreading) .field("antialiasing", &self.antialiasing) diff --git a/glutin/src/application.rs b/glutin/src/application.rs index b7bf21c372..5921bdd01a 100644 --- a/glutin/src/application.rs +++ b/glutin/src/application.rs @@ -71,7 +71,7 @@ where settings.id, ); - log::info!("Window builder: {:#?}", builder); + log::debug!("Window builder: {:#?}", builder); let opengl_builder = ContextBuilder::new() .with_vsync(true) diff --git a/src/window/icon.rs b/src/window/icon.rs index d57eb79c5c..659d2b64bb 100644 --- a/src/window/icon.rs +++ b/src/window/icon.rs @@ -6,9 +6,15 @@ use std::io; use std::path::Path; /// The icon of a window. -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct Icon(iced_winit::winit::window::Icon); +impl fmt::Debug for Icon { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("Icon").field(&format_args!("_")).finish() + } +} + impl Icon { /// Creates an icon from 32bpp RGBA data. pub fn from_rgba( diff --git a/wgpu/src/settings.rs b/wgpu/src/settings.rs index fd3b990a38..5ef794997f 100644 --- a/wgpu/src/settings.rs +++ b/wgpu/src/settings.rs @@ -1,10 +1,12 @@ //! Configure a renderer. +use std::fmt; + pub use crate::Antialiasing; /// The settings of a [`Backend`]. /// /// [`Backend`]: crate::Backend -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Clone, Copy, PartialEq)] pub struct Settings { /// The present mode of the [`Backend`]. /// @@ -36,6 +38,20 @@ pub struct Settings { pub antialiasing: Option, } +impl fmt::Debug for Settings { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Settings") + .field("present_mode", &self.present_mode) + .field("internal_backend", &self.internal_backend) + // Instead of printing the font bytes, we simply show a `bool` indicating if using a default font or not. + .field("default_font", &self.default_font.is_some()) + .field("default_text_size", &self.default_text_size) + .field("text_multithreading", &self.text_multithreading) + .field("antialiasing", &self.antialiasing) + .finish() + } +} + impl Settings { /// Creates new [`Settings`] using environment configuration. /// diff --git a/winit/src/application.rs b/winit/src/application.rs index 3fdec6586d..9781a453eb 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -157,7 +157,7 @@ where ) .with_visible(false); - log::info!("Window builder: {:#?}", builder); + log::debug!("Window builder: {:#?}", builder); let window = builder .build(&event_loop) diff --git a/winit/src/settings.rs b/winit/src/settings.rs index 45f388337d..78d580001a 100644 --- a/winit/src/settings.rs +++ b/winit/src/settings.rs @@ -23,9 +23,12 @@ pub use platform::PlatformSpecific; use crate::conversion; use crate::Position; + use winit::monitor::MonitorHandle; use winit::window::WindowBuilder; +use std::fmt; + /// The settings of an application. #[derive(Debug, Clone, Default)] pub struct Settings { @@ -59,7 +62,7 @@ pub struct Settings { } /// The window settings of an application. -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct Window { /// The size of the window. pub size: (u32, u32), @@ -95,6 +98,24 @@ pub struct Window { pub platform_specific: platform::PlatformSpecific, } +impl fmt::Debug for Window { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Window") + .field("size", &self.size) + .field("position", &self.position) + .field("min_size", &self.min_size) + .field("max_size", &self.max_size) + .field("visible", &self.visible) + .field("resizable", &self.resizable) + .field("decorations", &self.decorations) + .field("transparent", &self.transparent) + .field("always_on_top", &self.always_on_top) + .field("icon", &self.icon.is_some()) + .field("platform_specific", &self.platform_specific) + .finish() + } +} + impl Window { /// Converts the window settings into a `WindowBuilder` from `winit`. pub fn into_builder( From 1475f5fa58273e45e67ebd94642ae9e1251fe5f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sat, 25 Feb 2023 15:04:40 +0100 Subject: [PATCH 07/85] Update `resvg` in `iced_graphics` --- graphics/Cargo.toml | 12 ++---------- graphics/src/image/vector.rs | 17 +++++++---------- 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/graphics/Cargo.toml b/graphics/Cargo.toml index 13ab61d80b..a37c99a20e 100644 --- a/graphics/Cargo.toml +++ b/graphics/Cargo.toml @@ -11,7 +11,7 @@ keywords = ["gui", "ui", "graphics", "interface", "widgets"] categories = ["gui"] [features] -svg = ["resvg", "usvg", "tiny-skia"] +svg = ["resvg"] image = ["png", "jpeg", "jpeg_rayon", "gif", "webp", "bmp"] png = ["image_rs/png"] jpeg = ["image_rs/jpeg"] @@ -71,15 +71,7 @@ default-features = false optional = true [dependencies.resvg] -version = "0.18" -optional = true - -[dependencies.usvg] -version = "0.18" -optional = true - -[dependencies.tiny-skia] -version = "0.6" +version = "0.29" optional = true [dependencies.kamadak-exif] diff --git a/graphics/src/image/vector.rs b/graphics/src/image/vector.rs index 82d77aff64..c950ccd63f 100644 --- a/graphics/src/image/vector.rs +++ b/graphics/src/image/vector.rs @@ -5,6 +5,8 @@ use crate::Color; use iced_native::svg; use iced_native::Size; +use resvg::tiny_skia; +use resvg::usvg; use std::collections::{HashMap, HashSet}; use std::fs; @@ -21,7 +23,7 @@ impl Svg { pub fn viewport_dimensions(&self) -> Size { match self { Svg::Loaded(tree) => { - let size = tree.svg_node().size; + let size = tree.size; Size::new(size.width() as u32, size.height() as u32) } @@ -51,20 +53,14 @@ impl Cache { let svg = match handle.data() { svg::Data::Path(path) => { let tree = fs::read_to_string(path).ok().and_then(|contents| { - usvg::Tree::from_str( - &contents, - &usvg::Options::default().to_ref(), - ) - .ok() + usvg::Tree::from_str(&contents, &usvg::Options::default()) + .ok() }); tree.map(Svg::Loaded).unwrap_or(Svg::NotFound) } svg::Data::Bytes(bytes) => { - match usvg::Tree::from_data( - bytes, - &usvg::Options::default().to_ref(), - ) { + match usvg::Tree::from_data(bytes, &usvg::Options::default()) { Ok(tree) => Svg::Loaded(tree), Err(_) => Svg::NotFound, } @@ -125,6 +121,7 @@ impl Cache { } else { usvg::FitTo::Height(height) }, + tiny_skia::Transform::default(), img.as_mut(), )?; From 871b7e0311c73358213aae6326973300f0b56faa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sat, 25 Feb 2023 21:28:21 +0100 Subject: [PATCH 08/85] Fix `Padding::fit` on irregular values for an axis --- core/src/padding.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/core/src/padding.rs b/core/src/padding.rs index 752b2b8699..0b1bba13da 100644 --- a/core/src/padding.rs +++ b/core/src/padding.rs @@ -77,12 +77,14 @@ impl Padding { /// Fits the [`Padding`] between the provided `inner` and `outer` [`Size`]. pub fn fit(self, inner: Size, outer: Size) -> Self { let available = (outer - inner).max(Size::ZERO); + let new_top = self.top.min(available.height); + let new_left = self.left.min(available.width); Padding { - top: self.top.min(available.height / 2.0), - right: self.right.min(available.width / 2.0), - bottom: self.bottom.min(available.height / 2.0), - left: self.left.min(available.width / 2.0), + top: new_top, + bottom: self.bottom.min(available.height - new_top), + left: new_left, + right: self.right.min(available.width - new_left), } } } From 5f93437285c4451a8b5ffac1b3c9e1b91e8a5918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sat, 25 Feb 2023 21:43:53 +0100 Subject: [PATCH 09/85] Bump version of `iced_core` :tada: --- core/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index 43865e4d83..0d6310d3bd 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iced_core" -version = "0.8.0" +version = "0.8.1" authors = ["Héctor Ramón Jiménez "] edition = "2021" description = "The essential concepts of Iced" From 9e815cb749f1bf6bce0232e870be266aca0c0742 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 27 Feb 2023 16:49:25 +0100 Subject: [PATCH 10/85] Remove `Fill` variant for `Alignment` Implementing this generically in our `flex` logic has an exponential cost. Let's explore other options! --- core/src/alignment.rs | 3 -- examples/component/src/main.rs | 5 ++- examples/scrollable/src/main.rs | 1 - examples/websocket/src/main.rs | 4 +- native/src/layout/flex.rs | 67 ++++----------------------------- native/src/layout/node.rs | 6 --- 6 files changed, 14 insertions(+), 72 deletions(-) diff --git a/core/src/alignment.rs b/core/src/alignment.rs index 73f41d3fab..51b7fca9da 100644 --- a/core/src/alignment.rs +++ b/core/src/alignment.rs @@ -11,9 +11,6 @@ pub enum Alignment { /// Align at the end of the axis. End, - - /// Fill the entire axis. - Fill, } impl From for Alignment { diff --git a/examples/component/src/main.rs b/examples/component/src/main.rs index c407bb06c6..bbf549e757 100644 --- a/examples/component/src/main.rs +++ b/examples/component/src/main.rs @@ -127,7 +127,8 @@ mod numeric_input { .horizontal_alignment(alignment::Horizontal::Center) .vertical_alignment(alignment::Vertical::Center), ) - .width(50) + .width(40) + .height(40) .on_press(on_press) }; @@ -145,7 +146,7 @@ mod numeric_input { .padding(10), button("+", Event::IncrementPressed), ] - .align_items(Alignment::Fill) + .align_items(Alignment::Center) .spacing(10) .into() } diff --git a/examples/scrollable/src/main.rs b/examples/scrollable/src/main.rs index 7c85896147..a3ade54fb9 100644 --- a/examples/scrollable/src/main.rs +++ b/examples/scrollable/src/main.rs @@ -254,7 +254,6 @@ impl Application for ScrollableDemo { scroll_to_beginning_button(), vertical_space(40), ] - .align_items(Alignment::Fill) .spacing(40), horizontal_space(1200), text("Horizontal - End!"), diff --git a/examples/websocket/src/main.rs b/examples/websocket/src/main.rs index ccd9c81582..e617b8ce2b 100644 --- a/examples/websocket/src/main.rs +++ b/examples/websocket/src/main.rs @@ -146,7 +146,9 @@ impl Application for WebSocket { } } - row![input, button].spacing(10).align_items(Alignment::Fill) + row![input, button] + .spacing(10) + .align_items(Alignment::Center) }; column![message_log, new_message_input] diff --git a/native/src/layout/flex.rs b/native/src/layout/flex.rs index 5d70c2fc13..8b9678497b 100644 --- a/native/src/layout/flex.rs +++ b/native/src/layout/flex.rs @@ -81,32 +81,6 @@ where let mut nodes: Vec = Vec::with_capacity(items.len()); nodes.resize(items.len(), Node::default()); - if align_items == Alignment::Fill { - let mut fill_cross = axis.cross(limits.min()); - - items.iter().for_each(|child| { - let cross_fill_factor = match axis { - Axis::Horizontal => child.as_widget().height(), - Axis::Vertical => child.as_widget().width(), - } - .fill_factor(); - - if cross_fill_factor == 0 { - let (max_width, max_height) = axis.pack(available, max_cross); - - let child_limits = - Limits::new(Size::ZERO, Size::new(max_width, max_height)); - - let layout = child.as_widget().layout(renderer, &child_limits); - let size = layout.size(); - - fill_cross = fill_cross.max(axis.cross(size)); - } - }); - - cross = fill_cross; - } - for (i, child) in items.iter().enumerate() { let fill_factor = match axis { Axis::Horizontal => child.as_widget().width(), @@ -115,31 +89,16 @@ where .fill_factor(); if fill_factor == 0 { - let (min_width, min_height) = if align_items == Alignment::Fill { - axis.pack(0.0, cross) - } else { - axis.pack(0.0, 0.0) - }; + let (max_width, max_height) = axis.pack(available, max_cross); - let (max_width, max_height) = if align_items == Alignment::Fill { - axis.pack(available, cross) - } else { - axis.pack(available, max_cross) - }; - - let child_limits = Limits::new( - Size::new(min_width, min_height), - Size::new(max_width, max_height), - ); + let child_limits = + Limits::new(Size::ZERO, Size::new(max_width, max_height)); let layout = child.as_widget().layout(renderer, &child_limits); let size = layout.size(); available -= axis.main(size); - - if align_items != Alignment::Fill { - cross = cross.max(axis.cross(size)); - } + cross = cross.max(axis.cross(size)); nodes[i] = layout; } else { @@ -164,17 +123,10 @@ where max_main }; - let (min_width, min_height) = if align_items == Alignment::Fill { - axis.pack(min_main, cross) - } else { - axis.pack(min_main, axis.cross(limits.min())) - }; + let (min_width, min_height) = + axis.pack(min_main, axis.cross(limits.min())); - let (max_width, max_height) = if align_items == Alignment::Fill { - axis.pack(max_main, cross) - } else { - axis.pack(max_main, max_cross) - }; + let (max_width, max_height) = axis.pack(max_main, max_cross); let child_limits = Limits::new( Size::new(min_width, min_height), @@ -182,10 +134,7 @@ where ); let layout = child.as_widget().layout(renderer, &child_limits); - - if align_items != Alignment::Fill { - cross = cross.max(axis.cross(layout.size())); - } + cross = cross.max(axis.cross(layout.size())); nodes[i] = layout; } diff --git a/native/src/layout/node.rs b/native/src/layout/node.rs index e0c7dcb2fd..2b44a7d549 100644 --- a/native/src/layout/node.rs +++ b/native/src/layout/node.rs @@ -56,9 +56,6 @@ impl Node { Alignment::End => { self.bounds.x += space.width - self.bounds.width; } - Alignment::Fill => { - self.bounds.width = space.width; - } } match vertical_alignment { @@ -69,9 +66,6 @@ impl Node { Alignment::End => { self.bounds.y += space.height - self.bounds.height; } - Alignment::Fill => { - self.bounds.height = space.height; - } } } From b2a9a1e73cb7b2026b2eeaa2be2c04a61c5efb21 Mon Sep 17 00:00:00 2001 From: Bingus Date: Thu, 2 Mar 2023 08:31:39 -0800 Subject: [PATCH 11/85] Fixed fullscreen only being possible on primary monitor. --- winit/src/application.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/winit/src/application.rs b/winit/src/application.rs index 9781a453eb..b13b7214ea 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -762,7 +762,7 @@ pub fn run_command( window::Action::ChangeMode(mode) => { window.set_visible(conversion::visible(mode)); window.set_fullscreen(conversion::fullscreen( - window.primary_monitor(), + window.current_monitor(), mode, )); } From a9ca89ca55157d7e94dc6422b4842826139ca2db Mon Sep 17 00:00:00 2001 From: Bingus Date: Thu, 2 Mar 2023 08:43:58 -0800 Subject: [PATCH 12/85] Added example of toggling fullscreen to TODOs. --- examples/todos/src/main.rs | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs index 6408f09c0b..0f5bfe3003 100644 --- a/examples/todos/src/main.rs +++ b/examples/todos/src/main.rs @@ -1,6 +1,6 @@ use iced::alignment::{self, Alignment}; use iced::event::{self, Event}; -use iced::keyboard; +use iced::keyboard::{self, KeyCode, Modifiers}; use iced::subscription; use iced::theme::{self, Theme}; use iced::widget::{ @@ -8,6 +8,8 @@ use iced::widget::{ text_input, Text, }; use iced::window; +#[cfg(not(target_arch = "wasm32"))] +use iced::window::Mode; use iced::{Application, Element}; use iced::{Color, Command, Font, Length, Settings, Subscription}; @@ -49,7 +51,11 @@ enum Message { CreateTask, FilterChanged(Filter), TaskMessage(usize, TaskMessage), - TabPressed { shift: bool }, + TabPressed { + shift: bool, + }, + #[cfg(not(target_arch = "wasm32"))] + ToggleFullscreen(Mode), } impl Application for Todos { @@ -156,6 +162,10 @@ impl Application for Todos { widget::focus_next() } } + #[cfg(not(target_arch = "wasm32"))] + Message::ToggleFullscreen(mode) => { + window::change_mode(mode) + } _ => Command::none(), }; @@ -266,6 +276,22 @@ impl Application for Todos { ) => Some(Message::TabPressed { shift: modifiers.shift(), }), + #[cfg(not(target_arch = "wasm32"))] + ( + Event::Keyboard(keyboard::Event::KeyPressed { + key_code, + modifiers: Modifiers::SHIFT, + }), + event::Status::Ignored, + ) => match key_code { + KeyCode::Up => { + Some(Message::ToggleFullscreen(Mode::Fullscreen)) + } + KeyCode::Down => { + Some(Message::ToggleFullscreen(Mode::Windowed)) + } + _ => None, + }, _ => None, }) } From 12781c717a08bf0e7bfb2594e568f89af3676d52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Fri, 3 Mar 2023 20:45:49 +0100 Subject: [PATCH 13/85] Expose `window` commands for Wasm builds --- examples/todos/src/main.rs | 15 ++++----------- src/window.rs | 1 - 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs index 0f5bfe3003..6a87f58ce4 100644 --- a/examples/todos/src/main.rs +++ b/examples/todos/src/main.rs @@ -8,8 +8,6 @@ use iced::widget::{ text_input, Text, }; use iced::window; -#[cfg(not(target_arch = "wasm32"))] -use iced::window::Mode; use iced::{Application, Element}; use iced::{Color, Command, Font, Length, Settings, Subscription}; @@ -51,11 +49,8 @@ enum Message { CreateTask, FilterChanged(Filter), TaskMessage(usize, TaskMessage), - TabPressed { - shift: bool, - }, - #[cfg(not(target_arch = "wasm32"))] - ToggleFullscreen(Mode), + TabPressed { shift: bool }, + ToggleFullscreen(window::Mode), } impl Application for Todos { @@ -162,7 +157,6 @@ impl Application for Todos { widget::focus_next() } } - #[cfg(not(target_arch = "wasm32"))] Message::ToggleFullscreen(mode) => { window::change_mode(mode) } @@ -276,7 +270,6 @@ impl Application for Todos { ) => Some(Message::TabPressed { shift: modifiers.shift(), }), - #[cfg(not(target_arch = "wasm32"))] ( Event::Keyboard(keyboard::Event::KeyPressed { key_code, @@ -285,10 +278,10 @@ impl Application for Todos { event::Status::Ignored, ) => match key_code { KeyCode::Up => { - Some(Message::ToggleFullscreen(Mode::Fullscreen)) + Some(Message::ToggleFullscreen(window::Mode::Fullscreen)) } KeyCode::Down => { - Some(Message::ToggleFullscreen(Mode::Windowed)) + Some(Message::ToggleFullscreen(window::Mode::Windowed)) } _ => None, }, diff --git a/src/window.rs b/src/window.rs index 2018053fbb..5a199580bf 100644 --- a/src/window.rs +++ b/src/window.rs @@ -8,5 +8,4 @@ pub use icon::Icon; pub use position::Position; pub use settings::Settings; -#[cfg(not(target_arch = "wasm32"))] pub use crate::runtime::window::*; From df68cca0c9dda051ae979ccc2f5ca8d37c3e3cb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 7 Mar 2023 07:22:48 +0100 Subject: [PATCH 14/85] Update `sysinfo` to `0.28` --- winit/Cargo.toml | 2 +- winit/src/system.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/winit/Cargo.toml b/winit/Cargo.toml index 60e464c6ca..dd5c12c27d 100644 --- a/winit/Cargo.toml +++ b/winit/Cargo.toml @@ -65,5 +65,5 @@ version = "0.3" features = ["Document", "Window"] [dependencies.sysinfo] -version = "0.23" +version = "0.28" optional = true diff --git a/winit/src/system.rs b/winit/src/system.rs index 619086b80a..8d8b018c71 100644 --- a/winit/src/system.rs +++ b/winit/src/system.rs @@ -16,11 +16,11 @@ pub fn fetch_information( pub(crate) fn information( graphics_info: compositor::Information, ) -> Information { - use sysinfo::{ProcessExt, ProcessorExt, System, SystemExt}; + use sysinfo::{CpuExt, ProcessExt, System, SystemExt}; let mut system = System::new_all(); system.refresh_all(); - let cpu = system.global_processor_info(); + let cpu = system.global_cpu_info(); let memory_used = sysinfo::get_current_pid() .and_then(|pid| system.process(pid).ok_or("Process not found")) From 046f3596ca88c1ad4c2941f2f74007684920e5d5 Mon Sep 17 00:00:00 2001 From: FinnPerry Date: Wed, 8 Mar 2023 11:33:13 +1100 Subject: [PATCH 15/85] fix: add `width` to scrollables --- native/src/widget/scrollable.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index c1df8c39dc..45cd2a0e5f 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -33,6 +33,7 @@ where Renderer::Theme: StyleSheet, { id: Option, + width: Length, height: Length, vertical: Properties, horizontal: Option, @@ -50,6 +51,7 @@ where pub fn new(content: impl Into>) -> Self { Scrollable { id: None, + width: Length::Shrink, height: Length::Shrink, vertical: Properties::default(), horizontal: None, @@ -65,6 +67,12 @@ where self } + /// Sets the width of the [`Scrollable`]. + pub fn width(mut self, width: impl Into) -> Self { + self.width = width.into(); + self + } + /// Sets the height of the [`Scrollable`]. pub fn height(mut self, height: impl Into) -> Self { self.height = height.into(); @@ -173,7 +181,7 @@ where } fn width(&self) -> Length { - self.content.as_widget().width() + self.width } fn height(&self) -> Length { @@ -188,7 +196,7 @@ where layout( renderer, limits, - Widget::::width(self), + self.width, self.height, self.horizontal.is_some(), |renderer, limits| { From 782c080bd098dd56e74a3c8adabf09bf5dae047b Mon Sep 17 00:00:00 2001 From: lupd <93457935+lupd@users.noreply.github.com> Date: Wed, 8 Mar 2023 12:55:52 -0500 Subject: [PATCH 16/85] Use correct package name in checkbox example docs --- examples/checkbox/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/checkbox/README.md b/examples/checkbox/README.md index b7f8568447..76e6764c45 100644 --- a/examples/checkbox/README.md +++ b/examples/checkbox/README.md @@ -6,7 +6,7 @@ The __[`main`]__ file contains all the code of the example. You can run it with `cargo run`: ``` -cargo run --package pick_list +cargo run --package checkbox ``` [`main`]: src/main.rs From 1816c985fad2ead8dc1e7b62dc8e4bafeed856b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 14 Mar 2023 11:11:17 +0100 Subject: [PATCH 17/85] Fix `clippy` lints for Rust 1.68 --- core/src/font.rs | 9 ++------- core/src/mouse/interaction.rs | 9 ++------- examples/pick_list/src/main.rs | 9 ++------- examples/todos/src/main.rs | 11 ++++------- graphics/src/primitive.rs | 9 ++------- graphics/src/widget/canvas/cache.rs | 7 ++----- graphics/src/widget/canvas/stroke.rs | 18 ++++-------------- 7 files changed, 18 insertions(+), 54 deletions(-) diff --git a/core/src/font.rs b/core/src/font.rs index 3f9ad2b5a4..d8c34e5a86 100644 --- a/core/src/font.rs +++ b/core/src/font.rs @@ -1,10 +1,11 @@ /// A font. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Default)] pub enum Font { /// The default font. /// /// This is normally a font configured in a renderer or loaded from the /// system. + #[default] Default, /// An external font. @@ -16,9 +17,3 @@ pub enum Font { bytes: &'static [u8], }, } - -impl Default for Font { - fn default() -> Font { - Font::Default - } -} diff --git a/core/src/mouse/interaction.rs b/core/src/mouse/interaction.rs index 664147a71f..57da93fe02 100644 --- a/core/src/mouse/interaction.rs +++ b/core/src/mouse/interaction.rs @@ -1,7 +1,8 @@ /// The interaction of a mouse cursor. -#[derive(Debug, Eq, PartialEq, Clone, Copy, PartialOrd, Ord)] +#[derive(Debug, Eq, PartialEq, Clone, Copy, PartialOrd, Ord, Default)] #[allow(missing_docs)] pub enum Interaction { + #[default] Idle, Pointer, Grab, @@ -12,9 +13,3 @@ pub enum Interaction { ResizingHorizontally, ResizingVertically, } - -impl Default for Interaction { - fn default() -> Interaction { - Interaction::Idle - } -} diff --git a/examples/pick_list/src/main.rs b/examples/pick_list/src/main.rs index 62a4ef880a..2120062151 100644 --- a/examples/pick_list/src/main.rs +++ b/examples/pick_list/src/main.rs @@ -61,8 +61,9 @@ impl Sandbox for Example { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub enum Language { + #[default] Rust, Elm, Ruby, @@ -84,12 +85,6 @@ impl Language { ]; } -impl Default for Language { - fn default() -> Language { - Language::Rust - } -} - impl std::fmt::Display for Language { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs index 6a87f58ce4..6361667eb6 100644 --- a/examples/todos/src/main.rs +++ b/examples/todos/src/main.rs @@ -435,19 +435,16 @@ fn view_controls(tasks: &[Task], current_filter: Filter) -> Element { .into() } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[derive( + Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize, +)] pub enum Filter { + #[default] All, Active, Completed, } -impl Default for Filter { - fn default() -> Self { - Filter::All - } -} - impl Filter { fn matches(&self, task: &Task) -> bool { match self { diff --git a/graphics/src/primitive.rs b/graphics/src/primitive.rs index 5a163a2f5f..cef422a2ae 100644 --- a/graphics/src/primitive.rs +++ b/graphics/src/primitive.rs @@ -9,9 +9,10 @@ use crate::triangle; use std::sync::Arc; /// A rendering primitive. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] pub enum Primitive { /// An empty primitive + #[default] None, /// A group of primitives Group { @@ -117,9 +118,3 @@ pub enum Primitive { cache: Arc, }, } - -impl Default for Primitive { - fn default() -> Primitive { - Primitive::None - } -} diff --git a/graphics/src/widget/canvas/cache.rs b/graphics/src/widget/canvas/cache.rs index 52217bbbe0..678b0f927b 100644 --- a/graphics/src/widget/canvas/cache.rs +++ b/graphics/src/widget/canvas/cache.rs @@ -4,7 +4,9 @@ use crate::Primitive; use iced_native::Size; use std::{cell::RefCell, sync::Arc}; +#[derive(Default)] enum State { + #[default] Empty, Filled { bounds: Size, @@ -12,11 +14,6 @@ enum State { }, } -impl Default for State { - fn default() -> Self { - State::Empty - } -} /// A simple cache that stores generated [`Geometry`] to avoid recomputation. /// /// A [`Cache`] will not redraw its geometry unless the dimensions of its layer diff --git a/graphics/src/widget/canvas/stroke.rs b/graphics/src/widget/canvas/stroke.rs index 4c19251d79..49f5701cee 100644 --- a/graphics/src/widget/canvas/stroke.rs +++ b/graphics/src/widget/canvas/stroke.rs @@ -59,9 +59,10 @@ impl<'a> Default for Stroke<'a> { } /// The shape used at the end of open subpaths when they are stroked. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Default)] pub enum LineCap { /// The stroke for each sub-path does not extend beyond its two endpoints. + #[default] Butt, /// At the end of each sub-path, the shape representing the stroke will be /// extended by a square. @@ -71,12 +72,6 @@ pub enum LineCap { Round, } -impl Default for LineCap { - fn default() -> LineCap { - LineCap::Butt - } -} - impl From for lyon::tessellation::LineCap { fn from(line_cap: LineCap) -> lyon::tessellation::LineCap { match line_cap { @@ -89,9 +84,10 @@ impl From for lyon::tessellation::LineCap { /// The shape used at the corners of paths or basic shapes when they are /// stroked. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Default)] pub enum LineJoin { /// A sharp corner. + #[default] Miter, /// A round corner. Round, @@ -99,12 +95,6 @@ pub enum LineJoin { Bevel, } -impl Default for LineJoin { - fn default() -> LineJoin { - LineJoin::Miter - } -} - impl From for lyon::tessellation::LineJoin { fn from(line_join: LineJoin) -> lyon::tessellation::LineJoin { match line_join { From ed7b61380473c7c88c25b5d4d17112b844a9364d Mon Sep 17 00:00:00 2001 From: Casper Storm Date: Thu, 23 Feb 2023 14:33:53 +0100 Subject: [PATCH 18/85] Added macOS platform specific options --- src/window.rs | 2 +- src/window/settings.rs | 18 +++++++++++++++++- src/window/settings/macos.rs | 20 ++++++++++++++++++++ src/window/settings/other.rs | 9 +++++++++ 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 src/window/settings/macos.rs create mode 100644 src/window/settings/other.rs diff --git a/src/window.rs b/src/window.rs index 5a199580bf..aba4bce881 100644 --- a/src/window.rs +++ b/src/window.rs @@ -6,6 +6,6 @@ pub mod icon; pub use icon::Icon; pub use position::Position; -pub use settings::Settings; +pub use settings::{PlatformSpecific, Settings}; pub use crate::runtime::window::*; diff --git a/src/window/settings.rs b/src/window/settings.rs index 24d0f4f9ec..a039e213a5 100644 --- a/src/window/settings.rs +++ b/src/window/settings.rs @@ -1,5 +1,15 @@ use crate::window::{Icon, Position}; +#[cfg(target_os = "macos")] +#[path = "settings/macos.rs"] +mod platform; + +#[cfg(not(target_os = "macos"))] +#[path = "settings/other.rs"] +mod platform; + +pub use platform::PlatformSpecific; + /// The window settings of an application. #[derive(Debug, Clone)] pub struct Settings { @@ -32,6 +42,9 @@ pub struct Settings { /// The icon of the window. pub icon: Option, + + /// Platform specific settings. + pub platform_specific: platform::PlatformSpecific, } impl Default for Settings { @@ -47,6 +60,7 @@ impl Default for Settings { transparent: false, always_on_top: false, icon: None, + platform_specific: Default::default(), } } } @@ -64,7 +78,9 @@ impl From for iced_winit::settings::Window { transparent: settings.transparent, always_on_top: settings.always_on_top, icon: settings.icon.map(Icon::into), - platform_specific: Default::default(), + platform_specific: iced_winit::settings::PlatformSpecific::from( + settings.platform_specific, + ), } } } diff --git a/src/window/settings/macos.rs b/src/window/settings/macos.rs new file mode 100644 index 0000000000..c08020cf80 --- /dev/null +++ b/src/window/settings/macos.rs @@ -0,0 +1,20 @@ +/// The platform specific window settings of an application. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub struct PlatformSpecific { + /// Hides the window title. + pub title_hidden: bool, + /// Makes the titlebar transparent and allows the content to appear behind it. + pub titlebar_transparent: bool, + /// Makes the window content appear behind the titlebar. + pub fullsize_content_view: bool, +} + +impl From for iced_winit::settings::PlatformSpecific { + fn from(platform_specific: PlatformSpecific) -> Self { + Self { + title_hidden: platform_specific.title_hidden, + titlebar_transparent: platform_specific.titlebar_transparent, + fullsize_content_view: platform_specific.fullsize_content_view, + } + } +} diff --git a/src/window/settings/other.rs b/src/window/settings/other.rs new file mode 100644 index 0000000000..943d9f0de4 --- /dev/null +++ b/src/window/settings/other.rs @@ -0,0 +1,9 @@ +/// The platform specific window settings of an application. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub struct PlatformSpecific; + +impl From for iced_winit::settings::PlatformSpecific { + fn from(_: PlatformSpecific) -> Self { + Default::default() + } +} From 4405a3d483286faf962930ca3b294b34f86bd2f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 14 Mar 2023 11:22:06 +0100 Subject: [PATCH 19/85] Re-export `settings::PlatformSpecific` from `iced_winit` directly --- src/window/settings.rs | 16 +++------------- src/window/settings/macos.rs | 20 -------------------- src/window/settings/other.rs | 9 --------- 3 files changed, 3 insertions(+), 42 deletions(-) delete mode 100644 src/window/settings/macos.rs delete mode 100644 src/window/settings/other.rs diff --git a/src/window/settings.rs b/src/window/settings.rs index a039e213a5..3c8da62ffe 100644 --- a/src/window/settings.rs +++ b/src/window/settings.rs @@ -1,14 +1,6 @@ use crate::window::{Icon, Position}; -#[cfg(target_os = "macos")] -#[path = "settings/macos.rs"] -mod platform; - -#[cfg(not(target_os = "macos"))] -#[path = "settings/other.rs"] -mod platform; - -pub use platform::PlatformSpecific; +pub use iced_winit::settings::PlatformSpecific; /// The window settings of an application. #[derive(Debug, Clone)] @@ -44,7 +36,7 @@ pub struct Settings { pub icon: Option, /// Platform specific settings. - pub platform_specific: platform::PlatformSpecific, + pub platform_specific: PlatformSpecific, } impl Default for Settings { @@ -78,9 +70,7 @@ impl From for iced_winit::settings::Window { transparent: settings.transparent, always_on_top: settings.always_on_top, icon: settings.icon.map(Icon::into), - platform_specific: iced_winit::settings::PlatformSpecific::from( - settings.platform_specific, - ), + platform_specific: settings.platform_specific, } } } diff --git a/src/window/settings/macos.rs b/src/window/settings/macos.rs deleted file mode 100644 index c08020cf80..0000000000 --- a/src/window/settings/macos.rs +++ /dev/null @@ -1,20 +0,0 @@ -/// The platform specific window settings of an application. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] -pub struct PlatformSpecific { - /// Hides the window title. - pub title_hidden: bool, - /// Makes the titlebar transparent and allows the content to appear behind it. - pub titlebar_transparent: bool, - /// Makes the window content appear behind the titlebar. - pub fullsize_content_view: bool, -} - -impl From for iced_winit::settings::PlatformSpecific { - fn from(platform_specific: PlatformSpecific) -> Self { - Self { - title_hidden: platform_specific.title_hidden, - titlebar_transparent: platform_specific.titlebar_transparent, - fullsize_content_view: platform_specific.fullsize_content_view, - } - } -} diff --git a/src/window/settings/other.rs b/src/window/settings/other.rs deleted file mode 100644 index 943d9f0de4..0000000000 --- a/src/window/settings/other.rs +++ /dev/null @@ -1,9 +0,0 @@ -/// The platform specific window settings of an application. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] -pub struct PlatformSpecific; - -impl From for iced_winit::settings::PlatformSpecific { - fn from(_: PlatformSpecific) -> Self { - Default::default() - } -} From a3f6b782a16a2bacdedccb4f6919e952ca28368d Mon Sep 17 00:00:00 2001 From: Nick Senger Date: Wed, 8 Feb 2023 23:17:05 -0800 Subject: [PATCH 20/85] optimization: reduce unnecessary rebuilds in `Component` --- lazy/src/component.rs | 236 ++++++++++++++++++++++++++++-------------- 1 file changed, 157 insertions(+), 79 deletions(-) diff --git a/lazy/src/component.rs b/lazy/src/component.rs index b23da9f7b4..f6c331b9d7 100644 --- a/lazy/src/component.rs +++ b/lazy/src/component.rs @@ -13,6 +13,7 @@ use iced_native::{ use ouroboros::self_referencing; use std::cell::RefCell; use std::marker::PhantomData; +use std::rc::Rc; /// A reusable, custom widget that uses The Elm Architecture. /// @@ -58,6 +59,8 @@ pub trait Component { } } +struct Tag(T); + /// Turns an implementor of [`Component`] into an [`Element`] that can be /// embedded in any application. pub fn view<'a, C, Message, Renderer>( @@ -79,11 +82,13 @@ where } .build(), )), + tree: RefCell::new(Rc::new(RefCell::new(None))), }) } struct Instance<'a, Message, Renderer, Event, S> { state: RefCell>>, + tree: RefCell>>>, } #[self_referencing] @@ -100,40 +105,91 @@ struct State<'a, Message: 'a, Renderer: 'a, Event: 'a, S: 'a> { impl<'a, Message, Renderer, Event, S> Instance<'a, Message, Renderer, Event, S> where - S: Default, + S: Default + 'static, + Renderer: iced_native::Renderer, { - fn rebuild_element(&self, state: &S) { - let heads = self.state.borrow_mut().take().unwrap().into_heads(); + fn diff_self(&self) { + self.with_element(|element| { + self.tree + .borrow_mut() + .borrow_mut() + .as_mut() + .unwrap() + .diff_children(std::slice::from_ref(&element)); + }); + } - *self.state.borrow_mut() = Some( - StateBuilder { - component: heads.component, - message: PhantomData, - state: PhantomData, - element_builder: |component| Some(component.view(state)), - } - .build(), - ); + fn rebuild_element_if_necessary(&self) { + let inner = self.state.borrow_mut().take().unwrap(); + if inner.borrow_element().is_none() { + let heads = inner.into_heads(); + + *self.state.borrow_mut() = Some( + StateBuilder { + component: heads.component, + message: PhantomData, + state: PhantomData, + element_builder: |component| { + Some( + component.view( + self.tree + .borrow() + .borrow() + .as_ref() + .unwrap() + .state + .downcast_ref::(), + ), + ) + }, + } + .build(), + ); + self.diff_self(); + } else { + *self.state.borrow_mut() = Some(inner); + } } fn rebuild_element_with_operation( &self, - state: &mut S, operation: &mut dyn widget::Operation, ) { let heads = self.state.borrow_mut().take().unwrap().into_heads(); - heads.component.operate(state, operation); + heads.component.operate( + self.tree + .borrow_mut() + .borrow_mut() + .as_mut() + .unwrap() + .state + .downcast_mut(), + operation, + ); *self.state.borrow_mut() = Some( StateBuilder { component: heads.component, message: PhantomData, state: PhantomData, - element_builder: |component| Some(component.view(state)), + element_builder: |component| { + Some( + component.view( + self.tree + .borrow() + .borrow() + .as_ref() + .unwrap() + .state + .downcast_ref(), + ), + ) + }, } .build(), ); + self.diff_self(); } fn with_element( @@ -147,6 +203,7 @@ where &self, f: impl FnOnce(&mut Element<'_, Event, Renderer>) -> T, ) -> T { + self.rebuild_element_if_necessary(); self.state .borrow_mut() .as_mut() @@ -162,24 +219,27 @@ where Renderer: iced_native::Renderer, { fn tag(&self) -> tree::Tag { - struct Tag(T); tree::Tag::of::>() } fn state(&self) -> tree::State { - tree::State::new(S::default()) + let state = Rc::new(RefCell::new(Some(Tree { + tag: tree::Tag::of::>(), + state: tree::State::new(S::default()), + children: vec![Tree::empty()], + }))); + *self.tree.borrow_mut() = state.clone(); + tree::State::new(state) } fn children(&self) -> Vec { - self.rebuild_element(&S::default()); - self.with_element(|element| vec![Tree::new(element)]) + vec![] } fn diff(&self, tree: &mut Tree) { - self.rebuild_element(tree.state.downcast_ref()); - self.with_element(|element| { - tree.diff_children(std::slice::from_ref(&element)) - }) + let tree = tree.state.downcast_ref::>>>(); + *self.tree.borrow_mut() = tree.clone(); + self.rebuild_element_if_necessary(); } fn width(&self) -> Length { @@ -213,9 +273,10 @@ where let mut local_messages = Vec::new(); let mut local_shell = Shell::new(&mut local_messages); + let t = tree.state.downcast_mut::>>>(); let event_status = self.with_element_mut(|element| { element.as_widget_mut().on_event( - &mut tree.children[0], + &mut t.borrow_mut().as_mut().unwrap().children[0], event, layout, cursor_position, @@ -235,9 +296,10 @@ where let mut heads = self.state.take().unwrap().into_heads(); for message in local_messages.into_iter().filter_map(|message| { - heads - .component - .update(tree.state.downcast_mut::(), message) + heads.component.update( + t.borrow_mut().as_mut().unwrap().state.downcast_mut(), + message, + ) }) { shell.publish(message); } @@ -247,17 +309,11 @@ where component: heads.component, message: PhantomData, state: PhantomData, - element_builder: |state| { - Some(state.view(tree.state.downcast_ref::())) - }, + element_builder: |_| None, } .build(), )); - self.with_element(|element| { - tree.diff_children(std::slice::from_ref(&element)) - }); - shell.invalidate_layout(); } @@ -271,10 +327,7 @@ where renderer: &Renderer, operation: &mut dyn widget::Operation, ) { - self.rebuild_element_with_operation( - tree.state.downcast_mut(), - operation, - ); + self.rebuild_element_with_operation(operation); struct MapOperation<'a, B> { operation: &'a mut dyn widget::Operation, @@ -310,11 +363,10 @@ where } } + let tree = tree.state.downcast_mut::>>>(); self.with_element(|element| { - tree.diff_children(std::slice::from_ref(&element)); - element.as_widget().operate( - &mut tree.children[0], + &mut tree.borrow_mut().as_mut().unwrap().children[0], layout, renderer, &mut MapOperation { operation }, @@ -332,9 +384,10 @@ where cursor_position: Point, viewport: &Rectangle, ) { + let tree = tree.state.downcast_ref::>>>(); self.with_element(|element| { element.as_widget().draw( - &tree.children[0], + &tree.borrow().as_ref().unwrap().children[0], renderer, theme, style, @@ -353,9 +406,10 @@ where viewport: &Rectangle, renderer: &Renderer, ) -> mouse::Interaction { + let tree = tree.state.downcast_ref::>>>(); self.with_element(|element| { element.as_widget().mouse_interaction( - &tree.children[0], + &tree.borrow().as_ref().unwrap().children[0], layout, cursor_position, viewport, @@ -370,25 +424,34 @@ where layout: Layout<'_>, renderer: &Renderer, ) -> Option> { - let overlay = OverlayBuilder { - instance: self, - tree, - types: PhantomData, - overlay_builder: |instance, tree| { - instance.state.get_mut().as_mut().unwrap().with_element_mut( - move |element| { - element.as_mut().unwrap().as_widget_mut().overlay( - &mut tree.children[0], - layout, - renderer, - ) - }, - ) - }, - } - .build(); + self.rebuild_element_if_necessary(); + let tree = tree + .state + .downcast_mut::>>>() + .borrow_mut() + .take() + .unwrap(); + let overlay = Overlay(Some( + InnerBuilder { + instance: self, + tree, + types: PhantomData, + overlay_builder: |instance, tree| { + instance.state.get_mut().as_mut().unwrap().with_element_mut( + move |element| { + element.as_mut().unwrap().as_widget_mut().overlay( + &mut tree.children[0], + layout, + renderer, + ) + }, + ) + }, + } + .build(), + )); - let has_overlay = overlay.with_overlay(|overlay| { + let has_overlay = overlay.0.as_ref().unwrap().with_overlay(|overlay| { overlay.as_ref().map(overlay::Element::position) }); @@ -403,10 +466,24 @@ where } } +struct Overlay<'a, 'b, Message, Renderer, Event, S>( + Option>, +); + +impl<'a, 'b, Message, Renderer, Event, S> Drop + for Overlay<'a, 'b, Message, Renderer, Event, S> +{ + fn drop(&mut self) { + if let Some(heads) = self.0.take().map(|inner| inner.into_heads()) { + *heads.instance.tree.borrow_mut().borrow_mut() = Some(heads.tree); + } + } +} + #[self_referencing] -struct Overlay<'a, 'b, Message, Renderer, Event, S> { +struct Inner<'a, 'b, Message, Renderer, Event, S> { instance: &'a mut Instance<'b, Message, Renderer, Event, S>, - tree: &'a mut Tree, + tree: Tree, types: PhantomData<(Message, Event, S)>, #[borrows(mut instance, mut tree)] @@ -426,6 +503,9 @@ impl<'a, 'b, Message, Renderer, Event, S> f: impl FnOnce(&overlay::Element<'_, Event, Renderer>) -> T, ) -> Option { self.overlay + .as_ref() + .unwrap() + .0 .as_ref() .unwrap() .borrow_overlay() @@ -438,6 +518,9 @@ impl<'a, 'b, Message, Renderer, Event, S> f: impl FnOnce(&mut overlay::Element<'_, Event, Renderer>) -> T, ) -> Option { self.overlay + .as_mut() + .unwrap() + .0 .as_mut() .unwrap() .with_overlay_mut(|overlay| overlay.as_mut().map(f)) @@ -523,42 +606,37 @@ where local_shell.revalidate_layout(|| shell.invalidate_layout()); if !local_messages.is_empty() { - let overlay = self.overlay.take().unwrap().into_heads(); - let mut heads = overlay.instance.state.take().unwrap().into_heads(); + let mut inner = + self.overlay.take().unwrap().0.take().unwrap().into_heads(); + let mut heads = inner.instance.state.take().unwrap().into_heads(); for message in local_messages.into_iter().filter_map(|message| { heads .component - .update(overlay.tree.state.downcast_mut::(), message) + .update(inner.tree.state.downcast_mut(), message) }) { shell.publish(message); } - *overlay.instance.state.borrow_mut() = Some( + *inner.instance.state.borrow_mut() = Some( StateBuilder { component: heads.component, message: PhantomData, state: PhantomData, - element_builder: |state| { - Some(state.view(overlay.tree.state.downcast_ref::())) - }, + element_builder: |_| None, } .build(), ); - overlay.instance.with_element(|element| { - overlay.tree.diff_children(std::slice::from_ref(&element)) - }); - - self.overlay = Some( - OverlayBuilder { - instance: overlay.instance, - tree: overlay.tree, + self.overlay = Some(Overlay(Some( + InnerBuilder { + instance: inner.instance, + tree: inner.tree, types: PhantomData, overlay_builder: |_, _| None, } .build(), - ); + ))); shell.invalidate_layout(); } From 257264de1c41435328ef685b8b66a71c7666764f Mon Sep 17 00:00:00 2001 From: Aaron Honeycutt Date: Tue, 14 Mar 2023 14:34:32 -0600 Subject: [PATCH 21/85] Working on Radio example --- examples/radio/Cargo.toml | 10 ++++++++++ examples/radio/src/main.rs | 3 +++ 2 files changed, 13 insertions(+) create mode 100644 examples/radio/Cargo.toml create mode 100644 examples/radio/src/main.rs diff --git a/examples/radio/Cargo.toml b/examples/radio/Cargo.toml new file mode 100644 index 0000000000..a8c7f35184 --- /dev/null +++ b/examples/radio/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "radio" +version = "0.1.0" +authors = ["Aaron Honeycutt "] +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +iced = { path = "../.." } diff --git a/examples/radio/src/main.rs b/examples/radio/src/main.rs new file mode 100644 index 0000000000..e7a11a969c --- /dev/null +++ b/examples/radio/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} From 6d9cf0b60198e7d6c9615f539dac4b06eada5851 Mon Sep 17 00:00:00 2001 From: Aaron Honeycutt Date: Tue, 14 Mar 2023 14:36:31 -0600 Subject: [PATCH 22/85] Cleaning up from the Checkbox example that this is based on --- examples/radio/src/main.rs | 59 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/examples/radio/src/main.rs b/examples/radio/src/main.rs index e7a11a969c..3baccbe2d4 100644 --- a/examples/radio/src/main.rs +++ b/examples/radio/src/main.rs @@ -1,3 +1,58 @@ -fn main() { - println!("Hello, world!"); +use iced::widget::{column, container, Radio}; +use iced::{Element, Length, Sandbox, Settings}; + +pub fn main() -> iced::Result { + Example::run(Settings::default()) +} + +#[derive(Default)] +struct Example { + default_checkbox: bool, + custom_checkbox: bool, +} + +#[derive(Debug, Clone, Copy)] +enum Message { + DefaultChecked(bool), + CustomChecked(bool), +} + +impl Sandbox for Example { + type Message = Message; + + fn new() -> Self { + Default::default() + } + + fn title(&self) -> String { + String::from("Checkbox - Iced") + } + + fn update(&mut self, message: Message) { + match message { + Message::DefaultChecked(value) => self.default_checkbox = value, + Message::CustomChecked(value) => self.custom_checkbox = value, + } + } + + fn view(&self) -> Element { + let default_checkbox = + checkbox("Default", self.default_checkbox, Message::DefaultChecked); + let custom_checkbox = + checkbox("Custom", self.custom_checkbox, Message::CustomChecked) + .icon(checkbox::Icon { + font: ICON_FONT, + code_point: '\u{e901}', + size: None, + }); + + let content = column![default_checkbox, custom_checkbox].spacing(22); + + container(content) + .width(Length::Fill) + .height(Length::Fill) + .center_x() + .center_y() + .into() + } } From c96ab27b24a5775aeef7d8a0ed31214a5cb3f0a7 Mon Sep 17 00:00:00 2001 From: Aaron Honeycutt Date: Tue, 14 Mar 2023 14:39:26 -0600 Subject: [PATCH 23/85] Work on example more --- examples/radio/src/main.rs | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/examples/radio/src/main.rs b/examples/radio/src/main.rs index 3baccbe2d4..424fdf4252 100644 --- a/examples/radio/src/main.rs +++ b/examples/radio/src/main.rs @@ -25,7 +25,7 @@ impl Sandbox for Example { } fn title(&self) -> String { - String::from("Checkbox - Iced") + String::from("Radio - Iced") } fn update(&mut self, message: Message) { @@ -36,17 +36,12 @@ impl Sandbox for Example { } fn view(&self) -> Element { - let default_checkbox = - checkbox("Default", self.default_checkbox, Message::DefaultChecked); - let custom_checkbox = - checkbox("Custom", self.custom_checkbox, Message::CustomChecked) - .icon(checkbox::Icon { - font: ICON_FONT, - code_point: '\u{e901}', - size: None, - }); - - let content = column![default_checkbox, custom_checkbox].spacing(22); + let selected_choice = Some(Choice::A); + + Radio::new(Choice::A, "This is A", selected_choice, Message::RadioSelected); + Radio::new(Choice::B, "This is B", selected_choice, Message::RadioSelected); + + let content = column![selected_choice].spacing(22); container(content) .width(Length::Fill) From addc443f8ddb688248cc1d34e55ee7beed42195c Mon Sep 17 00:00:00 2001 From: Aaron Honeycutt Date: Tue, 14 Mar 2023 15:46:04 -0600 Subject: [PATCH 24/85] Working more on example --- examples/radio/src/main.rs | 46 +++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/examples/radio/src/main.rs b/examples/radio/src/main.rs index 424fdf4252..8881f059a5 100644 --- a/examples/radio/src/main.rs +++ b/examples/radio/src/main.rs @@ -7,14 +7,12 @@ pub fn main() -> iced::Result { #[derive(Default)] struct Example { - default_checkbox: bool, - custom_checkbox: bool, + selected_radio: Option, } #[derive(Debug, Clone, Copy)] enum Message { - DefaultChecked(bool), - CustomChecked(bool), + RadioSelected(Choice), } impl Sandbox for Example { @@ -30,18 +28,19 @@ impl Sandbox for Example { fn update(&mut self, message: Message) { match message { - Message::DefaultChecked(value) => self.default_checkbox = value, - Message::CustomChecked(value) => self.custom_checkbox = value, + Message::RadioSelected(value) => { + self.selected_radio = Some(choice); + } } } fn view(&self) -> Element { - let selected_choice = Some(Choice::A); + let selected_radio = Some(Choice::A); - Radio::new(Choice::A, "This is A", selected_choice, Message::RadioSelected); - Radio::new(Choice::B, "This is B", selected_choice, Message::RadioSelected); + Radio::new(Choice::A, "This is A", selected_radio, Message::RadioSelected); + Radio::new(Choice::B, "This is B", selected_radio, Message::RadioSelected); - let content = column![selected_choice].spacing(22); + let content = column![selected_radio].spacing(22); container(content) .width(Length::Fill) @@ -51,3 +50,30 @@ impl Sandbox for Example { .into() } } + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub enum Choice { + #[default] + A, + B, +} + +impl Choice { + const ALL: [Choice; 2] = [ + Choice::A, + Choice::B, + ]; +} + +impl std::fmt::Display for Choice { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + match self { + Choice::A => "A", + Choice::B => "B", + } + ) + } +} From d5f26c3d39079e5a1d8aa4a18b9fb04e8834022f Mon Sep 17 00:00:00 2001 From: Aaron Honeycutt Date: Fri, 17 Mar 2023 13:12:33 -0600 Subject: [PATCH 25/85] More work on example --- examples/radio/src/main.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/radio/src/main.rs b/examples/radio/src/main.rs index 8881f059a5..73b036108b 100644 --- a/examples/radio/src/main.rs +++ b/examples/radio/src/main.rs @@ -1,4 +1,4 @@ -use iced::widget::{column, container, Radio}; +use iced::widget::{column, container, radio}; use iced::{Element, Length, Sandbox, Settings}; pub fn main() -> iced::Result { @@ -37,10 +37,10 @@ impl Sandbox for Example { fn view(&self) -> Element { let selected_radio = Some(Choice::A); - Radio::new(Choice::A, "This is A", selected_radio, Message::RadioSelected); - Radio::new(Choice::B, "This is B", selected_radio, Message::RadioSelected); - - let content = column![selected_radio].spacing(22); + let content = column![ + Radio::new(Choice::A, "This is A", selected_radio, Message::RadioSelected), + Radio::new(Choice::B, "This is B", selected_radio, Message::RadioSelected), + ]; container(content) .width(Length::Fill) From 4fdd76c07c15f85a518c240aca0e55f482b18bc3 Mon Sep 17 00:00:00 2001 From: Aaron Honeycutt Date: Fri, 17 Mar 2023 13:32:11 -0600 Subject: [PATCH 26/85] Now is a working example --- examples/radio/src/main.rs | 65 +++++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/examples/radio/src/main.rs b/examples/radio/src/main.rs index 73b036108b..3b19924e13 100644 --- a/examples/radio/src/main.rs +++ b/examples/radio/src/main.rs @@ -7,7 +7,7 @@ pub fn main() -> iced::Result { #[derive(Default)] struct Example { - selected_radio: Option, + radio: Option, } #[derive(Debug, Clone, Copy)] @@ -29,18 +29,44 @@ impl Sandbox for Example { fn update(&mut self, message: Message) { match message { Message::RadioSelected(value) => { - self.selected_radio = Some(choice); + self.radio = Some(value); } } } fn view(&self) -> Element { - let selected_radio = Some(Choice::A); + let a_checkbox = radio( + "A", + Choice::A, + self.radio, + Message::RadioSelected, + ); + + let b_checkbox = radio( + "B", + Choice::B, + self.radio, + Message::RadioSelected, + ); + + let c_checkbox = radio( + "C", + Choice::C, + self.radio, + Message::RadioSelected, + ); + + let all_checkbox = radio("All of the above", Choice::All, self.radio, Message::RadioSelected); let content = column![ - Radio::new(Choice::A, "This is A", selected_radio, Message::RadioSelected), - Radio::new(Choice::B, "This is B", selected_radio, Message::RadioSelected), - ]; + a_checkbox, + b_checkbox, + c_checkbox, + all_checkbox, + ] + .spacing(20) + .padding(20) + .max_width(600); container(content) .width(Length::Fill) @@ -51,29 +77,10 @@ impl Sandbox for Example { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] -pub enum Choice { - #[default] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum Choice { A, B, -} - -impl Choice { - const ALL: [Choice; 2] = [ - Choice::A, - Choice::B, - ]; -} - -impl std::fmt::Display for Choice { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{}", - match self { - Choice::A => "A", - Choice::B => "B", - } - ) - } + C, + All, } From 0231ed6f1d5929d84bb81e6673d3319a24f08b16 Mon Sep 17 00:00:00 2001 From: traxys Date: Mon, 20 Mar 2023 00:18:41 +0100 Subject: [PATCH 27/85] winit: Fix replacement of node in wasm Replacing a node ends up with the following error: Node.replaceChild: Child to be replaced is not a child of this node It seems that Node.replaceChild is not recommended, and instead Element.replaceWith should be preferred. Using it avoids the panic. --- winit/src/application.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/winit/src/application.rs b/winit/src/application.rs index b13b7214ea..31654f26ee 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -179,13 +179,17 @@ where .unwrap_or(None) }); - let _ = match target { - Some(node) => node - .replace_child(&canvas, &node) - .expect(&format!("Could not replace #{}", node.id())), - None => body - .append_child(&canvas) - .expect("Append canvas to HTML body"), + match target { + Some(node) => { + let _ = node + .replace_with_with_node_1(&canvas) + .expect(&format!("Could not replace #{}", node.id())); + } + None => { + let _ = body + .append_child(&canvas) + .expect("Append canvas to HTML body"); + } }; } From c337bf297d1836c429cd24964e8b3bdcc13850be Mon Sep 17 00:00:00 2001 From: Giuliano Bellini s294739 Date: Sat, 25 Mar 2023 01:05:56 +0100 Subject: [PATCH 28/85] renamed scrollable styles --- native/src/widget/scrollable.rs | 8 ++++---- style/src/scrollable.rs | 14 +++++++------- style/src/theme.rs | 20 ++++++++++---------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index b88b77e51d..be81bee13b 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -858,9 +858,9 @@ pub fn draw( let style = if state.y_scroller_grabbed_at.is_some() { theme.dragging(style) } else if mouse_over_y_scrollbar { - theme.hovered(style) + theme.hovered_scrollbar(style) } else if mouse_over_scrollable { - theme.focused(style) + theme.hovered(style) } else { theme.active(style) }; @@ -873,9 +873,9 @@ pub fn draw( let style = if state.x_scroller_grabbed_at.is_some() { theme.dragging_horizontal(style) } else if mouse_over_x_scrollbar { - theme.hovered_horizontal(style) + theme.hovered_scrollbar_horizontal(style) } else if mouse_over_scrollable { - theme.focused_horizontal(style) + theme.hovered_horizontal(style) } else { theme.active_horizontal(style) }; diff --git a/style/src/scrollable.rs b/style/src/scrollable.rs index a3f3db209f..f3c04235bd 100644 --- a/style/src/scrollable.rs +++ b/style/src/scrollable.rs @@ -38,15 +38,15 @@ pub trait StyleSheet { fn active(&self, style: &Self::Style) -> Scrollbar; /// Produces the style of a hovered scrollbar. - fn hovered(&self, style: &Self::Style) -> Scrollbar; + fn hovered_scrollbar(&self, style: &Self::Style) -> Scrollbar; /// Produces the style of a scrollbar that is being dragged. fn dragging(&self, style: &Self::Style) -> Scrollbar { - self.hovered(style) + self.hovered_scrollbar(style) } /// Produces the style of a scrollbar when mouse is over the scrollable area. - fn focused(&self, style: &Self::Style) -> Scrollbar { + fn hovered(&self, style: &Self::Style) -> Scrollbar { self.active(style) } @@ -56,17 +56,17 @@ pub trait StyleSheet { } /// Produces the style of a hovered horizontal scrollbar. - fn hovered_horizontal(&self, style: &Self::Style) -> Scrollbar { - self.hovered(style) + fn hovered_scrollbar_horizontal(&self, style: &Self::Style) -> Scrollbar { + self.hovered_scrollbar(style) } /// Produces the style of a horizontal scrollbar that is being dragged. fn dragging_horizontal(&self, style: &Self::Style) -> Scrollbar { - self.hovered_horizontal(style) + self.hovered_scrollbar_horizontal(style) } /// Produces the style of a horizontal scrollbar when mouse is over the scrollable area. - fn focused_horizontal(&self, style: &Self::Style) -> Scrollbar { + fn hovered_horizontal(&self, style: &Self::Style) -> Scrollbar { self.active_horizontal(style) } } diff --git a/style/src/theme.rs b/style/src/theme.rs index 1d26256272..9a3105b52f 100644 --- a/style/src/theme.rs +++ b/style/src/theme.rs @@ -906,7 +906,7 @@ impl scrollable::StyleSheet for Theme { } } - fn hovered(&self, style: &Self::Style) -> scrollable::Scrollbar { + fn hovered_scrollbar(&self, style: &Self::Style) -> scrollable::Scrollbar { match style { Scrollable::Default => { let palette = self.extended_palette(); @@ -924,21 +924,21 @@ impl scrollable::StyleSheet for Theme { }, } } - Scrollable::Custom(custom) => custom.hovered(self), + Scrollable::Custom(custom) => custom.hovered_scrollbar(self), } } fn dragging(&self, style: &Self::Style) -> scrollable::Scrollbar { match style { - Scrollable::Default => self.hovered(style), + Scrollable::Default => self.hovered_scrollbar(style), Scrollable::Custom(custom) => custom.dragging(self), } } - fn focused(&self, style: &Self::Style) -> scrollable::Scrollbar { + fn hovered(&self, style: &Self::Style) -> scrollable::Scrollbar { match style { Scrollable::Default => self.active(style), - Scrollable::Custom(custom) => custom.focused(self), + Scrollable::Custom(custom) => custom.hovered(self), } } @@ -949,10 +949,10 @@ impl scrollable::StyleSheet for Theme { } } - fn hovered_horizontal(&self, style: &Self::Style) -> scrollable::Scrollbar { + fn hovered_scrollbar_horizontal(&self, style: &Self::Style) -> scrollable::Scrollbar { match style { - Scrollable::Default => self.hovered(style), - Scrollable::Custom(custom) => custom.hovered_horizontal(self), + Scrollable::Default => self.hovered_scrollbar(style), + Scrollable::Custom(custom) => custom.hovered_scrollbar_horizontal(self), } } @@ -966,13 +966,13 @@ impl scrollable::StyleSheet for Theme { } } - fn focused_horizontal( + fn hovered_horizontal( &self, style: &Self::Style, ) -> scrollable::Scrollbar { match style { Scrollable::Default => self.active_horizontal(style), - Scrollable::Custom(custom) => custom.focused_horizontal(self), + Scrollable::Custom(custom) => custom.hovered_horizontal(self), } } } From c407b4504cd5e7dcb04a8fd31ad0400c891fc3e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 27 Mar 2023 15:51:32 +0200 Subject: [PATCH 29/85] Introduce `is_mouse_over_scrollbar` to `StyleSheet::hovered` for `Scrollable` --- examples/scrollable/src/main.rs | 36 +++++++++++++------ native/src/widget/scrollable.rs | 8 ++--- style/src/scrollable.rs | 32 ++++++++--------- style/src/theme.rs | 63 ++++++++++++++++----------------- 4 files changed, 73 insertions(+), 66 deletions(-) diff --git a/examples/scrollable/src/main.rs b/examples/scrollable/src/main.rs index 7c85896147..f8c5aa742f 100644 --- a/examples/scrollable/src/main.rs +++ b/examples/scrollable/src/main.rs @@ -339,22 +339,36 @@ impl scrollable::StyleSheet for ScrollbarCustomStyle { style.active(&theme::Scrollable::Default) } - fn hovered(&self, style: &Self::Style) -> Scrollbar { - style.hovered(&theme::Scrollable::Default) + fn hovered( + &self, + style: &Self::Style, + is_mouse_over_scrollbar: bool, + ) -> Scrollbar { + style.hovered(&theme::Scrollable::Default, is_mouse_over_scrollbar) } - fn hovered_horizontal(&self, style: &Self::Style) -> Scrollbar { - Scrollbar { - background: style.active(&theme::Scrollable::default()).background, - border_radius: 0.0, - border_width: 0.0, - border_color: Default::default(), - scroller: Scroller { - color: Color::from_rgb8(250, 85, 134), + fn hovered_horizontal( + &self, + style: &Self::Style, + is_mouse_over_scrollbar: bool, + ) -> Scrollbar { + if is_mouse_over_scrollbar { + Scrollbar { + background: style + .active(&theme::Scrollable::default()) + .background, border_radius: 0.0, border_width: 0.0, border_color: Default::default(), - }, + scroller: Scroller { + color: Color::from_rgb8(250, 85, 134), + border_radius: 0.0, + border_width: 0.0, + border_color: Default::default(), + }, + } + } else { + self.active(style) } } } diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index be81bee13b..d9cdf29618 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -857,10 +857,8 @@ pub fn draw( if let Some(scrollbar) = scrollbars.y { let style = if state.y_scroller_grabbed_at.is_some() { theme.dragging(style) - } else if mouse_over_y_scrollbar { - theme.hovered_scrollbar(style) } else if mouse_over_scrollable { - theme.hovered(style) + theme.hovered(style, mouse_over_y_scrollbar) } else { theme.active(style) }; @@ -872,10 +870,8 @@ pub fn draw( if let Some(scrollbar) = scrollbars.x { let style = if state.x_scroller_grabbed_at.is_some() { theme.dragging_horizontal(style) - } else if mouse_over_x_scrollbar { - theme.hovered_scrollbar_horizontal(style) } else if mouse_over_scrollable { - theme.hovered_horizontal(style) + theme.hovered_horizontal(style, mouse_over_x_scrollbar) } else { theme.active_horizontal(style) }; diff --git a/style/src/scrollable.rs b/style/src/scrollable.rs index f3c04235bd..64a91b694b 100644 --- a/style/src/scrollable.rs +++ b/style/src/scrollable.rs @@ -37,17 +37,16 @@ pub trait StyleSheet { /// Produces the style of an active scrollbar. fn active(&self, style: &Self::Style) -> Scrollbar; - /// Produces the style of a hovered scrollbar. - fn hovered_scrollbar(&self, style: &Self::Style) -> Scrollbar; + /// Produces the style of a scrollbar when the scrollable is being hovered. + fn hovered( + &self, + style: &Self::Style, + is_mouse_over_scrollbar: bool, + ) -> Scrollbar; /// Produces the style of a scrollbar that is being dragged. fn dragging(&self, style: &Self::Style) -> Scrollbar { - self.hovered_scrollbar(style) - } - - /// Produces the style of a scrollbar when mouse is over the scrollable area. - fn hovered(&self, style: &Self::Style) -> Scrollbar { - self.active(style) + self.hovered(style, true) } /// Produces the style of an active horizontal scrollbar. @@ -55,18 +54,17 @@ pub trait StyleSheet { self.active(style) } - /// Produces the style of a hovered horizontal scrollbar. - fn hovered_scrollbar_horizontal(&self, style: &Self::Style) -> Scrollbar { - self.hovered_scrollbar(style) + /// Produces the style of a horizontal scrollbar when the scrollable is being hovered. + fn hovered_horizontal( + &self, + style: &Self::Style, + is_mouse_over_scrollbar: bool, + ) -> Scrollbar { + self.hovered(style, is_mouse_over_scrollbar) } /// Produces the style of a horizontal scrollbar that is being dragged. fn dragging_horizontal(&self, style: &Self::Style) -> Scrollbar { - self.hovered_scrollbar_horizontal(style) - } - - /// Produces the style of a horizontal scrollbar when mouse is over the scrollable area. - fn hovered_horizontal(&self, style: &Self::Style) -> Scrollbar { - self.active_horizontal(style) + self.hovered(style, true) } } diff --git a/style/src/theme.rs b/style/src/theme.rs index 9a3105b52f..0ebd82a45a 100644 --- a/style/src/theme.rs +++ b/style/src/theme.rs @@ -906,42 +906,45 @@ impl scrollable::StyleSheet for Theme { } } - fn hovered_scrollbar(&self, style: &Self::Style) -> scrollable::Scrollbar { + fn hovered( + &self, + style: &Self::Style, + is_mouse_over_scrollbar: bool, + ) -> scrollable::Scrollbar { match style { Scrollable::Default => { - let palette = self.extended_palette(); + if is_mouse_over_scrollbar { + let palette = self.extended_palette(); - scrollable::Scrollbar { - background: palette.background.weak.color.into(), - border_radius: 2.0, - border_width: 0.0, - border_color: Color::TRANSPARENT, - scroller: scrollable::Scroller { - color: palette.primary.strong.color, + scrollable::Scrollbar { + background: palette.background.weak.color.into(), border_radius: 2.0, border_width: 0.0, border_color: Color::TRANSPARENT, - }, + scroller: scrollable::Scroller { + color: palette.primary.strong.color, + border_radius: 2.0, + border_width: 0.0, + border_color: Color::TRANSPARENT, + }, + } + } else { + self.active(style) } } - Scrollable::Custom(custom) => custom.hovered_scrollbar(self), + Scrollable::Custom(custom) => { + custom.hovered(self, is_mouse_over_scrollbar) + } } } fn dragging(&self, style: &Self::Style) -> scrollable::Scrollbar { match style { - Scrollable::Default => self.hovered_scrollbar(style), + Scrollable::Default => self.hovered(style, true), Scrollable::Custom(custom) => custom.dragging(self), } } - fn hovered(&self, style: &Self::Style) -> scrollable::Scrollbar { - match style { - Scrollable::Default => self.active(style), - Scrollable::Custom(custom) => custom.hovered(self), - } - } - fn active_horizontal(&self, style: &Self::Style) -> scrollable::Scrollbar { match style { Scrollable::Default => self.active(style), @@ -949,30 +952,26 @@ impl scrollable::StyleSheet for Theme { } } - fn hovered_scrollbar_horizontal(&self, style: &Self::Style) -> scrollable::Scrollbar { - match style { - Scrollable::Default => self.hovered_scrollbar(style), - Scrollable::Custom(custom) => custom.hovered_scrollbar_horizontal(self), - } - } - - fn dragging_horizontal( + fn hovered_horizontal( &self, style: &Self::Style, + is_mouse_over_scrollbar: bool, ) -> scrollable::Scrollbar { match style { - Scrollable::Default => self.hovered_horizontal(style), - Scrollable::Custom(custom) => custom.dragging_horizontal(self), + Scrollable::Default => self.hovered(style, is_mouse_over_scrollbar), + Scrollable::Custom(custom) => { + custom.hovered_horizontal(self, is_mouse_over_scrollbar) + } } } - fn hovered_horizontal( + fn dragging_horizontal( &self, style: &Self::Style, ) -> scrollable::Scrollbar { match style { - Scrollable::Default => self.active_horizontal(style), - Scrollable::Custom(custom) => custom.hovered_horizontal(self), + Scrollable::Default => self.hovered_horizontal(style, true), + Scrollable::Custom(custom) => custom.dragging_horizontal(self), } } } From dcccf7064d506abb3aab15227e6cdf34d852514d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 27 Mar 2023 15:57:51 +0200 Subject: [PATCH 30/85] Fix inconsistency in default implementation of `scrollable::StyleSheet` --- style/src/scrollable.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/style/src/scrollable.rs b/style/src/scrollable.rs index 64a91b694b..b528c4444a 100644 --- a/style/src/scrollable.rs +++ b/style/src/scrollable.rs @@ -65,6 +65,6 @@ pub trait StyleSheet { /// Produces the style of a horizontal scrollbar that is being dragged. fn dragging_horizontal(&self, style: &Self::Style) -> Scrollbar { - self.hovered(style, true) + self.hovered_horizontal(style, true) } } From cb78dc548b04b951d9ee86d18a5fbef8c591dd4e Mon Sep 17 00:00:00 2001 From: Jacob Kiesel Date: Fri, 31 Mar 2023 20:37:09 -0600 Subject: [PATCH 31/85] re-expose winit features for window servers in Linux --- winit/Cargo.toml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/winit/Cargo.toml b/winit/Cargo.toml index dd5c12c27d..255b28bbbc 100644 --- a/winit/Cargo.toml +++ b/winit/Cargo.toml @@ -11,11 +11,16 @@ keywords = ["gui", "ui", "graphics", "interface", "widgets"] categories = ["gui"] [features] +default = ["x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"] trace = ["tracing", "tracing-core", "tracing-subscriber"] chrome-trace = ["trace", "tracing-chrome"] debug = ["iced_native/debug"] system = ["sysinfo"] application = [] +x11 = ["winit/x11"] +wayland = ["winit/wayland"] +wayland-dlopen = ["winit/wayland-dlopen"] +wayland-csd-adwaita = ["winit/wayland-csd-adwaita"] [dependencies] window_clipboard = "0.2" @@ -26,6 +31,7 @@ thiserror = "1.0" version = "0.27" git = "https://github.com/iced-rs/winit.git" rev = "940457522e9fb9f5dac228b0ecfafe0138b4048c" +default-features = false [dependencies.iced_native] version = "0.9" From 703484c5fdd87c64a41d3e627b0c79b43179e124 Mon Sep 17 00:00:00 2001 From: David Huculak Date: Sat, 1 Apr 2023 16:10:28 -0400 Subject: [PATCH 32/85] remove colons from shader labels --- wgpu/src/image.rs | 2 +- wgpu/src/quad.rs | 2 +- wgpu/src/triangle.rs | 4 ++-- wgpu/src/triangle/msaa.rs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs index a5e63b171c..9f56c1881d 100644 --- a/wgpu/src/image.rs +++ b/wgpu/src/image.rs @@ -141,7 +141,7 @@ impl Pipeline { let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: Some("iced_wgpu::image::shader"), + label: Some("iced_wgpu image shader"), source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed( include_str!("shader/image.wgsl"), )), diff --git a/wgpu/src/quad.rs b/wgpu/src/quad.rs index 2f5fcc6be9..1343181ee5 100644 --- a/wgpu/src/quad.rs +++ b/wgpu/src/quad.rs @@ -63,7 +63,7 @@ impl Pipeline { let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: Some("iced_wgpu::quad::shader"), + label: Some("iced_wgpu quad shader"), source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed( include_str!("shader/quad.wgsl"), )), diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs index efdd214b0f..162428f0a6 100644 --- a/wgpu/src/triangle.rs +++ b/wgpu/src/triangle.rs @@ -457,7 +457,7 @@ mod solid { let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { label: Some( - "iced_wgpu::triangle::solid create shader module", + "iced_wgpu triangle solid create shader module", ), source: wgpu::ShaderSource::Wgsl( std::borrow::Cow::Borrowed(include_str!( @@ -654,7 +654,7 @@ mod gradient { let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { label: Some( - "iced_wgpu::triangle::gradient create shader module", + "iced_wgpu triangle gradient create shader module", ), source: wgpu::ShaderSource::Wgsl( std::borrow::Cow::Borrowed(include_str!( diff --git a/wgpu/src/triangle/msaa.rs b/wgpu/src/triangle/msaa.rs index a3016ff8fb..e76f7d5eaa 100644 --- a/wgpu/src/triangle/msaa.rs +++ b/wgpu/src/triangle/msaa.rs @@ -75,7 +75,7 @@ impl Blit { let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: Some("iced_wgpu::triangle::blit_shader"), + label: Some("iced_wgpu triangle blit_shader"), source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed( include_str!("../shader/blit.wgsl"), )), From 15125c23f1896f5fa5f4d61a362add691f3cb131 Mon Sep 17 00:00:00 2001 From: bq_wrongway <97843623+bq-wrongway@users.noreply.github.com> Date: Wed, 5 Apr 2023 20:00:25 +0200 Subject: [PATCH 33/85] Updating Roadmap.md I collapsed Animations because the underlying issue was resolved, and i added issue #343 to 3D Canvas widget --- ROADMAP.md | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index ef6df0e4f7..c30c591b2d 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -19,6 +19,7 @@ Once a step is completed, it is collapsed and added to this list: * [x] Custom styling ([#146]) * [x] Canvas for 2D graphics ([#193]) * [x] Basic overlay support ([#444]) + * [x] Animations [#31] [#24]: https://github.com/iced-rs/iced/issues/24 [#25]: https://github.com/iced-rs/iced/issues/25 @@ -29,6 +30,7 @@ Once a step is completed, it is collapsed and added to this list: [#146]: https://github.com/iced-rs/iced/pull/146 [#193]: https://github.com/iced-rs/iced/pull/193 [#444]: https://github.com/iced-rs/iced/pull/444 +[#31]: https://github.com/iced-rs/iced/issues/31 ### Multi-window support ([#27]) Open and control multiple windows at runtime. @@ -39,16 +41,7 @@ This approach should also allow us to perform custom optimizations for this part [#27]: https://github.com/iced-rs/iced/issues/27 -### Animations ([#31]) -Allow widgets to request a redraw at a specific time. - -This is a necessary feature to render loading spinners, a blinking text cursor, GIF images, etc. - -[`winit`] allows flexible control of its event loop. We may be able to use [`ControlFlow::WaitUntil`](https://docs.rs/winit/0.20.0-alpha3/winit/event_loop/enum.ControlFlow.html#variant.WaitUntil) for this purpose. - -[#31]: https://github.com/iced-rs/iced/issues/31 - -### Canvas widget for 3D graphics ([#32]) +### Canvas widget for 3D graphics (~~[#32]~~ [#343]) A widget to draw freely in 3D. It could be used to draw charts, implement a Paint clone, a CAD application, etc. As a first approach, we could expose the underlying renderer directly here, and couple this widget with it ([`wgpu`] for now). Once [`wgpu`] gets WebGL or WebGPU support, this widget will be able to run on the web too. The renderer primitive could be a simple texture that the widget draws to. @@ -56,6 +49,7 @@ As a first approach, we could expose the underlying renderer directly here, and In the long run, we could expose a renderer-agnostic abstraction to perform the drawing. [#32]: https://github.com/iced-rs/iced/issues/32 +[#343] https://github.com/iced-rs/iced/issues/343 ### Text shaping and font fallback ([#33]) [`wgpu_glyph`] uses [`glyph_brush`], which in turn uses [`rusttype`]. While the current implementation is able to layout text quite nicely, it does not perform any [text shaping]. From 7b369842959511f17d5c27941fd0308484bff8ea Mon Sep 17 00:00:00 2001 From: Casper Storm Date: Mon, 13 Feb 2023 11:38:05 +0100 Subject: [PATCH 34/85] feat: added handle to text_input --- examples/text_input/Cargo.toml | 9 +++ examples/text_input/README.md | 10 +++ examples/text_input/fonts/icons.ttf | Bin 0 -> 1612 bytes examples/text_input/src/main.rs | 93 ++++++++++++++++++++++ native/src/widget/text_input.rs | 119 +++++++++++++++++++++++++++- src/widget.rs | 2 +- style/src/text_input.rs | 2 + style/src/theme.rs | 3 + 8 files changed, 236 insertions(+), 2 deletions(-) create mode 100644 examples/text_input/Cargo.toml create mode 100644 examples/text_input/README.md create mode 100644 examples/text_input/fonts/icons.ttf create mode 100644 examples/text_input/src/main.rs diff --git a/examples/text_input/Cargo.toml b/examples/text_input/Cargo.toml new file mode 100644 index 0000000000..5937ef8e71 --- /dev/null +++ b/examples/text_input/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "text_input" +version = "0.1.0" +authors = ["Casper Rogild Storm"] +edition = "2021" +publish = false + +[dependencies] +iced = { path = "../..", features = ["debug"] } diff --git a/examples/text_input/README.md b/examples/text_input/README.md new file mode 100644 index 0000000000..2b2d80590f --- /dev/null +++ b/examples/text_input/README.md @@ -0,0 +1,10 @@ +## TextInput + +A `TextInput` is a field that can be filled with text. + +You can run it with `cargo run`: +``` +cargo run --package text_input +``` + +[`main`]: src/main.rs diff --git a/examples/text_input/fonts/icons.ttf b/examples/text_input/fonts/icons.ttf new file mode 100644 index 0000000000000000000000000000000000000000..bfe8a24b966869737a85aac1242d5a35f32381b2 GIT binary patch literal 1612 zcmaJ>OH3PA6uobr83)JsgFho`YzBXW<3Ao7P>YgM9ML9ClonJeZ5juRZ3TM}7zv~- zSVWQ9P1{9Pv5n}i$}YQ9iqu70scb5-Y8REdLUhwbrAh_dGanR_qTbP)ckb`qbLY)_ z#E9r3Z4;;5&G)he=V0GAusp}Ox7ez#N((=KLd1rlzhAGeuVQvV*X9fLm96ESUu@q( z-y{+qHEPwR$l%lOiTDxxr3MVvulNr{mLl|Mqt)3QB>Efr2hfLC)Ws_EvR|Qhpu1Yt z%~i5f1^OfCa=Y59?Y!Op5AeC&1zq77aBAP;RAe1!)wD4TpD}YiYM3lE0lSSZkbsAgd?0j>yInm? zJ|G>l?(dQXdA;j0Vu*6~*aV{Tq#p7z?*DBTQo;EPp2Rva%F_&}7yOdPFGfbg@v>){ zjVoc%${kD!vGLJDsa%RDc!othUNI7mE5*`y!Cx-MBVp0!^&=*qE0w32K!ah&Q6Q?E zkP;0XIl_Zub3;c%h`F8@D1Y7S6QRzHm1Rkiwb3671Y!brCzEb22>X)%aW$#}D^X^~v5W1CIpv2JD51XT-*K zjKKdcp!IT>eEbQ2Kmk&)FK20;q3qE*6hRw%0@gZzzqVZoVc+Ejx0J@s?drvQ!R9s3 zm`k3C$7f{MnCDtEC{5&qC%Fkpc%WpnvtGz-HmfL^?5xl0_0488$A35*2O?T* z!H*`B@7_$~yPN-;bNk2dK6bwT7hSaIzwd9(XE6;Jv$-=k@951Cj)f4Y+4 z#@lLm3|&iuu3}uqxT{-jscM^=w1{Kc!ceKbP+MDXs%<%+$W7J8%6`*XxxOoyYdSD7h`_(2mk;8 literal 0 HcmV?d00001 diff --git a/examples/text_input/src/main.rs b/examples/text_input/src/main.rs new file mode 100644 index 0000000000..b25ed7e18c --- /dev/null +++ b/examples/text_input/src/main.rs @@ -0,0 +1,93 @@ +use iced::widget::{checkbox, column, container, text_input}; +use iced::{Element, Font, Length, Sandbox, Settings}; + +const ICON_FONT: Font = Font::External { + name: "Icons", + bytes: include_bytes!("../fonts/icons.ttf"), +}; + +pub fn main() -> iced::Result { + Example::run(Settings::default()) +} + +#[derive(Default)] +struct Example { + value: String, + is_showing_handle: bool, +} + +#[derive(Debug, Clone)] +enum Message { + Changed(String), + ToggleHandle(bool), +} + +impl Sandbox for Example { + type Message = Message; + + fn new() -> Self { + Self::default() + } + + fn title(&self) -> String { + String::from("Text Input - Iced") + } + + fn update(&mut self, message: Message) { + match message { + Message::Changed(value) => self.value = value, + Message::ToggleHandle(_) => { + self.is_showing_handle = !self.is_showing_handle + } + } + } + + fn view(&self) -> Element { + let checkbox = + checkbox("Handle", self.is_showing_handle, Message::ToggleHandle) + .spacing(5) + .text_size(16); + + let mut text_input = + text_input("Placeholder", self.value.as_str(), Message::Changed); + + if self.is_showing_handle { + text_input = text_input.handle(text_input::Handle { + font: ICON_FONT, + text: String::from('\u{e900}'), + size: Some(18), + position: text_input::HandlePosition::Right, + }); + } + + let content = column!["What is blazing fast?", text_input, checkbox] + .width(Length::Units(200)) + .spacing(10); + + container(content) + .width(Length::Fill) + .height(Length::Fill) + .center_x() + .center_y() + .into() + } + + fn theme(&self) -> iced::Theme { + iced::Theme::default() + } + + fn style(&self) -> iced::theme::Application { + iced::theme::Application::default() + } + + fn scale_factor(&self) -> f64 { + 1.0 + } + + fn run(settings: Settings<()>) -> Result<(), iced::Error> + where + Self: 'static + Sized, + { + ::run(settings) + } +} diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index ee0473ea78..7696da99df 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -31,6 +31,47 @@ use crate::{ pub use iced_style::text_input::{Appearance, StyleSheet}; +/// The position of the [`Handle`]. +#[derive(Clone, Default, Debug)] +pub enum HandlePosition { + /// Position the handle to the left. + Left, + /// Position the handle to the left. + /// + /// This is the default. + #[default] + Right, +} + +/// The content of the [`Handle`]. +#[derive(Clone)] +pub struct Handle +where + Renderer: text::Renderer, +{ + /// Font that will be used to display the `text`. + pub font: Renderer::Font, + /// Text that will be shown. + pub text: String, + /// Font size of the content. + pub size: Option, + /// Position of the handle. + pub position: HandlePosition, +} + +impl std::fmt::Debug for Handle +where + Renderer: text::Renderer, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Handle") + .field("text", &self.text) + .field("size", &self.size) + .field("position", &self.position) + .finish() + } +} + /// A field that can be filled with text. /// /// # Example @@ -68,6 +109,7 @@ where on_change: Box Message + 'a>, on_paste: Option Message + 'a>>, on_submit: Option, + handle: Option>, style: ::Style, } @@ -99,6 +141,7 @@ where on_change: Box::new(on_change), on_paste: None, on_submit: None, + handle: None, style: Default::default(), } } @@ -132,6 +175,13 @@ where self.font = font; self } + + /// Sets the [`Handle`] of the [`TextInput`]. + pub fn handle(mut self, handle: Handle) -> Self { + self.handle = Some(handle); + self + } + /// Sets the width of the [`TextInput`]. pub fn width(mut self, width: impl Into) -> Self { self.width = width.into(); @@ -188,8 +238,10 @@ where value.unwrap_or(&self.value), &self.placeholder, self.size, + self.padding, &self.font, self.is_secure, + self.handle.as_ref(), &self.style, ) } @@ -286,8 +338,10 @@ where &self.value, &self.placeholder, self.size, + self.padding, &self.font, self.is_secure, + self.handle.as_ref(), &self.style, ) } @@ -812,8 +866,10 @@ pub fn draw( value: &Value, placeholder: &str, size: Option, + padding: Padding, font: &Renderer::Font, is_secure: bool, + handle: Option<&Handle>, style: &::Style, ) where Renderer: text::Renderer, @@ -823,7 +879,37 @@ pub fn draw( let value = secure_value.as_ref().unwrap_or(value); let bounds = layout.bounds(); - let text_bounds = layout.children().next().unwrap().bounds(); + let text_bounds = { + let bounds = layout.children().next().unwrap().bounds(); + if let Some(handle) = handle { + let Handle { + font, + size, + text, + position, + } = handle; + + let padding = f32::from(padding.horizontal()); + let size = size.unwrap_or_else(|| renderer.default_size()); + let width = + renderer.measure_width(text.as_str(), size, font.clone()); + + match position { + HandlePosition::Left => Rectangle { + x: bounds.x + (width + padding), + width: bounds.width - (width + padding), + ..bounds + }, + HandlePosition::Right => Rectangle { + x: bounds.x, + width: bounds.width - (width + padding), + ..bounds + }, + } + } else { + bounds + } + }; let is_mouse_over = bounds.contains(cursor_position); @@ -845,6 +931,37 @@ pub fn draw( appearance.background, ); + if let Some(handle) = handle { + let Handle { + size, + font, + text, + position, + } = handle; + + let padding = f32::from(padding.horizontal()); + let size = size.unwrap_or_else(|| renderer.default_size()); + let width = renderer.measure_width(text.as_str(), size, font.clone()); + + renderer.fill_text(Text { + content: text, + size: f32::from(size), + font: font.clone(), + color: appearance.handle_color, + bounds: Rectangle { + x: match position { + HandlePosition::Left => bounds.x + width + padding, + HandlePosition::Right => bounds.x + bounds.width - padding, + }, + y: bounds.center_y() - f32::from(size) / 2.0, + height: f32::from(size), + ..bounds + }, + horizontal_alignment: alignment::Horizontal::Right, + vertical_alignment: alignment::Vertical::Top, + }); + } + let text = value.to_string(); let size = size.unwrap_or_else(|| renderer.default_size()); diff --git a/src/widget.rs b/src/widget.rs index e2b0537e53..948456d314 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -124,7 +124,7 @@ pub mod text_input { //! Display fields that can be filled with text. pub use iced_native::widget::text_input::{ focus, move_cursor_to, move_cursor_to_end, move_cursor_to_front, - select_all, Appearance, Id, StyleSheet, + select_all, Appearance, Handle, HandlePosition, Id, StyleSheet, }; /// A field that can be filled with text. diff --git a/style/src/text_input.rs b/style/src/text_input.rs index d97016dc93..5e703e6176 100644 --- a/style/src/text_input.rs +++ b/style/src/text_input.rs @@ -12,6 +12,8 @@ pub struct Appearance { pub border_width: f32, /// The border [`Color`] of the text input. pub border_color: Color, + /// The handle [`Color`] of the text input. + pub handle_color: Color, } /// A set of rules that dictate the style of a text input. diff --git a/style/src/theme.rs b/style/src/theme.rs index 0ebd82a45a..de46d7d4be 100644 --- a/style/src/theme.rs +++ b/style/src/theme.rs @@ -1028,6 +1028,7 @@ impl text_input::StyleSheet for Theme { border_radius: 2.0, border_width: 1.0, border_color: palette.background.strong.color, + handle_color: palette.background.weak.text, } } @@ -1043,6 +1044,7 @@ impl text_input::StyleSheet for Theme { border_radius: 2.0, border_width: 1.0, border_color: palette.background.base.text, + handle_color: palette.background.weak.text, } } @@ -1058,6 +1060,7 @@ impl text_input::StyleSheet for Theme { border_radius: 2.0, border_width: 1.0, border_color: palette.primary.strong.color, + handle_color: palette.primary.weak.text, } } From bfc5db9009f10a67ed277ce9d1997bcdea3f6acd Mon Sep 17 00:00:00 2001 From: Casper Storm Date: Mon, 13 Feb 2023 12:00:08 +0100 Subject: [PATCH 35/85] Updated `handle_color` for focused state --- style/src/theme.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/style/src/theme.rs b/style/src/theme.rs index de46d7d4be..4837e10b28 100644 --- a/style/src/theme.rs +++ b/style/src/theme.rs @@ -1060,7 +1060,7 @@ impl text_input::StyleSheet for Theme { border_radius: 2.0, border_width: 1.0, border_color: palette.primary.strong.color, - handle_color: palette.primary.weak.text, + handle_color: palette.background.weak.text, } } From d24a4a46895ed711ddfc3199a0445f0b69a812e4 Mon Sep 17 00:00:00 2001 From: Casper Storm Date: Thu, 16 Feb 2023 14:32:59 +0100 Subject: [PATCH 36/85] Changed `Handle` to `Icon` to be consistent --- examples/text_input/src/main.rs | 18 +++---- native/src/widget/text_input.rs | 87 +++++++++++++++++---------------- src/widget.rs | 2 +- style/src/text_input.rs | 4 +- style/src/theme.rs | 6 +-- 5 files changed, 59 insertions(+), 58 deletions(-) diff --git a/examples/text_input/src/main.rs b/examples/text_input/src/main.rs index b25ed7e18c..e0ba198324 100644 --- a/examples/text_input/src/main.rs +++ b/examples/text_input/src/main.rs @@ -13,13 +13,13 @@ pub fn main() -> iced::Result { #[derive(Default)] struct Example { value: String, - is_showing_handle: bool, + is_showing_icon: bool, } #[derive(Debug, Clone)] enum Message { Changed(String), - ToggleHandle(bool), + ToggleIcon(bool), } impl Sandbox for Example { @@ -36,27 +36,27 @@ impl Sandbox for Example { fn update(&mut self, message: Message) { match message { Message::Changed(value) => self.value = value, - Message::ToggleHandle(_) => { - self.is_showing_handle = !self.is_showing_handle + Message::ToggleIcon(_) => { + self.is_showing_icon = !self.is_showing_icon } } } fn view(&self) -> Element { let checkbox = - checkbox("Handle", self.is_showing_handle, Message::ToggleHandle) + checkbox("Icon", self.is_showing_icon, Message::ToggleIcon) .spacing(5) .text_size(16); let mut text_input = text_input("Placeholder", self.value.as_str(), Message::Changed); - if self.is_showing_handle { - text_input = text_input.handle(text_input::Handle { + if self.is_showing_icon { + text_input = text_input.icon(text_input::Icon { font: ICON_FONT, - text: String::from('\u{e900}'), + code_point: '\u{e900}', size: Some(18), - position: text_input::HandlePosition::Right, + position: text_input::IconPosition::Right, }); } diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 7696da99df..575f44368a 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -31,41 +31,38 @@ use crate::{ pub use iced_style::text_input::{Appearance, StyleSheet}; -/// The position of the [`Handle`]. +/// The position of the [`Icon`]. #[derive(Clone, Default, Debug)] -pub enum HandlePosition { - /// Position the handle to the left. +pub enum IconPosition { + /// Position the [`Icon`] to the left. Left, - /// Position the handle to the left. + /// Position the [`Icon`] to the left. /// /// This is the default. #[default] Right, } -/// The content of the [`Handle`]. +/// The content of the [`Icon`]. #[derive(Clone)] -pub struct Handle -where - Renderer: text::Renderer, -{ - /// Font that will be used to display the `text`. - pub font: Renderer::Font, - /// Text that will be shown. - pub text: String, +pub struct Icon { + /// 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, - /// Position of the handle. - pub position: HandlePosition, + /// Position of the icon. + pub position: IconPosition, } -impl std::fmt::Debug for Handle +impl std::fmt::Debug for Icon where Renderer: text::Renderer, { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Handle") - .field("text", &self.text) + f.debug_struct("Icon") + .field("code_point", &self.code_point) .field("size", &self.size) .field("position", &self.position) .finish() @@ -109,7 +106,7 @@ where on_change: Box Message + 'a>, on_paste: Option Message + 'a>>, on_submit: Option, - handle: Option>, + icon: Option>, style: ::Style, } @@ -141,7 +138,7 @@ where on_change: Box::new(on_change), on_paste: None, on_submit: None, - handle: None, + icon: None, style: Default::default(), } } @@ -176,9 +173,9 @@ where self } - /// Sets the [`Handle`] of the [`TextInput`]. - pub fn handle(mut self, handle: Handle) -> Self { - self.handle = Some(handle); + /// Sets the [`Icon`] of the [`TextInput`]. + pub fn icon(mut self, icon: Icon) -> Self { + self.icon = Some(icon); self } @@ -241,7 +238,7 @@ where self.padding, &self.font, self.is_secure, - self.handle.as_ref(), + self.icon.as_ref(), &self.style, ) } @@ -341,7 +338,7 @@ where self.padding, &self.font, self.is_secure, - self.handle.as_ref(), + self.icon.as_ref(), &self.style, ) } @@ -869,7 +866,7 @@ pub fn draw( padding: Padding, font: &Renderer::Font, is_secure: bool, - handle: Option<&Handle>, + icon: Option<&Icon>, style: &::Style, ) where Renderer: text::Renderer, @@ -881,26 +878,29 @@ pub fn draw( let bounds = layout.bounds(); let text_bounds = { let bounds = layout.children().next().unwrap().bounds(); - if let Some(handle) = handle { - let Handle { + if let Some(icon) = icon { + let Icon { font, size, - text, + code_point, position, - } = handle; + } = icon; let padding = f32::from(padding.horizontal()); let size = size.unwrap_or_else(|| renderer.default_size()); - let width = - renderer.measure_width(text.as_str(), size, font.clone()); + let width = renderer.measure_width( + &code_point.to_string(), + size, + font.clone(), + ); match position { - HandlePosition::Left => Rectangle { + IconPosition::Left => Rectangle { x: bounds.x + (width + padding), width: bounds.width - (width + padding), ..bounds }, - HandlePosition::Right => Rectangle { + IconPosition::Right => Rectangle { x: bounds.x, width: bounds.width - (width + padding), ..bounds @@ -931,27 +931,28 @@ pub fn draw( appearance.background, ); - if let Some(handle) = handle { - let Handle { + if let Some(icon) = icon { + let Icon { size, font, - text, + code_point, position, - } = handle; + } = icon; let padding = f32::from(padding.horizontal()); let size = size.unwrap_or_else(|| renderer.default_size()); - let width = renderer.measure_width(text.as_str(), size, font.clone()); + let width = + renderer.measure_width(&code_point.to_string(), size, font.clone()); renderer.fill_text(Text { - content: text, + content: &code_point.to_string(), size: f32::from(size), font: font.clone(), - color: appearance.handle_color, + color: appearance.icon_color, bounds: Rectangle { x: match position { - HandlePosition::Left => bounds.x + width + padding, - HandlePosition::Right => bounds.x + bounds.width - padding, + IconPosition::Left => bounds.x + width + padding, + IconPosition::Right => bounds.x + bounds.width - padding, }, y: bounds.center_y() - f32::from(size) / 2.0, height: f32::from(size), diff --git a/src/widget.rs b/src/widget.rs index 948456d314..d3c160882f 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -124,7 +124,7 @@ pub mod text_input { //! Display fields that can be filled with text. pub use iced_native::widget::text_input::{ focus, move_cursor_to, move_cursor_to_end, move_cursor_to_front, - select_all, Appearance, Handle, HandlePosition, Id, StyleSheet, + select_all, Appearance, Icon, IconPosition, Id, StyleSheet, }; /// A field that can be filled with text. diff --git a/style/src/text_input.rs b/style/src/text_input.rs index 5e703e6176..73b0585234 100644 --- a/style/src/text_input.rs +++ b/style/src/text_input.rs @@ -12,8 +12,8 @@ pub struct Appearance { pub border_width: f32, /// The border [`Color`] of the text input. pub border_color: Color, - /// The handle [`Color`] of the text input. - pub handle_color: Color, + /// The icon [`Color`] of the text input. + pub icon_color: Color, } /// A set of rules that dictate the style of a text input. diff --git a/style/src/theme.rs b/style/src/theme.rs index 4837e10b28..0d974a19fc 100644 --- a/style/src/theme.rs +++ b/style/src/theme.rs @@ -1028,7 +1028,7 @@ impl text_input::StyleSheet for Theme { border_radius: 2.0, border_width: 1.0, border_color: palette.background.strong.color, - handle_color: palette.background.weak.text, + icon_color: palette.background.weak.text, } } @@ -1044,7 +1044,7 @@ impl text_input::StyleSheet for Theme { border_radius: 2.0, border_width: 1.0, border_color: palette.background.base.text, - handle_color: palette.background.weak.text, + icon_color: palette.background.weak.text, } } @@ -1060,7 +1060,7 @@ impl text_input::StyleSheet for Theme { border_radius: 2.0, border_width: 1.0, border_color: palette.primary.strong.color, - handle_color: palette.background.weak.text, + icon_color: palette.background.weak.text, } } From 898307e9ac8e11de275d7d4d58b93a6f24a1e800 Mon Sep 17 00:00:00 2001 From: Casper Storm Date: Mon, 20 Feb 2023 14:42:10 +0100 Subject: [PATCH 37/85] Removed text_input example in favor for Tour --- examples/text_input/Cargo.toml | 9 -- examples/text_input/README.md | 10 -- examples/text_input/src/main.rs | 93 ------------------ examples/{text_input => tour}/fonts/icons.ttf | Bin examples/tour/src/main.rs | 78 ++++++++++++--- 5 files changed, 66 insertions(+), 124 deletions(-) delete mode 100644 examples/text_input/Cargo.toml delete mode 100644 examples/text_input/README.md delete mode 100644 examples/text_input/src/main.rs rename examples/{text_input => tour}/fonts/icons.ttf (100%) diff --git a/examples/text_input/Cargo.toml b/examples/text_input/Cargo.toml deleted file mode 100644 index 5937ef8e71..0000000000 --- a/examples/text_input/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "text_input" -version = "0.1.0" -authors = ["Casper Rogild Storm"] -edition = "2021" -publish = false - -[dependencies] -iced = { path = "../..", features = ["debug"] } diff --git a/examples/text_input/README.md b/examples/text_input/README.md deleted file mode 100644 index 2b2d80590f..0000000000 --- a/examples/text_input/README.md +++ /dev/null @@ -1,10 +0,0 @@ -## TextInput - -A `TextInput` is a field that can be filled with text. - -You can run it with `cargo run`: -``` -cargo run --package text_input -``` - -[`main`]: src/main.rs diff --git a/examples/text_input/src/main.rs b/examples/text_input/src/main.rs deleted file mode 100644 index e0ba198324..0000000000 --- a/examples/text_input/src/main.rs +++ /dev/null @@ -1,93 +0,0 @@ -use iced::widget::{checkbox, column, container, text_input}; -use iced::{Element, Font, Length, Sandbox, Settings}; - -const ICON_FONT: Font = Font::External { - name: "Icons", - bytes: include_bytes!("../fonts/icons.ttf"), -}; - -pub fn main() -> iced::Result { - Example::run(Settings::default()) -} - -#[derive(Default)] -struct Example { - value: String, - is_showing_icon: bool, -} - -#[derive(Debug, Clone)] -enum Message { - Changed(String), - ToggleIcon(bool), -} - -impl Sandbox for Example { - type Message = Message; - - fn new() -> Self { - Self::default() - } - - fn title(&self) -> String { - String::from("Text Input - Iced") - } - - fn update(&mut self, message: Message) { - match message { - Message::Changed(value) => self.value = value, - Message::ToggleIcon(_) => { - self.is_showing_icon = !self.is_showing_icon - } - } - } - - fn view(&self) -> Element { - let checkbox = - checkbox("Icon", self.is_showing_icon, Message::ToggleIcon) - .spacing(5) - .text_size(16); - - let mut text_input = - text_input("Placeholder", self.value.as_str(), Message::Changed); - - if self.is_showing_icon { - text_input = text_input.icon(text_input::Icon { - font: ICON_FONT, - code_point: '\u{e900}', - size: Some(18), - position: text_input::IconPosition::Right, - }); - } - - let content = column!["What is blazing fast?", text_input, checkbox] - .width(Length::Units(200)) - .spacing(10); - - container(content) - .width(Length::Fill) - .height(Length::Fill) - .center_x() - .center_y() - .into() - } - - fn theme(&self) -> iced::Theme { - iced::Theme::default() - } - - fn style(&self) -> iced::theme::Application { - iced::theme::Application::default() - } - - fn scale_factor(&self) -> f64 { - 1.0 - } - - fn run(settings: Settings<()>) -> Result<(), iced::Error> - where - Self: 'static + Sized, - { - ::run(settings) - } -} diff --git a/examples/text_input/fonts/icons.ttf b/examples/tour/fonts/icons.ttf similarity index 100% rename from examples/text_input/fonts/icons.ttf rename to examples/tour/fonts/icons.ttf diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index de063d0086..5edee8503b 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -5,8 +5,14 @@ use iced::widget::{ scrollable, slider, text, text_input, toggler, vertical_space, }; use iced::widget::{Button, Column, Container, Slider}; +use iced::Font; use iced::{Color, Element, Length, Renderer, Sandbox, Settings}; +const ICON_FONT: Font = Font::External { + name: "Icons", + bytes: include_bytes!("../fonts/icons.ttf"), +}; + pub fn main() -> iced::Result { env_logger::init(); @@ -127,6 +133,7 @@ impl Steps { Step::TextInput { value: String::new(), is_secure: false, + is_showing_icon: false, }, Step::Debugger, Step::End, @@ -171,14 +178,32 @@ impl Steps { enum Step { Welcome, - Slider { value: u8 }, - RowsAndColumns { layout: Layout, spacing: u16 }, - Text { size: u16, color: Color }, - Radio { selection: Option }, - Toggler { can_continue: bool }, - Image { width: u16 }, + Slider { + value: u8, + }, + RowsAndColumns { + layout: Layout, + spacing: u16, + }, + Text { + size: u16, + color: Color, + }, + Radio { + selection: Option, + }, + Toggler { + can_continue: bool, + }, + Image { + width: u16, + }, Scrollable, - TextInput { value: String, is_secure: bool }, + TextInput { + value: String, + is_secure: bool, + is_showing_icon: bool, + }, Debugger, End, } @@ -194,6 +219,7 @@ pub enum StepMessage { ImageWidthChanged(u16), InputChanged(String), ToggleSecureInput(bool), + ToggleTextInputIcon(bool), DebugToggled(bool), TogglerChanged(bool), } @@ -256,6 +282,14 @@ impl<'a> Step { *can_continue = value; } } + StepMessage::ToggleTextInputIcon(toggle) => { + if let Step::TextInput { + is_showing_icon, .. + } = self + { + *is_showing_icon = toggle + } + } }; } @@ -303,9 +337,11 @@ impl<'a> Step { Self::rows_and_columns(*layout, *spacing) } Step::Scrollable => Self::scrollable(), - Step::TextInput { value, is_secure } => { - Self::text_input(value, *is_secure) - } + Step::TextInput { + value, + is_secure, + is_showing_icon, + } => Self::text_input(value, *is_secure, *is_showing_icon), Step::Debugger => Self::debugger(debug), Step::End => Self::end(), } @@ -530,8 +566,12 @@ impl<'a> Step { ) } - fn text_input(value: &str, is_secure: bool) -> Column<'a, StepMessage> { - let text_input = text_input( + fn text_input( + value: &str, + is_secure: bool, + is_showing_icon: bool, + ) -> Column<'a, StepMessage> { + let mut text_input = text_input( "Type something to continue...", value, StepMessage::InputChanged, @@ -539,6 +579,15 @@ impl<'a> Step { .padding(10) .size(30); + if is_showing_icon { + text_input = text_input.icon(text_input::Icon { + font: ICON_FONT, + code_point: '\u{e900}', + size: Some(35), + position: text_input::IconPosition::Right, + }); + } + Self::container("Text input") .push("Use a text input to ask for different kinds of information.") .push(if is_secure { @@ -551,6 +600,11 @@ impl<'a> Step { is_secure, StepMessage::ToggleSecureInput, )) + .push(checkbox( + "Show icon", + is_showing_icon, + StepMessage::ToggleTextInputIcon, + )) .push( "A text input produces a message every time it changes. It is \ very easy to keep track of its contents:", From 0e2fc99eb864800d2d1522c015054d84cad078f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 11 Apr 2023 05:13:56 +0200 Subject: [PATCH 38/85] Use `f32` for `Icon::size` and remove unnecessary conversions --- examples/tour/src/main.rs | 2 +- native/src/widget/text_input.rs | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index 5edee8503b..6a1380d7bd 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -583,7 +583,7 @@ impl<'a> Step { text_input = text_input.icon(text_input::Icon { font: ICON_FONT, code_point: '\u{e900}', - size: Some(35), + size: Some(35.0), position: text_input::IconPosition::Right, }); } diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 575f44368a..95e3b720ce 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -51,7 +51,7 @@ pub struct Icon { /// The unicode code point that will be used as the icon. pub code_point: char, /// Font size of the content. - pub size: Option, + pub size: Option, /// Position of the icon. pub position: IconPosition, } @@ -886,7 +886,7 @@ pub fn draw( position, } = icon; - let padding = f32::from(padding.horizontal()); + let padding = padding.horizontal(); let size = size.unwrap_or_else(|| renderer.default_size()); let width = renderer.measure_width( &code_point.to_string(), @@ -939,14 +939,14 @@ pub fn draw( position, } = icon; - let padding = f32::from(padding.horizontal()); + let padding = padding.horizontal(); let size = size.unwrap_or_else(|| renderer.default_size()); let width = renderer.measure_width(&code_point.to_string(), size, font.clone()); renderer.fill_text(Text { content: &code_point.to_string(), - size: f32::from(size), + size, font: font.clone(), color: appearance.icon_color, bounds: Rectangle { @@ -954,8 +954,8 @@ pub fn draw( IconPosition::Left => bounds.x + width + padding, IconPosition::Right => bounds.x + bounds.width - padding, }, - y: bounds.center_y() - f32::from(size) / 2.0, - height: f32::from(size), + y: bounds.center_y() - size / 2.0, + height: size, ..bounds }, horizontal_alignment: alignment::Horizontal::Right, From 9852b4b36442ef036f0b308f798e892ddaa06c2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 11 Apr 2023 05:45:55 +0200 Subject: [PATCH 39/85] Move `Icon` layout logic to `layout` in `text_input` Also add `Icon::spacing` field. --- examples/tour/src/main.rs | 5 +- native/src/widget/text_input.rs | 129 ++++++++++++++++---------------- 2 files changed, 67 insertions(+), 67 deletions(-) diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index 6a1380d7bd..8a1b8d5a41 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -138,7 +138,7 @@ impl Steps { Step::Debugger, Step::End, ], - current: 0, + current: 8, } } @@ -582,8 +582,9 @@ impl<'a> Step { if is_showing_icon { text_input = text_input.icon(text_input::Icon { font: ICON_FONT, - code_point: '\u{e900}', + code_point: '\u{E900}', size: Some(35.0), + spacing: 10.0, position: text_input::IconPosition::Right, }); } diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 95e3b720ce..f331f05a3e 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -52,6 +52,8 @@ pub struct Icon { pub code_point: char, /// Font size of the content. pub size: Option, + /// The spacing between the [`Icon`] and the text in a [`TextInput`]. + pub spacing: f32, /// Position of the icon. pub position: IconPosition, } @@ -235,7 +237,6 @@ where value.unwrap_or(&self.value), &self.placeholder, self.size, - self.padding, &self.font, self.is_secure, self.icon.as_ref(), @@ -272,7 +273,14 @@ where renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - layout(renderer, limits, self.width, self.padding, self.size) + layout( + renderer, + limits, + self.width, + self.padding, + self.size, + self.icon.as_ref(), + ) } fn operate( @@ -335,7 +343,6 @@ where &self.value, &self.placeholder, self.size, - self.padding, &self.font, self.is_secure, self.icon.as_ref(), @@ -431,6 +438,7 @@ pub fn layout( width: Length, padding: Padding, size: Option, + icon: Option<&Icon>, ) -> layout::Node where Renderer: text::Renderer, @@ -440,10 +448,51 @@ where let padding = padding.fit(Size::ZERO, limits.max()); let limits = limits.width(width).pad(padding).height(text_size); - let mut text = layout::Node::new(limits.resolve(Size::ZERO)); - text.move_to(Point::new(padding.left, padding.top)); + let text_bounds = limits.resolve(Size::ZERO); + + if let Some(icon) = icon { + let icon_width = renderer.measure_width( + &icon.code_point.to_string(), + icon.size.unwrap_or_else(|| renderer.default_size()), + icon.font.clone(), + ); + + let mut text_node = layout::Node::new( + text_bounds - Size::new(icon_width + icon.spacing, 0.0), + ); + + let mut icon_node = + layout::Node::new(Size::new(icon_width, text_bounds.height)); + + match icon.position { + IconPosition::Left => { + text_node.move_to(Point::new( + padding.left + icon_width + icon.spacing, + padding.top, + )); + + icon_node.move_to(Point::new(padding.left, padding.top)); + } + IconPosition::Right => { + text_node.move_to(Point::new(padding.left, padding.top)); + + icon_node.move_to(Point::new( + padding.left + text_bounds.width - icon_width, + padding.top, + )); + } + }; + + layout::Node::with_children( + text_bounds.pad(padding), + vec![text_node, icon_node], + ) + } else { + let mut text = layout::Node::new(text_bounds); + text.move_to(Point::new(padding.left, padding.top)); - layout::Node::with_children(text.size().pad(padding), vec![text]) + layout::Node::with_children(text.size().pad(padding), vec![text]) + } } /// Processes an [`Event`] and updates the [`State`] of a [`TextInput`] @@ -863,7 +912,6 @@ pub fn draw( value: &Value, placeholder: &str, size: Option, - padding: Padding, font: &Renderer::Font, is_secure: bool, icon: Option<&Icon>, @@ -876,40 +924,9 @@ pub fn draw( let value = secure_value.as_ref().unwrap_or(value); let bounds = layout.bounds(); - let text_bounds = { - let bounds = layout.children().next().unwrap().bounds(); - if let Some(icon) = icon { - let Icon { - font, - size, - code_point, - position, - } = icon; - - let padding = padding.horizontal(); - let size = size.unwrap_or_else(|| renderer.default_size()); - let width = renderer.measure_width( - &code_point.to_string(), - size, - font.clone(), - ); - - match position { - IconPosition::Left => Rectangle { - x: bounds.x + (width + padding), - width: bounds.width - (width + padding), - ..bounds - }, - IconPosition::Right => Rectangle { - x: bounds.x, - width: bounds.width - (width + padding), - ..bounds - }, - } - } else { - bounds - } - }; + + let mut children_layout = layout.children(); + let text_bounds = children_layout.next().unwrap().bounds(); let is_mouse_over = bounds.contains(cursor_position); @@ -932,33 +949,15 @@ pub fn draw( ); if let Some(icon) = icon { - let Icon { - size, - font, - code_point, - position, - } = icon; - - let padding = padding.horizontal(); - let size = size.unwrap_or_else(|| renderer.default_size()); - let width = - renderer.measure_width(&code_point.to_string(), size, font.clone()); + let icon_layout = children_layout.next().unwrap(); renderer.fill_text(Text { - content: &code_point.to_string(), - size, - font: font.clone(), + content: &icon.code_point.to_string(), + size: icon.size.unwrap_or_else(|| renderer.default_size()), + font: icon.font.clone(), color: appearance.icon_color, - bounds: Rectangle { - x: match position { - IconPosition::Left => bounds.x + width + padding, - IconPosition::Right => bounds.x + bounds.width - padding, - }, - y: bounds.center_y() - size / 2.0, - height: size, - ..bounds - }, - horizontal_alignment: alignment::Horizontal::Right, + bounds: icon_layout.bounds(), + horizontal_alignment: alignment::Horizontal::Left, vertical_alignment: alignment::Vertical::Top, }); } From 870b2fe513bd5b3fbcf3ba369afb14d68324aaa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 11 Apr 2023 05:48:12 +0200 Subject: [PATCH 40/85] Derive `Debug` for `text_input::Icon` --- native/src/widget/text_input.rs | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index f331f05a3e..a7fdcb1c67 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -32,19 +32,16 @@ use crate::{ pub use iced_style::text_input::{Appearance, StyleSheet}; /// The position of the [`Icon`]. -#[derive(Clone, Default, Debug)] +#[derive(Debug, Clone)] pub enum IconPosition { /// Position the [`Icon`] to the left. Left, - /// Position the [`Icon`] to the left. - /// - /// This is the default. - #[default] + /// Position the [`Icon`] to the right. Right, } /// The content of the [`Icon`]. -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct Icon { /// Font that will be used to display the `code_point`. pub font: Font, @@ -58,19 +55,6 @@ pub struct Icon { pub position: IconPosition, } -impl std::fmt::Debug for Icon -where - Renderer: text::Renderer, -{ - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Icon") - .field("code_point", &self.code_point) - .field("size", &self.size) - .field("position", &self.position) - .finish() - } -} - /// A field that can be filled with text. /// /// # Example From 57265ff211e8d040dce2a13e71409bdd6482204c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 11 Apr 2023 05:53:22 +0200 Subject: [PATCH 41/85] Move `Icon` definitions after `Widget` implementation --- native/src/widget/checkbox.rs | 22 +++++++-------- native/src/widget/text_input.rs | 48 ++++++++++++++++----------------- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/native/src/widget/checkbox.rs b/native/src/widget/checkbox.rs index 9b69e574f0..ad05a8e703 100644 --- a/native/src/widget/checkbox.rs +++ b/native/src/widget/checkbox.rs @@ -14,17 +14,6 @@ use crate::{ pub use iced_style::checkbox::{Appearance, StyleSheet}; -/// The icon in a [`Checkbox`]. -#[derive(Debug, Clone, PartialEq)] -pub struct Icon { - /// 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, -} - /// A box that can be checked. /// /// # Example @@ -319,3 +308,14 @@ where Element::new(checkbox) } } + +/// The icon in a [`Checkbox`]. +#[derive(Debug, Clone, PartialEq)] +pub struct Icon { + /// 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, +} diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index a7fdcb1c67..c43b735c5f 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -31,30 +31,6 @@ use crate::{ pub use iced_style::text_input::{Appearance, StyleSheet}; -/// The position of the [`Icon`]. -#[derive(Debug, Clone)] -pub enum IconPosition { - /// Position the [`Icon`] to the left. - Left, - /// Position the [`Icon`] to the right. - Right, -} - -/// The content of the [`Icon`]. -#[derive(Debug, Clone)] -pub struct Icon { - /// 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, - /// The spacing between the [`Icon`] and the text in a [`TextInput`]. - pub spacing: f32, - /// Position of the icon. - pub position: IconPosition, -} - /// A field that can be filled with text. /// /// # Example @@ -360,6 +336,30 @@ where } } +/// The content of the [`Icon`]. +#[derive(Debug, Clone)] +pub struct Icon { + /// The 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, + /// The font size of the content. + pub size: Option, + /// The spacing between the [`Icon`] and the text in a [`TextInput`]. + pub spacing: f32, + /// The position of the icon. + pub position: IconPosition, +} + +/// The position of an [`Icon`]. +#[derive(Debug, Clone)] +pub enum IconPosition { + /// Position the [`Icon`] on the left side of a [`TextInput`]. + Left, + /// Position the [`Icon`] on the right side of a [`TextInput`]. + Right, +} + /// The identifier of a [`TextInput`]. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Id(widget::Id); From cf9d8e01048845ee503a62eb55e634a76a0e9163 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 11 Apr 2023 05:54:51 +0200 Subject: [PATCH 42/85] Rename `IconPosition` to `Side` in `text_input` --- examples/tour/src/main.rs | 2 +- native/src/widget/text_input.rs | 18 +++++++++--------- src/widget.rs | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index 8a1b8d5a41..40ab33c2c3 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -585,7 +585,7 @@ impl<'a> Step { code_point: '\u{E900}', size: Some(35.0), spacing: 10.0, - position: text_input::IconPosition::Right, + side: text_input::Side::Right, }); } diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index c43b735c5f..bb3976453c 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -347,16 +347,16 @@ pub struct Icon { pub size: Option, /// The spacing between the [`Icon`] and the text in a [`TextInput`]. pub spacing: f32, - /// The position of the icon. - pub position: IconPosition, + /// The side of a [`TextInput`] where to display the [`Icon`]. + pub side: Side, } -/// The position of an [`Icon`]. +/// The side of a [`TextInput`]. #[derive(Debug, Clone)] -pub enum IconPosition { - /// Position the [`Icon`] on the left side of a [`TextInput`]. +pub enum Side { + /// The left side of a [`TextInput`]. Left, - /// Position the [`Icon`] on the right side of a [`TextInput`]. + /// The right side of a [`TextInput`]. Right, } @@ -448,8 +448,8 @@ where let mut icon_node = layout::Node::new(Size::new(icon_width, text_bounds.height)); - match icon.position { - IconPosition::Left => { + match icon.side { + Side::Left => { text_node.move_to(Point::new( padding.left + icon_width + icon.spacing, padding.top, @@ -457,7 +457,7 @@ where icon_node.move_to(Point::new(padding.left, padding.top)); } - IconPosition::Right => { + Side::Right => { text_node.move_to(Point::new(padding.left, padding.top)); icon_node.move_to(Point::new( diff --git a/src/widget.rs b/src/widget.rs index d3c160882f..c0ac716f17 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -124,7 +124,7 @@ pub mod text_input { //! Display fields that can be filled with text. pub use iced_native::widget::text_input::{ focus, move_cursor_to, move_cursor_to_end, move_cursor_to_front, - select_all, Appearance, Icon, IconPosition, Id, StyleSheet, + select_all, Appearance, Icon, Id, Side, StyleSheet, }; /// A field that can be filled with text. From c794d8ba788b388d4fb7a8ef1bba208b98e1bd02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 11 Apr 2023 05:56:34 +0200 Subject: [PATCH 43/85] Collapse `Font` import in `tour` example --- examples/tour/src/main.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index 40ab33c2c3..9ee386d47e 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -5,8 +5,7 @@ use iced::widget::{ scrollable, slider, text, text_input, toggler, vertical_space, }; use iced::widget::{Button, Column, Container, Slider}; -use iced::Font; -use iced::{Color, Element, Length, Renderer, Sandbox, Settings}; +use iced::{Color, Element, Font, Length, Renderer, Sandbox, Settings}; const ICON_FONT: Font = Font::External { name: "Icons", From aa0be30656e30d116e60a5d06557aea27442ce76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 11 Apr 2023 05:57:01 +0200 Subject: [PATCH 44/85] Move `ICON_FONT` constant in `tour` to `text_input` helper --- examples/tour/src/main.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index 9ee386d47e..e9b9fb3a0b 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -7,11 +7,6 @@ use iced::widget::{ use iced::widget::{Button, Column, Container, Slider}; use iced::{Color, Element, Font, Length, Renderer, Sandbox, Settings}; -const ICON_FONT: Font = Font::External { - name: "Icons", - bytes: include_bytes!("../fonts/icons.ttf"), -}; - pub fn main() -> iced::Result { env_logger::init(); @@ -570,6 +565,11 @@ impl<'a> Step { is_secure: bool, is_showing_icon: bool, ) -> Column<'a, StepMessage> { + const ICON_FONT: Font = Font::External { + name: "Icons", + bytes: include_bytes!("../fonts/icons.ttf"), + }; + let mut text_input = text_input( "Type something to continue...", value, From 45015e37d4123f01b546337e741820975fccf66a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 11 Apr 2023 05:57:31 +0200 Subject: [PATCH 45/85] Fix `current` step in `tour` --- examples/tour/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index e9b9fb3a0b..9086887701 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -132,7 +132,7 @@ impl Steps { Step::Debugger, Step::End, ], - current: 8, + current: 0, } } From 927c3a8caaefc241c48d6200266d286e20dc2076 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 11 Apr 2023 05:59:08 +0200 Subject: [PATCH 46/85] Reuse `text_bounds` in `text_input::layout` --- native/src/widget/text_input.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index bb3976453c..fd61a849d4 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -475,7 +475,7 @@ where let mut text = layout::Node::new(text_bounds); text.move_to(Point::new(padding.left, padding.top)); - layout::Node::with_children(text.size().pad(padding), vec![text]) + layout::Node::with_children(text_bounds.pad(padding), vec![text]) } } From ae7e6b3d481e420acf981fabbeff9745d4b5347d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 11 Apr 2023 07:46:54 +0200 Subject: [PATCH 47/85] Implement `subscription::channel` and simplify `unfold` --- examples/download_progress/src/download.rs | 17 ++-- examples/websocket/src/echo.rs | 100 +++++++++++---------- native/src/subscription.rs | 91 +++++++++++++------ 3 files changed, 122 insertions(+), 86 deletions(-) diff --git a/examples/download_progress/src/download.rs b/examples/download_progress/src/download.rs index 39dd843fb9..cd7647e814 100644 --- a/examples/download_progress/src/download.rs +++ b/examples/download_progress/src/download.rs @@ -18,10 +18,7 @@ pub struct Download { url: String, } -async fn download( - id: I, - state: State, -) -> (Option<(I, Progress)>, State) { +async fn download(id: I, state: State) -> ((I, Progress), State) { match state { State::Ready(url) => { let response = reqwest::get(&url).await; @@ -30,7 +27,7 @@ async fn download( Ok(response) => { if let Some(total) = response.content_length() { ( - Some((id, Progress::Started)), + (id, Progress::Started), State::Downloading { response, total, @@ -38,10 +35,10 @@ async fn download( }, ) } else { - (Some((id, Progress::Errored)), State::Finished) + ((id, Progress::Errored), State::Finished) } } - Err(_) => (Some((id, Progress::Errored)), State::Finished), + Err(_) => ((id, Progress::Errored), State::Finished), } } State::Downloading { @@ -55,7 +52,7 @@ async fn download( let percentage = (downloaded as f32 / total as f32) * 100.0; ( - Some((id, Progress::Advanced(percentage))), + (id, Progress::Advanced(percentage)), State::Downloading { response, total, @@ -63,8 +60,8 @@ async fn download( }, ) } - Ok(None) => (Some((id, Progress::Finished)), State::Finished), - Err(_) => (Some((id, Progress::Errored)), State::Finished), + Ok(None) => ((id, Progress::Finished), State::Finished), + Err(_) => ((id, Progress::Errored), State::Finished), }, State::Finished => { // We do not let the stream die, as it would start a diff --git a/examples/websocket/src/echo.rs b/examples/websocket/src/echo.rs index e74768a66b..4fabb660f6 100644 --- a/examples/websocket/src/echo.rs +++ b/examples/websocket/src/echo.rs @@ -13,63 +13,67 @@ use std::fmt; pub fn connect() -> Subscription { struct Connect; - subscription::unfold( + subscription::channel( std::any::TypeId::of::(), - State::Disconnected, - |state| async move { - match state { - State::Disconnected => { - const ECHO_SERVER: &str = "ws://localhost:3030"; - - match async_tungstenite::tokio::connect_async(ECHO_SERVER) + 100, + |mut output| async move { + let mut state = State::Disconnected; + + loop { + match &mut state { + State::Disconnected => { + const ECHO_SERVER: &str = "ws://localhost:3030"; + + match async_tungstenite::tokio::connect_async( + ECHO_SERVER, + ) .await - { - Ok((websocket, _)) => { - let (sender, receiver) = mpsc::channel(100); - - ( - Some(Event::Connected(Connection(sender))), - State::Connected(websocket, receiver), - ) - } - Err(_) => { - tokio::time::sleep( - tokio::time::Duration::from_secs(1), - ) - .await; + { + Ok((websocket, _)) => { + let (sender, receiver) = mpsc::channel(100); + + let _ = output + .send(Event::Connected(Connection(sender))) + .await; - (Some(Event::Disconnected), State::Disconnected) + state = State::Connected(websocket, receiver); + } + Err(_) => { + tokio::time::sleep( + tokio::time::Duration::from_secs(1), + ) + .await; + + let _ = output.send(Event::Disconnected).await; + } } } - } - State::Connected(mut websocket, mut input) => { - let mut fused_websocket = websocket.by_ref().fuse(); - - futures::select! { - received = fused_websocket.select_next_some() => { - match received { - Ok(tungstenite::Message::Text(message)) => { - ( - Some(Event::MessageReceived(Message::User(message))), - State::Connected(websocket, input) - ) - } - Ok(_) => { - (None, State::Connected(websocket, input)) - } - Err(_) => { - (Some(Event::Disconnected), State::Disconnected) + State::Connected(websocket, input) => { + let mut fused_websocket = websocket.by_ref().fuse(); + + futures::select! { + received = fused_websocket.select_next_some() => { + match received { + Ok(tungstenite::Message::Text(message)) => { + let _ = output.send(Event::MessageReceived(Message::User(message))).await; + } + Err(_) => { + let _ = output.send(Event::Disconnected).await; + + state = State::Disconnected; + } + Ok(_) => continue, } } - } - message = input.select_next_some() => { - let result = websocket.send(tungstenite::Message::Text(message.to_string())).await; + message = input.select_next_some() => { + let result = websocket.send(tungstenite::Message::Text(message.to_string())).await; + + if !result.is_ok() { + let _ = output.send(Event::Disconnected).await; - if result.is_ok() { - (None, State::Connected(websocket, input)) - } else { - (Some(Event::Disconnected), State::Disconnected) + state = State::Disconnected; + } } } } diff --git a/native/src/subscription.rs b/native/src/subscription.rs index 16e78e8296..0ff5e3201e 100644 --- a/native/src/subscription.rs +++ b/native/src/subscription.rs @@ -3,6 +3,8 @@ use crate::event::{self, Event}; use crate::window; use crate::Hasher; +use iced_futures::futures::channel::mpsc; +use iced_futures::futures::never::Never; use iced_futures::futures::{self, Future, Stream}; use iced_futures::{BoxStream, MaybeSend}; @@ -133,6 +135,27 @@ where /// [`Stream`] that will call the provided closure to produce every `Message`. /// /// The `id` will be used to uniquely identify the [`Subscription`]. +pub fn unfold( + id: I, + initial: T, + mut f: impl FnMut(T) -> Fut + MaybeSend + Sync + 'static, +) -> Subscription +where + I: Hash + 'static, + T: MaybeSend + 'static, + Fut: Future + MaybeSend + 'static, + Message: 'static + MaybeSend, +{ + use futures::future::FutureExt; + + run_with_id( + id, + futures::stream::unfold(initial, move |state| f(state).map(Some)), + ) +} + +/// Creates a [`Subscription`] that publishes the events sent from a [`Future`] +/// to an [`mpsc::Sender`] with the given bounds. /// /// # Creating an asynchronous worker with bidirectional communication /// You can leverage this helper to create a [`Subscription`] that spawns @@ -145,6 +168,7 @@ where /// ``` /// use iced_native::subscription::{self, Subscription}; /// use iced_native::futures::channel::mpsc; +/// use iced_native::futures::sink::SinkExt; /// /// pub enum Event { /// Ready(mpsc::Sender), @@ -165,27 +189,35 @@ where /// fn some_worker() -> Subscription { /// struct SomeWorker; /// -/// subscription::unfold(std::any::TypeId::of::(), State::Starting, |state| async move { -/// match state { -/// State::Starting => { -/// // Create channel -/// let (sender, receiver) = mpsc::channel(100); +/// subscription::channel(std::any::TypeId::of::(), 100, |mut output| async move { +/// let mut state = State::Starting; /// -/// (Some(Event::Ready(sender)), State::Ready(receiver)) -/// } -/// State::Ready(mut receiver) => { -/// use iced_native::futures::StreamExt; +/// loop { +/// match &mut state { +/// State::Starting => { +/// // Create channel +/// let (sender, receiver) = mpsc::channel(100); +/// +/// // Send the sender back to the application +/// output.send(Event::Ready(sender)).await; +/// +/// // We are ready to receive messages +/// state = State::Ready(receiver); +/// } +/// State::Ready(receiver) => { +/// use iced_native::futures::StreamExt; /// -/// // Read next input sent from `Application` -/// let input = receiver.select_next_some().await; +/// // Read next input sent from `Application` +/// let input = receiver.select_next_some().await; /// -/// match input { -/// Input::DoSomeWork => { -/// // Do some async work... +/// match input { +/// Input::DoSomeWork => { +/// // Do some async work... /// -/// // Finally, we can optionally return a message to tell the -/// // `Application` the work is done -/// (Some(Event::WorkFinished), State::Ready(receiver)) +/// // Finally, we can optionally produce a message to tell the +/// // `Application` the work is done +/// output.send(Event::WorkFinished).await; +/// } /// } /// } /// } @@ -198,25 +230,28 @@ where /// connection open. /// /// [`websocket`]: https://github.com/iced-rs/iced/tree/0.8/examples/websocket -pub fn unfold( +pub fn channel( id: I, - initial: T, - mut f: impl FnMut(T) -> Fut + MaybeSend + Sync + 'static, + size: usize, + f: impl Fn(mpsc::Sender) -> Fut + MaybeSend + Sync + 'static, ) -> Subscription where I: Hash + 'static, - T: MaybeSend + 'static, - Fut: Future, T)> + MaybeSend + 'static, + Fut: Future + MaybeSend + 'static, Message: 'static + MaybeSend, { - use futures::future::{self, FutureExt}; - use futures::stream::StreamExt; + use futures::stream::{self, StreamExt}; - run_with_id( + Subscription::from_recipe(Runner { id, - futures::stream::unfold(initial, move |state| f(state).map(Some)) - .filter_map(future::ready), - ) + spawn: move |_| { + let (sender, receiver) = mpsc::channel(size); + + let runner = stream::once(f(sender)).map(|_| unreachable!()); + + stream::select(receiver, runner) + }, + }) } struct Runner From 5908205a62eb160af745039b9ce0aa5329f984ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 11 Apr 2023 07:48:30 +0200 Subject: [PATCH 48/85] Use `127.0.0.1` instead of `localhost` in `websocket` example --- examples/websocket/src/echo.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/websocket/src/echo.rs b/examples/websocket/src/echo.rs index 4fabb660f6..f6c46e95f1 100644 --- a/examples/websocket/src/echo.rs +++ b/examples/websocket/src/echo.rs @@ -22,7 +22,7 @@ pub fn connect() -> Subscription { loop { match &mut state { State::Disconnected => { - const ECHO_SERVER: &str = "ws://localhost:3030"; + const ECHO_SERVER: &str = "ws://127.0.0.1:3030"; match async_tungstenite::tokio::connect_async( ECHO_SERVER, From 0ed54346b0d6271cf5874debef1de57f8322a249 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 11 Apr 2023 07:53:26 +0200 Subject: [PATCH 49/85] Use `Result::is_err` in `websocket` example --- examples/websocket/src/echo.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/websocket/src/echo.rs b/examples/websocket/src/echo.rs index f6c46e95f1..f9807172ae 100644 --- a/examples/websocket/src/echo.rs +++ b/examples/websocket/src/echo.rs @@ -69,7 +69,7 @@ pub fn connect() -> Subscription { message = input.select_next_some() => { let result = websocket.send(tungstenite::Message::Text(message.to_string())).await; - if !result.is_ok() { + if result.is_err() { let _ = output.send(Event::Disconnected).await; state = State::Disconnected; From f10e936f00d4b83dcacfdebd2727b1a5cd1add95 Mon Sep 17 00:00:00 2001 From: Dan Mishin Date: Fri, 3 Mar 2023 10:01:49 +0300 Subject: [PATCH 50/85] Introduce disabled state for `TextInput` --- examples/component/src/main.rs | 2 +- examples/integration_wgpu/src/controls.rs | 9 +- examples/lazy/src/main.rs | 9 +- examples/modal/src/main.rs | 14 +- examples/qr_code/src/main.rs | 12 +- examples/styling/src/main.rs | 11 +- examples/text_input/Cargo.toml | 11 + examples/text_input/README.md | 15 + examples/text_input/src/main.rs | 94 ++++++ examples/toast/src/main.rs | 6 +- examples/todos/src/main.rs | 29 +- examples/tour/src/main.rs | 11 +- examples/websocket/src/main.rs | 9 +- native/src/widget/helpers.rs | 3 +- native/src/widget/text_input.rs | 365 ++++++++++++---------- style/src/text_input.rs | 6 + style/src/theme.rs | 26 ++ 17 files changed, 394 insertions(+), 238 deletions(-) create mode 100644 examples/text_input/Cargo.toml create mode 100644 examples/text_input/README.md create mode 100644 examples/text_input/src/main.rs diff --git a/examples/component/src/main.rs b/examples/component/src/main.rs index bbf549e757..8be3f07663 100644 --- a/examples/component/src/main.rs +++ b/examples/component/src/main.rs @@ -141,8 +141,8 @@ mod numeric_input { .map(u32::to_string) .as_deref() .unwrap_or(""), - Event::InputChanged, ) + .on_change(Event::InputChanged) .padding(10), button("+", Event::IncrementPressed), ] diff --git a/examples/integration_wgpu/src/controls.rs b/examples/integration_wgpu/src/controls.rs index 533cb6e2a4..8c42513f66 100644 --- a/examples/integration_wgpu/src/controls.rs +++ b/examples/integration_wgpu/src/controls.rs @@ -100,11 +100,10 @@ impl Program for Controls { .size(14) .style(Color::WHITE), ) - .push(text_input( - "Placeholder", - text, - Message::TextChanged, - )), + .push( + text_input("Placeholder", text) + .on_change(Message::TextChanged), + ), ), ) .into() diff --git a/examples/lazy/src/main.rs b/examples/lazy/src/main.rs index 6512106f23..6b6dca2623 100644 --- a/examples/lazy/src/main.rs +++ b/examples/lazy/src/main.rs @@ -214,12 +214,9 @@ impl Sandbox for App { column![ scrollable(options).height(Length::Fill), row![ - text_input( - "Add a new option", - &self.input, - Message::InputChanged, - ) - .on_submit(Message::AddItem(self.input.clone())), + text_input("Add a new option", &self.input) + .on_change(Message::InputChanged) + .on_submit(Message::AddItem(self.input.clone())), button(text(format!("Toggle Order ({})", self.order))) .on_press(Message::ToggleOrder) ] diff --git a/examples/modal/src/main.rs b/examples/modal/src/main.rs index 54555684a0..1377f05492 100644 --- a/examples/modal/src/main.rs +++ b/examples/modal/src/main.rs @@ -133,18 +133,16 @@ impl Application for App { column![ column![ text("Email").size(12), - text_input( - "abc@123.com", - &self.email, - Message::Email - ) - .on_submit(Message::Submit) - .padding(5), + text_input("abc@123.com", &self.email,) + .on_change(Message::Email) + .on_submit(Message::Submit) + .padding(5), ] .spacing(5), column![ text("Password").size(12), - text_input("", &self.password, Message::Password) + text_input("", &self.password) + .on_change(Message::Password) .on_submit(Message::Submit) .password() .padding(5), diff --git a/examples/qr_code/src/main.rs b/examples/qr_code/src/main.rs index d8041745fe..0486b068bd 100644 --- a/examples/qr_code/src/main.rs +++ b/examples/qr_code/src/main.rs @@ -49,13 +49,11 @@ impl Sandbox for QRGenerator { .size(70) .style(Color::from([0.5, 0.5, 0.5])); - let input = text_input( - "Type the data of your QR code here...", - &self.data, - Message::DataChanged, - ) - .size(30) - .padding(15); + let input = + text_input("Type the data of your QR code here...", &self.data) + .on_change(Message::DataChanged) + .size(30) + .padding(15); let mut content = column![title, input] .width(700) diff --git a/examples/styling/src/main.rs b/examples/styling/src/main.rs index 448c9792a6..58f983df0c 100644 --- a/examples/styling/src/main.rs +++ b/examples/styling/src/main.rs @@ -90,13 +90,10 @@ impl Sandbox for Styling { }, ); - let text_input = text_input( - "Type something...", - &self.input_value, - Message::InputChanged, - ) - .padding(10) - .size(20); + let text_input = text_input("Type something...", &self.input_value) + .on_change(Message::InputChanged) + .padding(10) + .size(20); let button = button("Submit") .padding(10) diff --git a/examples/text_input/Cargo.toml b/examples/text_input/Cargo.toml new file mode 100644 index 0000000000..aead9896e4 --- /dev/null +++ b/examples/text_input/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "text_input" +authors = ["Dan Mishin "] +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +iced = { path = "../..", features = ["tokio"] } +tokio = { version = "1.26.0", features = ["time"] } diff --git a/examples/text_input/README.md b/examples/text_input/README.md new file mode 100644 index 0000000000..435989cce3 --- /dev/null +++ b/examples/text_input/README.md @@ -0,0 +1,15 @@ +# Text Input + +This example shows basic usage of text edit. +The button delays the change of the text field state to allow testing of the corner cases. + + + +You can run it with cargo run: +```bash +cargo run --package text_input +``` \ No newline at end of file diff --git a/examples/text_input/src/main.rs b/examples/text_input/src/main.rs new file mode 100644 index 0000000000..977b509939 --- /dev/null +++ b/examples/text_input/src/main.rs @@ -0,0 +1,94 @@ +use crate::Message::{StartTimer, TextEditModeChange}; +use iced::widget::{button, column, container, row, text, text_input}; +use iced::{ + executor, window, Application, Command, Element, Length, Renderer, + Settings, Theme, +}; +use tokio::time::{sleep, Duration}; + +fn main() -> iced::Result { + let settings = Settings { + window: window::Settings { + size: (700, 100), + ..window::Settings::default() + }, + ..Settings::default() + }; + + Example::run(settings) +} + +#[derive(Default)] +struct Example { + data: String, + text_edit_enabled: bool, +} + +#[derive(Debug, Clone)] +enum Message { + StartTimer, + TextEditModeChange, + TextInputChanged(String), +} + +impl Application for Example { + type Executor = executor::Default; + type Message = Message; + type Theme = Theme; + type Flags = (); + + fn new(_flags: Self::Flags) -> (Self, Command) { + (Self::default(), Command::none()) + } + + fn title(&self) -> String { + "TextInput example".into() + } + + fn update(&mut self, message: Self::Message) -> Command { + match message { + Message::TextEditModeChange => { + self.text_edit_enabled = !self.text_edit_enabled; + Command::none() + } + Message::TextInputChanged(updated_text) => { + self.data = updated_text; + Command::none() + } + StartTimer => { + let timer_f = async { + sleep(Duration::from_secs(3)).await; + }; + Command::perform(timer_f, |_| TextEditModeChange) + } + } + } + + fn view(&self) -> Element<'_, Self::Message, Renderer> { + let placeholder = if self.text_edit_enabled { + "Enabled TextEdit" + } else { + "Disabled TextEdit" + }; + + let mut txt_input = text_input(placeholder, &self.data); + + if self.text_edit_enabled { + txt_input = txt_input.on_change(Message::TextInputChanged); + } + + let btn = button("Enable/Disable").on_press(StartTimer); + let label = text( + "The mode will be changed after 3s when the button is pressed", + ); + + let content = row![txt_input, btn].spacing(10); + let content = column![content, label].spacing(10); + + container(content) + .width(Length::Shrink) + .height(Length::Shrink) + .padding(20) + .into() + } +} diff --git a/examples/toast/src/main.rs b/examples/toast/src/main.rs index e74b3ee62c..765afb8ff1 100644 --- a/examples/toast/src/main.rs +++ b/examples/toast/src/main.rs @@ -119,13 +119,15 @@ impl Application for App { column![ subtitle( "Title", - text_input("", &self.editing.title, Message::Title) + text_input("", &self.editing.title) + .on_change(Message::Title) .on_submit(Message::Add) .into() ), subtitle( "Message", - text_input("", &self.editing.body, Message::Body) + text_input("", &self.editing.body) + .on_change(Message::Body) .on_submit(Message::Add) .into() ), diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs index 6361667eb6..a6670626d6 100644 --- a/examples/todos/src/main.rs +++ b/examples/todos/src/main.rs @@ -204,15 +204,12 @@ impl Application for Todos { .style(Color::from([0.5, 0.5, 0.5])) .horizontal_alignment(alignment::Horizontal::Center); - let input = text_input( - "What needs to be done?", - input_value, - Message::InputChanged, - ) - .id(INPUT_ID.clone()) - .padding(15) - .size(30) - .on_submit(Message::CreateTask); + let input = text_input("What needs to be done?", input_value) + .on_change(Message::InputChanged) + .id(INPUT_ID.clone()) + .padding(15) + .size(30) + .on_submit(Message::CreateTask); let controls = view_controls(tasks, *filter); let filtered_tasks = @@ -375,14 +372,12 @@ impl Task { .into() } TaskState::Editing => { - let text_input = text_input( - "Describe your task...", - &self.description, - TaskMessage::DescriptionEdited, - ) - .id(Self::text_input_id(i)) - .on_submit(TaskMessage::FinishEdition) - .padding(10); + let text_input = + text_input("Describe your task...", &self.description) + .id(Self::text_input_id(i)) + .on_change(TaskMessage::DescriptionEdited) + .on_submit(TaskMessage::FinishEdition) + .padding(10); row![ text_input, diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index 9086887701..3b4cfd2d8b 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -570,13 +570,10 @@ impl<'a> Step { bytes: include_bytes!("../fonts/icons.ttf"), }; - let mut text_input = text_input( - "Type something to continue...", - value, - StepMessage::InputChanged, - ) - .padding(10) - .size(30); + let mut text_input = text_input("Type something to continue...", value) + .on_change(StepMessage::InputChanged) + .padding(10) + .size(30); if is_showing_icon { text_input = text_input.icon(text_input::Icon { diff --git a/examples/websocket/src/main.rs b/examples/websocket/src/main.rs index e617b8ce2b..1fda7f1888 100644 --- a/examples/websocket/src/main.rs +++ b/examples/websocket/src/main.rs @@ -125,12 +125,9 @@ impl Application for WebSocket { }; let new_message_input = { - let mut input = text_input( - "Type a message...", - &self.new_message, - Message::NewMessageChanged, - ) - .padding(10); + let mut input = text_input("Type a message...", &self.new_message) + .on_change(Message::NewMessageChanged) + .padding(10); let mut button = button( text("Send") diff --git a/native/src/widget/helpers.rs b/native/src/widget/helpers.rs index d13eca7598..0363fc99fb 100644 --- a/native/src/widget/helpers.rs +++ b/native/src/widget/helpers.rs @@ -171,14 +171,13 @@ where pub fn text_input<'a, Message, Renderer>( placeholder: &str, value: &str, - on_change: impl Fn(String) -> Message + 'a, ) -> widget::TextInput<'a, Message, Renderer> where Message: Clone, Renderer: crate::text::Renderer, Renderer::Theme: widget::text_input::StyleSheet, { - widget::TextInput::new(placeholder, value, on_change) + widget::TextInput::new(placeholder, value) } /// Creates a new [`Slider`]. diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index fd61a849d4..65897a6800 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -46,8 +46,8 @@ pub use iced_style::text_input::{Appearance, StyleSheet}; /// let input = TextInput::new( /// "This is the placeholder...", /// value, -/// Message::TextInputChanged, /// ) +/// .on_change(|_| Message::TextInputChanged) /// .padding(10); /// ``` /// ![Text input drawn by `iced_wgpu`](https://github.com/iced-rs/iced/blob/7760618fb112074bc40b148944521f312152012a/docs/images/text_input.png?raw=true) @@ -65,7 +65,7 @@ where width: Length, padding: Padding, size: Option, - on_change: Box Message + 'a>, + on_change: Option Message + 'a>>, on_paste: Option Message + 'a>>, on_submit: Option, icon: Option>, @@ -82,12 +82,8 @@ where /// /// It expects: /// - a placeholder, - /// - the current value, and - /// - a function that produces a message when the [`TextInput`] changes. - pub fn new(placeholder: &str, value: &str, on_change: F) -> Self - where - F: 'a + Fn(String) -> Message, - { + /// - the current value + pub fn new(placeholder: &str, value: &str) -> Self { TextInput { id: None, placeholder: String::from(placeholder), @@ -97,7 +93,7 @@ where width: Length::Fill, padding: Padding::new(5.0), size: None, - on_change: Box::new(on_change), + on_change: None, on_paste: None, on_submit: None, icon: None, @@ -175,6 +171,16 @@ where self } + /// Sets the callback which is called when the text gets changed + /// If not specified, the widget will be disabled + pub fn on_change(mut self, callback: F) -> Self + where + F: 'a + Fn(String) -> Message, + { + self.on_change = Some(Box::new(callback)); + self + } + /// Draws the [`TextInput`] with the given [`Renderer`], overriding its /// [`Value`] if provided. /// @@ -201,6 +207,7 @@ where self.is_secure, self.icon.as_ref(), &self.style, + self.on_change.is_none(), ) } } @@ -277,7 +284,7 @@ where self.size, &self.font, self.is_secure, - self.on_change.as_ref(), + self.on_change.as_deref(), self.on_paste.as_deref(), &self.on_submit, || tree.state.downcast_mut::(), @@ -307,6 +314,7 @@ where self.is_secure, self.icon.as_ref(), &self.style, + self.on_change.is_none(), ) } @@ -492,7 +500,7 @@ pub fn update<'a, Message, Renderer>( size: Option, font: &Renderer::Font, is_secure: bool, - on_change: &dyn Fn(String) -> Message, + on_change: Option<&dyn Fn(String) -> Message>, on_paste: Option<&dyn Fn(String) -> Message>, on_submit: &Option, state: impl FnOnce() -> &'a mut State, @@ -501,11 +509,14 @@ where Message: Clone, Renderer: text::Renderer, { + let is_disabled = on_change.is_none(); + match event { Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) | Event::Touch(touch::Event::FingerPressed { .. }) => { let state = state(); let is_clicked = layout.bounds().contains(cursor_position); + let is_clicked = is_clicked && !is_disabled; state.is_focused = if is_clicked { state.is_focused.or_else(|| { @@ -635,20 +646,22 @@ where let state = state(); if let Some(focus) = &mut state.is_focused { - if state.is_pasting.is_none() - && !state.keyboard_modifiers.command() - && !c.is_control() - { - let mut editor = Editor::new(value, &mut state.cursor); + if let Some(on_change) = on_change { + if state.is_pasting.is_none() + && !state.keyboard_modifiers.command() + && !c.is_control() + { + let mut editor = Editor::new(value, &mut state.cursor); - editor.insert(c); + editor.insert(c); - let message = (on_change)(editor.contents()); - shell.publish(message); + let message = (on_change)(editor.contents()); + shell.publish(message); - focus.updated_at = Instant::now(); + focus.updated_at = Instant::now(); - return event::Status::Captured; + return event::Status::Captured; + } } } } @@ -656,181 +669,188 @@ where let state = state(); if let Some(focus) = &mut state.is_focused { - let modifiers = state.keyboard_modifiers; - focus.updated_at = Instant::now(); + if let Some(on_change) = on_change { + let modifiers = state.keyboard_modifiers; + focus.updated_at = Instant::now(); - match key_code { - keyboard::KeyCode::Enter - | keyboard::KeyCode::NumpadEnter => { - if let Some(on_submit) = on_submit.clone() { - shell.publish(on_submit); + match key_code { + keyboard::KeyCode::Enter + | keyboard::KeyCode::NumpadEnter => { + if let Some(on_submit) = on_submit.clone() { + shell.publish(on_submit); + } } - } - keyboard::KeyCode::Backspace => { - if platform::is_jump_modifier_pressed(modifiers) - && state.cursor.selection(value).is_none() - { - if is_secure { - let cursor_pos = state.cursor.end(value); - state.cursor.select_range(0, cursor_pos); - } else { - state.cursor.select_left_by_words(value); + keyboard::KeyCode::Backspace => { + if platform::is_jump_modifier_pressed(modifiers) + && state.cursor.selection(value).is_none() + { + if is_secure { + let cursor_pos = state.cursor.end(value); + state.cursor.select_range(0, cursor_pos); + } else { + state.cursor.select_left_by_words(value); + } } + + let mut editor = + Editor::new(value, &mut state.cursor); + editor.backspace(); + + let message = (on_change)(editor.contents()); + shell.publish(message); } + keyboard::KeyCode::Delete => { + if platform::is_jump_modifier_pressed(modifiers) + && state.cursor.selection(value).is_none() + { + if is_secure { + let cursor_pos = state.cursor.end(value); + state + .cursor + .select_range(cursor_pos, value.len()); + } else { + state.cursor.select_right_by_words(value); + } + } - let mut editor = Editor::new(value, &mut state.cursor); - editor.backspace(); + let mut editor = + Editor::new(value, &mut state.cursor); + editor.delete(); - let message = (on_change)(editor.contents()); - shell.publish(message); - } - keyboard::KeyCode::Delete => { - if platform::is_jump_modifier_pressed(modifiers) - && state.cursor.selection(value).is_none() - { - if is_secure { - let cursor_pos = state.cursor.end(value); - state - .cursor - .select_range(cursor_pos, value.len()); + let message = (on_change)(editor.contents()); + shell.publish(message); + } + keyboard::KeyCode::Left => { + if platform::is_jump_modifier_pressed(modifiers) + && !is_secure + { + if modifiers.shift() { + state.cursor.select_left_by_words(value); + } else { + state.cursor.move_left_by_words(value); + } + } else if modifiers.shift() { + state.cursor.select_left(value) } else { - state.cursor.select_right_by_words(value); + state.cursor.move_left(value); } } - - let mut editor = Editor::new(value, &mut state.cursor); - editor.delete(); - - let message = (on_change)(editor.contents()); - shell.publish(message); - } - keyboard::KeyCode::Left => { - if platform::is_jump_modifier_pressed(modifiers) - && !is_secure - { - if modifiers.shift() { - state.cursor.select_left_by_words(value); + keyboard::KeyCode::Right => { + if platform::is_jump_modifier_pressed(modifiers) + && !is_secure + { + if modifiers.shift() { + state.cursor.select_right_by_words(value); + } else { + state.cursor.move_right_by_words(value); + } + } else if modifiers.shift() { + state.cursor.select_right(value) } else { - state.cursor.move_left_by_words(value); + state.cursor.move_right(value); } - } else if modifiers.shift() { - state.cursor.select_left(value) - } else { - state.cursor.move_left(value); } - } - keyboard::KeyCode::Right => { - if platform::is_jump_modifier_pressed(modifiers) - && !is_secure - { + keyboard::KeyCode::Home => { if modifiers.shift() { - state.cursor.select_right_by_words(value); + state + .cursor + .select_range(state.cursor.start(value), 0); } else { - state.cursor.move_right_by_words(value); + state.cursor.move_to(0); } - } else if modifiers.shift() { - state.cursor.select_right(value) - } else { - state.cursor.move_right(value); - } - } - keyboard::KeyCode::Home => { - if modifiers.shift() { - state - .cursor - .select_range(state.cursor.start(value), 0); - } else { - state.cursor.move_to(0); } - } - keyboard::KeyCode::End => { - if modifiers.shift() { - state.cursor.select_range( - state.cursor.start(value), - value.len(), - ); - } else { - state.cursor.move_to(value.len()); + keyboard::KeyCode::End => { + if modifiers.shift() { + state.cursor.select_range( + state.cursor.start(value), + value.len(), + ); + } else { + state.cursor.move_to(value.len()); + } } - } - keyboard::KeyCode::C - if state.keyboard_modifiers.command() => - { - if let Some((start, end)) = - state.cursor.selection(value) + keyboard::KeyCode::C + if state.keyboard_modifiers.command() => { - clipboard - .write(value.select(start, end).to_string()); + if let Some((start, end)) = + state.cursor.selection(value) + { + clipboard.write( + value.select(start, end).to_string(), + ); + } } - } - keyboard::KeyCode::X - if state.keyboard_modifiers.command() => - { - if let Some((start, end)) = - state.cursor.selection(value) + keyboard::KeyCode::X + if state.keyboard_modifiers.command() => { - clipboard - .write(value.select(start, end).to_string()); - } - - let mut editor = Editor::new(value, &mut state.cursor); - editor.delete(); - - let message = (on_change)(editor.contents()); - shell.publish(message); - } - keyboard::KeyCode::V => { - if state.keyboard_modifiers.command() { - let content = match state.is_pasting.take() { - Some(content) => content, - None => { - let content: String = clipboard - .read() - .unwrap_or_default() - .chars() - .filter(|c| !c.is_control()) - .collect(); - - Value::new(&content) - } - }; + if let Some((start, end)) = + state.cursor.selection(value) + { + clipboard.write( + value.select(start, end).to_string(), + ); + } let mut editor = Editor::new(value, &mut state.cursor); + editor.delete(); - editor.paste(content.clone()); - - let message = if let Some(paste) = &on_paste { - (paste)(editor.contents()) - } else { - (on_change)(editor.contents()) - }; + let message = (on_change)(editor.contents()); shell.publish(message); - - state.is_pasting = Some(content); - } else { - state.is_pasting = None; } - } - keyboard::KeyCode::A - if state.keyboard_modifiers.command() => - { - state.cursor.select_all(value); - } - keyboard::KeyCode::Escape => { - state.is_focused = None; - state.is_dragging = false; - state.is_pasting = None; + keyboard::KeyCode::V => { + if state.keyboard_modifiers.command() { + let content = match state.is_pasting.take() { + Some(content) => content, + None => { + let content: String = clipboard + .read() + .unwrap_or_default() + .chars() + .filter(|c| !c.is_control()) + .collect(); + + Value::new(&content) + } + }; + + let mut editor = + Editor::new(value, &mut state.cursor); + + editor.paste(content.clone()); + + let message = if let Some(paste) = &on_paste { + (paste)(editor.contents()) + } else { + (on_change)(editor.contents()) + }; + shell.publish(message); + + state.is_pasting = Some(content); + } else { + state.is_pasting = None; + } + } + keyboard::KeyCode::A + if state.keyboard_modifiers.command() => + { + state.cursor.select_all(value); + } + keyboard::KeyCode::Escape => { + state.is_focused = None; + state.is_dragging = false; + state.is_pasting = None; - state.keyboard_modifiers = - keyboard::Modifiers::default(); - } - keyboard::KeyCode::Tab - | keyboard::KeyCode::Up - | keyboard::KeyCode::Down => { - return event::Status::Ignored; + state.keyboard_modifiers = + keyboard::Modifiers::default(); + } + keyboard::KeyCode::Tab + | keyboard::KeyCode::Up + | keyboard::KeyCode::Down => { + return event::Status::Ignored; + } + _ => {} } - _ => {} } return event::Status::Captured; @@ -900,6 +920,7 @@ pub fn draw( is_secure: bool, icon: Option<&Icon>, style: &::Style, + is_disabled: bool, ) where Renderer: text::Renderer, Renderer::Theme: StyleSheet, @@ -914,7 +935,9 @@ pub fn draw( let is_mouse_over = bounds.contains(cursor_position); - let appearance = if state.is_focused() { + let appearance = if is_disabled { + theme.disabled(style) + } else if state.is_focused() { theme.focused(style) } else if is_mouse_over { theme.hovered(style) @@ -968,7 +991,7 @@ pub fn draw( % 2 == 0; - let cursor = if is_cursor_visible { + let cursor = if is_cursor_visible && !is_disabled { Some(( renderer::Quad { bounds: Rectangle { @@ -1057,6 +1080,8 @@ pub fn draw( content: if text.is_empty() { placeholder } else { &text }, color: if text.is_empty() { theme.placeholder_color(style) + } else if is_disabled { + theme.disabled_color(style) } else { theme.value_color(style) }, diff --git a/style/src/text_input.rs b/style/src/text_input.rs index 73b0585234..2616ad5a79 100644 --- a/style/src/text_input.rs +++ b/style/src/text_input.rs @@ -33,6 +33,9 @@ pub trait StyleSheet { /// Produces the [`Color`] of the value of a text input. fn value_color(&self, style: &Self::Style) -> Color; + /// Produces the [`Color`] of the value of a disabled text input. + fn disabled_color(&self, style: &Self::Style) -> Color; + /// Produces the [`Color`] of the selection of a text input. fn selection_color(&self, style: &Self::Style) -> Color; @@ -40,4 +43,7 @@ pub trait StyleSheet { fn hovered(&self, style: &Self::Style) -> Appearance { self.focused(style) } + + /// Produces the style of a disabled text input. + fn disabled(&self, style: &Self::Style) -> Appearance; } diff --git a/style/src/theme.rs b/style/src/theme.rs index 0d974a19fc..e13edb050a 100644 --- a/style/src/theme.rs +++ b/style/src/theme.rs @@ -1093,4 +1093,30 @@ impl text_input::StyleSheet for Theme { palette.primary.weak.color } + + fn disabled(&self, style: &Self::Style) -> text_input::Appearance { + if let TextInput::Custom(custom) = style { + return custom.disabled(self); + } + + let palette = self.extended_palette(); + + text_input::Appearance { + background: palette.background.base.color.into(), + border_radius: 2.0, + border_width: 1.0, + border_color: palette.background.weak.color, + icon_color: palette.background.weak.color, + } + } + + fn disabled_color(&self, style: &Self::Style) -> Color { + if let TextInput::Custom(custom) = style { + return custom.value_color(self); + } + + let palette = self.extended_palette(); + + palette.secondary.strong.color + } } From e6a93e960c3ac71a74f11555fd2d225c185f6e8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 12 Apr 2023 04:13:36 +0200 Subject: [PATCH 51/85] Rename `on_change` to `on_input` for `TextInput` --- examples/component/src/main.rs | 2 +- examples/integration_wgpu/src/controls.rs | 2 +- examples/lazy/src/main.rs | 2 +- examples/modal/src/main.rs | 4 +- examples/qr_code/src/main.rs | 2 +- examples/styling/src/main.rs | 2 +- examples/text_input/src/main.rs | 2 +- examples/toast/src/main.rs | 4 +- examples/todos/src/main.rs | 8 +- examples/tour/src/main.rs | 2 +- examples/websocket/src/main.rs | 2 +- native/src/widget/text_input.rs | 383 +++++++++++----------- 12 files changed, 204 insertions(+), 211 deletions(-) diff --git a/examples/component/src/main.rs b/examples/component/src/main.rs index 8be3f07663..21c2747c81 100644 --- a/examples/component/src/main.rs +++ b/examples/component/src/main.rs @@ -142,7 +142,7 @@ mod numeric_input { .as_deref() .unwrap_or(""), ) - .on_change(Event::InputChanged) + .on_input(Event::InputChanged) .padding(10), button("+", Event::IncrementPressed), ] diff --git a/examples/integration_wgpu/src/controls.rs b/examples/integration_wgpu/src/controls.rs index 8c42513f66..42623b15b1 100644 --- a/examples/integration_wgpu/src/controls.rs +++ b/examples/integration_wgpu/src/controls.rs @@ -102,7 +102,7 @@ impl Program for Controls { ) .push( text_input("Placeholder", text) - .on_change(Message::TextChanged), + .on_input(Message::TextChanged), ), ), ) diff --git a/examples/lazy/src/main.rs b/examples/lazy/src/main.rs index 6b6dca2623..55b13bcf04 100644 --- a/examples/lazy/src/main.rs +++ b/examples/lazy/src/main.rs @@ -215,7 +215,7 @@ impl Sandbox for App { scrollable(options).height(Length::Fill), row![ text_input("Add a new option", &self.input) - .on_change(Message::InputChanged) + .on_input(Message::InputChanged) .on_submit(Message::AddItem(self.input.clone())), button(text(format!("Toggle Order ({})", self.order))) .on_press(Message::ToggleOrder) diff --git a/examples/modal/src/main.rs b/examples/modal/src/main.rs index 1377f05492..490384756b 100644 --- a/examples/modal/src/main.rs +++ b/examples/modal/src/main.rs @@ -134,7 +134,7 @@ impl Application for App { column![ text("Email").size(12), text_input("abc@123.com", &self.email,) - .on_change(Message::Email) + .on_input(Message::Email) .on_submit(Message::Submit) .padding(5), ] @@ -142,7 +142,7 @@ impl Application for App { column![ text("Password").size(12), text_input("", &self.password) - .on_change(Message::Password) + .on_input(Message::Password) .on_submit(Message::Submit) .password() .padding(5), diff --git a/examples/qr_code/src/main.rs b/examples/qr_code/src/main.rs index 0486b068bd..867ebfa415 100644 --- a/examples/qr_code/src/main.rs +++ b/examples/qr_code/src/main.rs @@ -51,7 +51,7 @@ impl Sandbox for QRGenerator { let input = text_input("Type the data of your QR code here...", &self.data) - .on_change(Message::DataChanged) + .on_input(Message::DataChanged) .size(30) .padding(15); diff --git a/examples/styling/src/main.rs b/examples/styling/src/main.rs index 58f983df0c..e2015bac2b 100644 --- a/examples/styling/src/main.rs +++ b/examples/styling/src/main.rs @@ -91,7 +91,7 @@ impl Sandbox for Styling { ); let text_input = text_input("Type something...", &self.input_value) - .on_change(Message::InputChanged) + .on_input(Message::InputChanged) .padding(10) .size(20); diff --git a/examples/text_input/src/main.rs b/examples/text_input/src/main.rs index 977b509939..418593f4fc 100644 --- a/examples/text_input/src/main.rs +++ b/examples/text_input/src/main.rs @@ -74,7 +74,7 @@ impl Application for Example { let mut txt_input = text_input(placeholder, &self.data); if self.text_edit_enabled { - txt_input = txt_input.on_change(Message::TextInputChanged); + txt_input = txt_input.on_input(Message::TextInputChanged); } let btn = button("Enable/Disable").on_press(StartTimer); diff --git a/examples/toast/src/main.rs b/examples/toast/src/main.rs index 765afb8ff1..b4b4e007a1 100644 --- a/examples/toast/src/main.rs +++ b/examples/toast/src/main.rs @@ -120,14 +120,14 @@ impl Application for App { subtitle( "Title", text_input("", &self.editing.title) - .on_change(Message::Title) + .on_input(Message::Title) .on_submit(Message::Add) .into() ), subtitle( "Message", text_input("", &self.editing.body) - .on_change(Message::Body) + .on_input(Message::Body) .on_submit(Message::Add) .into() ), diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs index a6670626d6..99cdb8f992 100644 --- a/examples/todos/src/main.rs +++ b/examples/todos/src/main.rs @@ -205,11 +205,11 @@ impl Application for Todos { .horizontal_alignment(alignment::Horizontal::Center); let input = text_input("What needs to be done?", input_value) - .on_change(Message::InputChanged) .id(INPUT_ID.clone()) + .on_input(Message::InputChanged) + .on_submit(Message::CreateTask) .padding(15) - .size(30) - .on_submit(Message::CreateTask); + .size(30); let controls = view_controls(tasks, *filter); let filtered_tasks = @@ -375,7 +375,7 @@ impl Task { let text_input = text_input("Describe your task...", &self.description) .id(Self::text_input_id(i)) - .on_change(TaskMessage::DescriptionEdited) + .on_input(TaskMessage::DescriptionEdited) .on_submit(TaskMessage::FinishEdition) .padding(10); diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index 3b4cfd2d8b..16ee19c0bb 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -571,7 +571,7 @@ impl<'a> Step { }; let mut text_input = text_input("Type something to continue...", value) - .on_change(StepMessage::InputChanged) + .on_input(StepMessage::InputChanged) .padding(10) .size(30); diff --git a/examples/websocket/src/main.rs b/examples/websocket/src/main.rs index 1fda7f1888..920189f541 100644 --- a/examples/websocket/src/main.rs +++ b/examples/websocket/src/main.rs @@ -126,7 +126,7 @@ impl Application for WebSocket { let new_message_input = { let mut input = text_input("Type a message...", &self.new_message) - .on_change(Message::NewMessageChanged) + .on_input(Message::NewMessageChanged) .padding(10); let mut button = button( diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 65897a6800..0cbc65db99 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -47,7 +47,7 @@ pub use iced_style::text_input::{Appearance, StyleSheet}; /// "This is the placeholder...", /// value, /// ) -/// .on_change(|_| Message::TextInputChanged) +/// .on_input(Message::TextInputChanged) /// .padding(10); /// ``` /// ![Text input drawn by `iced_wgpu`](https://github.com/iced-rs/iced/blob/7760618fb112074bc40b148944521f312152012a/docs/images/text_input.png?raw=true) @@ -65,7 +65,7 @@ where width: Length, padding: Padding, size: Option, - on_change: Option Message + 'a>>, + on_input: Option Message + 'a>>, on_paste: Option Message + 'a>>, on_submit: Option, icon: Option>, @@ -93,7 +93,7 @@ where width: Length::Fill, padding: Padding::new(5.0), size: None, - on_change: None, + on_input: None, on_paste: None, on_submit: None, icon: None, @@ -113,6 +113,23 @@ where self } + /// Sets the callback which is called when the text gets changed + /// If not specified, the widget will be disabled + pub fn on_input(mut self, callback: F) -> Self + where + F: 'a + Fn(String) -> Message, + { + self.on_input = Some(Box::new(callback)); + self + } + + /// Sets the message that should be produced when the [`TextInput`] is + /// focused and the enter key is pressed. + pub fn on_submit(mut self, message: Message) -> Self { + self.on_submit = Some(message); + self + } + /// Sets the message that should be produced when some text is pasted into /// the [`TextInput`]. pub fn on_paste( @@ -155,13 +172,6 @@ where self } - /// Sets the message that should be produced when the [`TextInput`] is - /// focused and the enter key is pressed. - pub fn on_submit(mut self, message: Message) -> Self { - self.on_submit = Some(message); - self - } - /// Sets the style of the [`TextInput`]. pub fn style( mut self, @@ -171,16 +181,6 @@ where self } - /// Sets the callback which is called when the text gets changed - /// If not specified, the widget will be disabled - pub fn on_change(mut self, callback: F) -> Self - where - F: 'a + Fn(String) -> Message, - { - self.on_change = Some(Box::new(callback)); - self - } - /// Draws the [`TextInput`] with the given [`Renderer`], overriding its /// [`Value`] if provided. /// @@ -204,10 +204,10 @@ where &self.placeholder, self.size, &self.font, + self.on_input.is_none(), self.is_secure, self.icon.as_ref(), &self.style, - self.on_change.is_none(), ) } } @@ -284,7 +284,7 @@ where self.size, &self.font, self.is_secure, - self.on_change.as_deref(), + self.on_input.as_deref(), self.on_paste.as_deref(), &self.on_submit, || tree.state.downcast_mut::(), @@ -311,10 +311,10 @@ where &self.placeholder, self.size, &self.font, + self.on_input.is_none(), self.is_secure, self.icon.as_ref(), &self.style, - self.on_change.is_none(), ) } @@ -500,7 +500,7 @@ pub fn update<'a, Message, Renderer>( size: Option, font: &Renderer::Font, is_secure: bool, - on_change: Option<&dyn Fn(String) -> Message>, + on_input: Option<&dyn Fn(String) -> Message>, on_paste: Option<&dyn Fn(String) -> Message>, on_submit: &Option, state: impl FnOnce() -> &'a mut State, @@ -509,14 +509,12 @@ where Message: Clone, Renderer: text::Renderer, { - let is_disabled = on_change.is_none(); - match event { Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) | Event::Touch(touch::Event::FingerPressed { .. }) => { let state = state(); - let is_clicked = layout.bounds().contains(cursor_position); - let is_clicked = is_clicked && !is_disabled; + let is_clicked = + layout.bounds().contains(cursor_position) && on_input.is_some(); state.is_focused = if is_clicked { state.is_focused.or_else(|| { @@ -646,22 +644,22 @@ where let state = state(); if let Some(focus) = &mut state.is_focused { - if let Some(on_change) = on_change { - if state.is_pasting.is_none() - && !state.keyboard_modifiers.command() - && !c.is_control() - { - let mut editor = Editor::new(value, &mut state.cursor); + let Some(on_input) = on_input else { return event::Status::Ignored }; - editor.insert(c); + if state.is_pasting.is_none() + && !state.keyboard_modifiers.command() + && !c.is_control() + { + let mut editor = Editor::new(value, &mut state.cursor); - let message = (on_change)(editor.contents()); - shell.publish(message); + editor.insert(c); - focus.updated_at = Instant::now(); + let message = (on_input)(editor.contents()); + shell.publish(message); - return event::Status::Captured; - } + focus.updated_at = Instant::now(); + + return event::Status::Captured; } } } @@ -669,188 +667,183 @@ where let state = state(); if let Some(focus) = &mut state.is_focused { - if let Some(on_change) = on_change { - let modifiers = state.keyboard_modifiers; - focus.updated_at = Instant::now(); + let Some(on_input) = on_input else { return event::Status::Ignored }; - match key_code { - keyboard::KeyCode::Enter - | keyboard::KeyCode::NumpadEnter => { - if let Some(on_submit) = on_submit.clone() { - shell.publish(on_submit); - } - } - keyboard::KeyCode::Backspace => { - if platform::is_jump_modifier_pressed(modifiers) - && state.cursor.selection(value).is_none() - { - if is_secure { - let cursor_pos = state.cursor.end(value); - state.cursor.select_range(0, cursor_pos); - } else { - state.cursor.select_left_by_words(value); - } - } - - let mut editor = - Editor::new(value, &mut state.cursor); - editor.backspace(); - - let message = (on_change)(editor.contents()); - shell.publish(message); - } - keyboard::KeyCode::Delete => { - if platform::is_jump_modifier_pressed(modifiers) - && state.cursor.selection(value).is_none() - { - if is_secure { - let cursor_pos = state.cursor.end(value); - state - .cursor - .select_range(cursor_pos, value.len()); - } else { - state.cursor.select_right_by_words(value); - } - } + let modifiers = state.keyboard_modifiers; + focus.updated_at = Instant::now(); - let mut editor = - Editor::new(value, &mut state.cursor); - editor.delete(); - - let message = (on_change)(editor.contents()); - shell.publish(message); + match key_code { + keyboard::KeyCode::Enter + | keyboard::KeyCode::NumpadEnter => { + if let Some(on_submit) = on_submit.clone() { + shell.publish(on_submit); } - keyboard::KeyCode::Left => { - if platform::is_jump_modifier_pressed(modifiers) - && !is_secure - { - if modifiers.shift() { - state.cursor.select_left_by_words(value); - } else { - state.cursor.move_left_by_words(value); - } - } else if modifiers.shift() { - state.cursor.select_left(value) + } + keyboard::KeyCode::Backspace => { + if platform::is_jump_modifier_pressed(modifiers) + && state.cursor.selection(value).is_none() + { + if is_secure { + let cursor_pos = state.cursor.end(value); + state.cursor.select_range(0, cursor_pos); } else { - state.cursor.move_left(value); + state.cursor.select_left_by_words(value); } } - keyboard::KeyCode::Right => { - if platform::is_jump_modifier_pressed(modifiers) - && !is_secure - { - if modifiers.shift() { - state.cursor.select_right_by_words(value); - } else { - state.cursor.move_right_by_words(value); - } - } else if modifiers.shift() { - state.cursor.select_right(value) + + let mut editor = Editor::new(value, &mut state.cursor); + editor.backspace(); + + let message = (on_input)(editor.contents()); + shell.publish(message); + } + keyboard::KeyCode::Delete => { + if platform::is_jump_modifier_pressed(modifiers) + && state.cursor.selection(value).is_none() + { + if is_secure { + let cursor_pos = state.cursor.end(value); + state + .cursor + .select_range(cursor_pos, value.len()); } else { - state.cursor.move_right(value); + state.cursor.select_right_by_words(value); } } - keyboard::KeyCode::Home => { + + let mut editor = Editor::new(value, &mut state.cursor); + editor.delete(); + + let message = (on_input)(editor.contents()); + shell.publish(message); + } + keyboard::KeyCode::Left => { + if platform::is_jump_modifier_pressed(modifiers) + && !is_secure + { if modifiers.shift() { - state - .cursor - .select_range(state.cursor.start(value), 0); + state.cursor.select_left_by_words(value); } else { - state.cursor.move_to(0); + state.cursor.move_left_by_words(value); } + } else if modifiers.shift() { + state.cursor.select_left(value) + } else { + state.cursor.move_left(value); } - keyboard::KeyCode::End => { + } + keyboard::KeyCode::Right => { + if platform::is_jump_modifier_pressed(modifiers) + && !is_secure + { if modifiers.shift() { - state.cursor.select_range( - state.cursor.start(value), - value.len(), - ); + state.cursor.select_right_by_words(value); } else { - state.cursor.move_to(value.len()); + state.cursor.move_right_by_words(value); } + } else if modifiers.shift() { + state.cursor.select_right(value) + } else { + state.cursor.move_right(value); } - keyboard::KeyCode::C - if state.keyboard_modifiers.command() => + } + keyboard::KeyCode::Home => { + if modifiers.shift() { + state + .cursor + .select_range(state.cursor.start(value), 0); + } else { + state.cursor.move_to(0); + } + } + keyboard::KeyCode::End => { + if modifiers.shift() { + state.cursor.select_range( + state.cursor.start(value), + value.len(), + ); + } else { + state.cursor.move_to(value.len()); + } + } + keyboard::KeyCode::C + if state.keyboard_modifiers.command() => + { + if let Some((start, end)) = + state.cursor.selection(value) { - if let Some((start, end)) = - state.cursor.selection(value) - { - clipboard.write( - value.select(start, end).to_string(), - ); - } + clipboard + .write(value.select(start, end).to_string()); } - keyboard::KeyCode::X - if state.keyboard_modifiers.command() => + } + keyboard::KeyCode::X + if state.keyboard_modifiers.command() => + { + if let Some((start, end)) = + state.cursor.selection(value) { - if let Some((start, end)) = - state.cursor.selection(value) - { - clipboard.write( - value.select(start, end).to_string(), - ); - } + clipboard + .write(value.select(start, end).to_string()); + } + + let mut editor = Editor::new(value, &mut state.cursor); + editor.delete(); + + let message = (on_input)(editor.contents()); + shell.publish(message); + } + keyboard::KeyCode::V => { + if state.keyboard_modifiers.command() { + let content = match state.is_pasting.take() { + Some(content) => content, + None => { + let content: String = clipboard + .read() + .unwrap_or_default() + .chars() + .filter(|c| !c.is_control()) + .collect(); + + Value::new(&content) + } + }; let mut editor = Editor::new(value, &mut state.cursor); - editor.delete(); - let message = (on_change)(editor.contents()); - shell.publish(message); - } - keyboard::KeyCode::V => { - if state.keyboard_modifiers.command() { - let content = match state.is_pasting.take() { - Some(content) => content, - None => { - let content: String = clipboard - .read() - .unwrap_or_default() - .chars() - .filter(|c| !c.is_control()) - .collect(); - - Value::new(&content) - } - }; - - let mut editor = - Editor::new(value, &mut state.cursor); - - editor.paste(content.clone()); - - let message = if let Some(paste) = &on_paste { - (paste)(editor.contents()) - } else { - (on_change)(editor.contents()) - }; - shell.publish(message); - - state.is_pasting = Some(content); + editor.paste(content.clone()); + + let message = if let Some(paste) = &on_paste { + (paste)(editor.contents()) } else { - state.is_pasting = None; - } - } - keyboard::KeyCode::A - if state.keyboard_modifiers.command() => - { - state.cursor.select_all(value); - } - keyboard::KeyCode::Escape => { - state.is_focused = None; - state.is_dragging = false; - state.is_pasting = None; + (on_input)(editor.contents()) + }; + shell.publish(message); - state.keyboard_modifiers = - keyboard::Modifiers::default(); - } - keyboard::KeyCode::Tab - | keyboard::KeyCode::Up - | keyboard::KeyCode::Down => { - return event::Status::Ignored; + state.is_pasting = Some(content); + } else { + state.is_pasting = None; } - _ => {} } + keyboard::KeyCode::A + if state.keyboard_modifiers.command() => + { + state.cursor.select_all(value); + } + keyboard::KeyCode::Escape => { + state.is_focused = None; + state.is_dragging = false; + state.is_pasting = None; + + state.keyboard_modifiers = + keyboard::Modifiers::default(); + } + keyboard::KeyCode::Tab + | keyboard::KeyCode::Up + | keyboard::KeyCode::Down => { + return event::Status::Ignored; + } + _ => {} } return event::Status::Captured; @@ -917,10 +910,10 @@ pub fn draw( placeholder: &str, size: Option, font: &Renderer::Font, + is_disabled: bool, is_secure: bool, icon: Option<&Icon>, style: &::Style, - is_disabled: bool, ) where Renderer: text::Renderer, Renderer::Theme: StyleSheet, From 250ba3a7f1b41c7f7ca32b8db40a8c4069ebef77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 12 Apr 2023 04:19:54 +0200 Subject: [PATCH 52/85] Remove `text_input` example --- examples/text_input/Cargo.toml | 11 ---- examples/text_input/README.md | 15 ------ examples/text_input/src/main.rs | 94 --------------------------------- 3 files changed, 120 deletions(-) delete mode 100644 examples/text_input/Cargo.toml delete mode 100644 examples/text_input/README.md delete mode 100644 examples/text_input/src/main.rs diff --git a/examples/text_input/Cargo.toml b/examples/text_input/Cargo.toml deleted file mode 100644 index aead9896e4..0000000000 --- a/examples/text_input/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "text_input" -authors = ["Dan Mishin "] -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -iced = { path = "../..", features = ["tokio"] } -tokio = { version = "1.26.0", features = ["time"] } diff --git a/examples/text_input/README.md b/examples/text_input/README.md deleted file mode 100644 index 435989cce3..0000000000 --- a/examples/text_input/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# Text Input - -This example shows basic usage of text edit. -The button delays the change of the text field state to allow testing of the corner cases. - - - -You can run it with cargo run: -```bash -cargo run --package text_input -``` \ No newline at end of file diff --git a/examples/text_input/src/main.rs b/examples/text_input/src/main.rs deleted file mode 100644 index 418593f4fc..0000000000 --- a/examples/text_input/src/main.rs +++ /dev/null @@ -1,94 +0,0 @@ -use crate::Message::{StartTimer, TextEditModeChange}; -use iced::widget::{button, column, container, row, text, text_input}; -use iced::{ - executor, window, Application, Command, Element, Length, Renderer, - Settings, Theme, -}; -use tokio::time::{sleep, Duration}; - -fn main() -> iced::Result { - let settings = Settings { - window: window::Settings { - size: (700, 100), - ..window::Settings::default() - }, - ..Settings::default() - }; - - Example::run(settings) -} - -#[derive(Default)] -struct Example { - data: String, - text_edit_enabled: bool, -} - -#[derive(Debug, Clone)] -enum Message { - StartTimer, - TextEditModeChange, - TextInputChanged(String), -} - -impl Application for Example { - type Executor = executor::Default; - type Message = Message; - type Theme = Theme; - type Flags = (); - - fn new(_flags: Self::Flags) -> (Self, Command) { - (Self::default(), Command::none()) - } - - fn title(&self) -> String { - "TextInput example".into() - } - - fn update(&mut self, message: Self::Message) -> Command { - match message { - Message::TextEditModeChange => { - self.text_edit_enabled = !self.text_edit_enabled; - Command::none() - } - Message::TextInputChanged(updated_text) => { - self.data = updated_text; - Command::none() - } - StartTimer => { - let timer_f = async { - sleep(Duration::from_secs(3)).await; - }; - Command::perform(timer_f, |_| TextEditModeChange) - } - } - } - - fn view(&self) -> Element<'_, Self::Message, Renderer> { - let placeholder = if self.text_edit_enabled { - "Enabled TextEdit" - } else { - "Disabled TextEdit" - }; - - let mut txt_input = text_input(placeholder, &self.data); - - if self.text_edit_enabled { - txt_input = txt_input.on_input(Message::TextInputChanged); - } - - let btn = button("Enable/Disable").on_press(StartTimer); - let label = text( - "The mode will be changed after 3s when the button is pressed", - ); - - let content = row![txt_input, btn].spacing(10); - let content = column![content, label].spacing(10); - - container(content) - .width(Length::Shrink) - .height(Length::Shrink) - .padding(20) - .into() - } -} From 7e69cb4b18cde29eceb57e3712c0630d279e7984 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 12 Apr 2023 04:20:04 +0200 Subject: [PATCH 53/85] Unfocus `TextInput` if it becomes disabled --- native/src/widget/text_input.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 0cbc65db99..4c10f5bbb2 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -227,6 +227,18 @@ where tree::State::new(State::new()) } + fn diff(&self, tree: &mut Tree) { + let state = tree.state.downcast_mut::(); + + // Unfocus text input if it becomes disabled + if self.on_input.is_none() { + state.last_click = None; + state.is_focused = None; + state.is_pasting = None; + state.is_dragging = false; + } + } + fn width(&self) -> Length { self.width } From 70e4af46aaa9f0750bad2b8b6ed676dd841d6401 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 12 Apr 2023 04:21:58 +0200 Subject: [PATCH 54/85] Improve consistency of `on_input` documentation --- native/src/widget/text_input.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 4c10f5bbb2..63751b4d3b 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -113,8 +113,10 @@ where self } - /// Sets the callback which is called when the text gets changed - /// If not specified, the widget will be disabled + /// Sets the message that should be produced when some text is typed into + /// the [`TextInput`]. + /// + /// If this method is not called, the [`TextInput`] will be disabled. pub fn on_input(mut self, callback: F) -> Self where F: 'a + Fn(String) -> Message, From 6b8548869ea25ad7bb7a49fad77adfa3b43cc87d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 12 Apr 2023 04:25:02 +0200 Subject: [PATCH 55/85] Remove unnecessary `is_disabled` check in `text_input::draw` A disabled `TextInput` cannot be focused. --- native/src/widget/text_input.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 63751b4d3b..d704692d1d 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -998,7 +998,7 @@ pub fn draw( % 2 == 0; - let cursor = if is_cursor_visible && !is_disabled { + let cursor = if is_cursor_visible { Some(( renderer::Quad { bounds: Rectangle { From 1de794aabfaa651f021ff2e5831397d7ce9fc53c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 12 Apr 2023 04:34:37 +0200 Subject: [PATCH 56/85] Fine-tune built-in styling of disabled `TextInput` --- style/src/theme.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/style/src/theme.rs b/style/src/theme.rs index e13edb050a..6bd82a96ca 100644 --- a/style/src/theme.rs +++ b/style/src/theme.rs @@ -1102,21 +1102,19 @@ impl text_input::StyleSheet for Theme { let palette = self.extended_palette(); text_input::Appearance { - background: palette.background.base.color.into(), + background: palette.background.weak.color.into(), border_radius: 2.0, border_width: 1.0, - border_color: palette.background.weak.color, - icon_color: palette.background.weak.color, + border_color: palette.background.strong.color, + icon_color: palette.background.strong.color, } } fn disabled_color(&self, style: &Self::Style) -> Color { if let TextInput::Custom(custom) = style { - return custom.value_color(self); + return custom.disabled_color(self); } - let palette = self.extended_palette(); - - palette.secondary.strong.color + self.placeholder_color(style) } } From 7e7e66586d990788ffd77b17e98357e74252f497 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 12 Apr 2023 04:37:39 +0200 Subject: [PATCH 57/85] Show `NotAllowed` as mouse icon when hovering a disabled `TextInput` --- core/src/mouse/interaction.rs | 1 + native/src/widget/text_input.rs | 9 +++++++-- winit/src/conversion.rs | 1 + 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/core/src/mouse/interaction.rs b/core/src/mouse/interaction.rs index 57da93fe02..072033fdd4 100644 --- a/core/src/mouse/interaction.rs +++ b/core/src/mouse/interaction.rs @@ -12,4 +12,5 @@ pub enum Interaction { Grabbing, ResizingHorizontally, ResizingVertically, + NotAllowed, } diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index d704692d1d..8627aa98aa 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -340,7 +340,7 @@ where _viewport: &Rectangle, _renderer: &Renderer, ) -> mouse::Interaction { - mouse_interaction(layout, cursor_position) + mouse_interaction(layout, cursor_position, self.on_input.is_none()) } } @@ -1117,9 +1117,14 @@ pub fn draw( pub fn mouse_interaction( layout: Layout<'_>, cursor_position: Point, + is_disabled: bool, ) -> mouse::Interaction { if layout.bounds().contains(cursor_position) { - mouse::Interaction::Text + if is_disabled { + mouse::Interaction::NotAllowed + } else { + mouse::Interaction::Text + } } else { mouse::Interaction::default() } diff --git a/winit/src/conversion.rs b/winit/src/conversion.rs index 1b2ead36a4..d2dc9c06aa 100644 --- a/winit/src/conversion.rs +++ b/winit/src/conversion.rs @@ -236,6 +236,7 @@ pub fn mouse_interaction( winit::window::CursorIcon::EwResize } Interaction::ResizingVertically => winit::window::CursorIcon::NsResize, + Interaction::NotAllowed => winit::window::CursorIcon::NotAllowed, } } From de51bc3f41752634c0ccce8484d0a9bade62a45a Mon Sep 17 00:00:00 2001 From: Night_Hunter Date: Fri, 6 Jan 2023 14:55:09 +1300 Subject: [PATCH 58/85] Introduce left and right colors for slider rails --- native/src/widget/slider.rs | 92 ++++++++++++++++------------ native/src/widget/vertical_slider.rs | 87 +++++++++++++++----------- style/src/slider.rs | 17 ++++- style/src/theme.rs | 14 +++-- 4 files changed, 130 insertions(+), 80 deletions(-) diff --git a/native/src/widget/slider.rs b/native/src/widget/slider.rs index d3715b1c0d..b3f3306cd9 100644 --- a/native/src/widget/slider.rs +++ b/native/src/widget/slider.rs @@ -8,13 +8,15 @@ use crate::renderer; use crate::touch; use crate::widget::tree::{self, Tree}; use crate::{ - Background, Clipboard, Color, Element, Layout, Length, Pixels, Point, - Rectangle, Shell, Size, Widget, + Clipboard, Element, Layout, Length, Pixels, Point, Rectangle, Shell, Size, + Widget, }; use std::ops::RangeInclusive; -pub use iced_style::slider::{Appearance, Handle, HandleShape, StyleSheet}; +pub use iced_style::slider::{ + Appearance, Handle, HandleShape, Rail, StyleSheet, +}; /// An horizontal bar and a handle that selects a single value from a range of /// values. @@ -368,37 +370,7 @@ pub fn draw( style_sheet.active(style) }; - let rail_y = bounds.y + (bounds.height / 2.0).round(); - - renderer.fill_quad( - renderer::Quad { - bounds: Rectangle { - x: bounds.x, - y: rail_y - 1.0, - width: bounds.width, - height: 2.0, - }, - border_radius: 0.0.into(), - border_width: 0.0, - border_color: Color::TRANSPARENT, - }, - style.rail_colors.0, - ); - - renderer.fill_quad( - renderer::Quad { - bounds: Rectangle { - x: bounds.x, - y: rail_y + 1.0, - width: bounds.width, - height: 2.0, - }, - border_radius: 0.0.into(), - border_width: 0.0, - border_color: Color::TRANSPARENT, - }, - Background::Color(style.rail_colors.1), - ); + let value = value.into() as f32; let (handle_width, handle_height, handle_border_radius) = match style .handle @@ -411,25 +383,69 @@ pub fn draw( } => (f32::from(width), bounds.height, border_radius), }; - let value = value.into() as f32; let (range_start, range_end) = { let (start, end) = range.clone().into_inner(); (start.into() as f32, end.into() as f32) }; - let handle_offset = if range_start >= range_end { + let offset = if range_start >= range_end { 0.0 } else { (bounds.width - handle_width) * (value - range_start) / (range_end - range_start) }; + let line_y = bounds.y + bounds.height / 2.0 - style.rail.size / 2.0; + let line_offset = offset + handle_width / 2.0; + + renderer.fill_quad( + renderer::Quad { + bounds: Rectangle { + x: bounds.x, + y: line_y, + width: line_offset, + height: style.rail.size, + }, + border_radius: [ + style.rail.border_radius, + 0.0, + 0.0, + style.rail.border_radius, + ] + .into(), + border_width: style.rail.border_width, + border_color: style.rail.border_color, + }, + style.rail.colors.0, + ); + + renderer.fill_quad( + renderer::Quad { + bounds: Rectangle { + x: bounds.x + line_offset.round(), + y: line_y, + width: bounds.width - line_offset, + height: style.rail.size, + }, + border_radius: [ + 0.0, + style.rail.border_radius, + style.rail.border_radius, + 0.0, + ] + .into(), + border_width: style.rail.border_width, + border_color: style.rail.border_color, + }, + style.rail.colors.1, + ); + renderer.fill_quad( renderer::Quad { bounds: Rectangle { - x: bounds.x + handle_offset.round(), - y: rail_y - handle_height / 2.0, + x: bounds.x + offset.round(), + y: bounds.y + bounds.height / 2.0 - handle_height / 2.0, width: handle_width, height: handle_height, }, diff --git a/native/src/widget/vertical_slider.rs b/native/src/widget/vertical_slider.rs index f1687e38dd..3a8c30b6b6 100644 --- a/native/src/widget/vertical_slider.rs +++ b/native/src/widget/vertical_slider.rs @@ -8,8 +8,8 @@ pub use iced_style::slider::{Appearance, Handle, HandleShape, StyleSheet}; use crate::event::{self, Event}; use crate::widget::tree::{self, Tree}; use crate::{ - layout, mouse, renderer, touch, Background, Clipboard, Color, Element, - Layout, Length, Pixels, Point, Rectangle, Shell, Size, Widget, + layout, mouse, renderer, touch, Clipboard, Element, Layout, Length, Pixels, + Point, Rectangle, Shell, Size, Widget, }; /// An vertical bar and a handle that selects a single value from a range of @@ -363,38 +363,6 @@ pub fn draw( style_sheet.active(style) }; - let rail_x = bounds.x + (bounds.width / 2.0).round(); - - renderer.fill_quad( - renderer::Quad { - bounds: Rectangle { - x: rail_x - 1.0, - y: bounds.y, - width: 2.0, - height: bounds.height, - }, - border_radius: 0.0.into(), - border_width: 0.0, - border_color: Color::TRANSPARENT, - }, - style.rail_colors.0, - ); - - renderer.fill_quad( - renderer::Quad { - bounds: Rectangle { - x: rail_x + 1.0, - y: bounds.y, - width: 2.0, - height: bounds.height, - }, - border_radius: 0.0.into(), - border_width: 0.0, - border_color: Color::TRANSPARENT, - }, - Background::Color(style.rail_colors.1), - ); - let (handle_width, handle_height, handle_border_radius) = match style .handle .shape @@ -413,18 +381,63 @@ pub fn draw( (start.into() as f32, end.into() as f32) }; - let handle_offset = if range_start >= range_end { + let offset = if range_start >= range_end { 0.0 } else { (bounds.height - handle_width) * (value - range_end) / (range_start - range_end) }; + let line_x = bounds.x + bounds.width / 2.0 - style.rail.size / 2.0; + let line_offset = offset + handle_width / 2.0; + + renderer.fill_quad( + renderer::Quad { + bounds: Rectangle { + x: line_x, + y: bounds.y, + width: style.rail.size, + height: line_offset, + }, + border_radius: [ + style.rail.border_radius, + style.rail.border_radius, + 0.0, + 0.0, + ] + .into(), + border_width: style.rail.border_width, + border_color: style.rail.border_color, + }, + style.rail.colors.1, + ); + + renderer.fill_quad( + renderer::Quad { + bounds: Rectangle { + x: line_x, + y: bounds.y + line_offset.round(), + width: style.rail.size, + height: bounds.height - line_offset, + }, + border_radius: [ + 0.0, + 0.0, + style.rail.border_radius, + style.rail.border_radius, + ] + .into(), + border_width: style.rail.border_width, + border_color: style.rail.border_color, + }, + style.rail.colors.0, + ); + renderer.fill_quad( renderer::Quad { bounds: Rectangle { - x: rail_x - (handle_height / 2.0), - y: bounds.y + handle_offset.round(), + x: bounds.x + bounds.width / 2.0 - handle_height / 2.0, + y: bounds.y + offset.round(), width: handle_height, height: handle_width, }, diff --git a/style/src/slider.rs b/style/src/slider.rs index 4b52fad302..c143915ef5 100644 --- a/style/src/slider.rs +++ b/style/src/slider.rs @@ -5,11 +5,26 @@ use iced_core::Color; #[derive(Debug, Clone, Copy)] pub struct Appearance { /// The colors of the rail of the slider. - pub rail_colors: (Color, Color), + pub rail: Rail, /// The appearance of the [`Handle`] of the slider. pub handle: Handle, } +/// The appearance of a slider rail +#[derive(Debug, Clone, Copy)] +pub struct Rail { + /// The colors of the rail of the slider. + pub colors: (Color, Color), + /// The height of a slider rail and the width of a vertical slider. + pub size: f32, + /// The border radius of the slider. + pub border_radius: f32, + /// The border width of the slider. + pub border_width: f32, + /// The border [`Color`] of the slider. + pub border_color: Color, +} + /// The appearance of the handle of a slider. #[derive(Debug, Clone, Copy)] pub struct Handle { diff --git a/style/src/theme.rs b/style/src/theme.rs index 6bd82a96ca..417680a1b9 100644 --- a/style/src/theme.rs +++ b/style/src/theme.rs @@ -416,10 +416,16 @@ impl slider::StyleSheet for Theme { }; slider::Appearance { - rail_colors: ( - palette.primary.base.color, - Color::TRANSPARENT, - ), + rail: slider::Rail { + colors: ( + palette.primary.base.color, + palette.primary.base.color, + ), + size: 2.0, + border_radius: 0.0, + border_width: 0.0, + border_color: Color::TRANSPARENT, + }, handle: slider::Handle { color: palette.background.base.color, border_color: palette.primary.base.color, From 45cfce3f6dcf2773bc8ccc7e356906cb778f2f27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 12 Apr 2023 05:19:01 +0200 Subject: [PATCH 59/85] Simplify `draw` logic of sliders --- native/src/widget/slider.rs | 18 ++++++++---------- native/src/widget/vertical_slider.rs | 15 +++++++-------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/native/src/widget/slider.rs b/native/src/widget/slider.rs index b3f3306cd9..2f946a8a5e 100644 --- a/native/src/widget/slider.rs +++ b/native/src/widget/slider.rs @@ -370,8 +370,6 @@ pub fn draw( style_sheet.active(style) }; - let value = value.into() as f32; - let (handle_width, handle_height, handle_border_radius) = match style .handle .shape @@ -383,6 +381,7 @@ pub fn draw( } => (f32::from(width), bounds.height, border_radius), }; + let value = value.into() as f32; let (range_start, range_end) = { let (start, end) = range.clone().into_inner(); @@ -396,15 +395,14 @@ pub fn draw( / (range_end - range_start) }; - let line_y = bounds.y + bounds.height / 2.0 - style.rail.size / 2.0; - let line_offset = offset + handle_width / 2.0; + let rail_y = bounds.y + bounds.height / 2.0; renderer.fill_quad( renderer::Quad { bounds: Rectangle { x: bounds.x, - y: line_y, - width: line_offset, + y: rail_y - style.rail.size / 2.0, + width: offset, height: style.rail.size, }, border_radius: [ @@ -423,9 +421,9 @@ pub fn draw( renderer.fill_quad( renderer::Quad { bounds: Rectangle { - x: bounds.x + line_offset.round(), - y: line_y, - width: bounds.width - line_offset, + x: bounds.x + offset, + y: rail_y - style.rail.size / 2.0, + width: bounds.width - offset, height: style.rail.size, }, border_radius: [ @@ -445,7 +443,7 @@ pub fn draw( renderer::Quad { bounds: Rectangle { x: bounds.x + offset.round(), - y: bounds.y + bounds.height / 2.0 - handle_height / 2.0, + y: rail_y - handle_height / 2.0, width: handle_width, height: handle_height, }, diff --git a/native/src/widget/vertical_slider.rs b/native/src/widget/vertical_slider.rs index 3a8c30b6b6..8c6d1e5d6f 100644 --- a/native/src/widget/vertical_slider.rs +++ b/native/src/widget/vertical_slider.rs @@ -388,16 +388,15 @@ pub fn draw( / (range_start - range_end) }; - let line_x = bounds.x + bounds.width / 2.0 - style.rail.size / 2.0; - let line_offset = offset + handle_width / 2.0; + let rail_x = bounds.x + bounds.width / 2.0; renderer.fill_quad( renderer::Quad { bounds: Rectangle { - x: line_x, + x: rail_x - style.rail.size / 2.0, y: bounds.y, width: style.rail.size, - height: line_offset, + height: offset, }, border_radius: [ style.rail.border_radius, @@ -415,10 +414,10 @@ pub fn draw( renderer.fill_quad( renderer::Quad { bounds: Rectangle { - x: line_x, - y: bounds.y + line_offset.round(), + x: rail_x - style.rail.size / 2.0, + y: bounds.y + offset, width: style.rail.size, - height: bounds.height - line_offset, + height: bounds.height - offset, }, border_radius: [ 0.0, @@ -436,7 +435,7 @@ pub fn draw( renderer.fill_quad( renderer::Quad { bounds: Rectangle { - x: bounds.x + bounds.width / 2.0 - handle_height / 2.0, + x: rail_x - handle_height / 2.0, y: bounds.y + offset.round(), width: handle_height, height: handle_width, From c2cc9a835d8281c1d26c861ecac7991a924c3785 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 12 Apr 2023 05:21:46 +0200 Subject: [PATCH 60/85] Remove `border_width` support in `slider::Rail` --- native/src/widget/slider.rs | 12 ++++++------ native/src/widget/vertical_slider.rs | 12 ++++++------ style/src/slider.rs | 4 ---- style/src/theme.rs | 2 -- 4 files changed, 12 insertions(+), 18 deletions(-) diff --git a/native/src/widget/slider.rs b/native/src/widget/slider.rs index 2f946a8a5e..5b26ae0188 100644 --- a/native/src/widget/slider.rs +++ b/native/src/widget/slider.rs @@ -8,8 +8,8 @@ use crate::renderer; use crate::touch; use crate::widget::tree::{self, Tree}; use crate::{ - Clipboard, Element, Layout, Length, Pixels, Point, Rectangle, Shell, Size, - Widget, + Clipboard, Color, Element, Layout, Length, Pixels, Point, Rectangle, Shell, + Size, Widget, }; use std::ops::RangeInclusive; @@ -412,8 +412,8 @@ pub fn draw( style.rail.border_radius, ] .into(), - border_width: style.rail.border_width, - border_color: style.rail.border_color, + border_width: 0.0, + border_color: Color::TRANSPARENT, }, style.rail.colors.0, ); @@ -433,8 +433,8 @@ pub fn draw( 0.0, ] .into(), - border_width: style.rail.border_width, - border_color: style.rail.border_color, + border_width: 0.0, + border_color: Color::TRANSPARENT, }, style.rail.colors.1, ); diff --git a/native/src/widget/vertical_slider.rs b/native/src/widget/vertical_slider.rs index 8c6d1e5d6f..5fb6022a6f 100644 --- a/native/src/widget/vertical_slider.rs +++ b/native/src/widget/vertical_slider.rs @@ -8,8 +8,8 @@ pub use iced_style::slider::{Appearance, Handle, HandleShape, StyleSheet}; use crate::event::{self, Event}; use crate::widget::tree::{self, Tree}; use crate::{ - layout, mouse, renderer, touch, Clipboard, Element, Layout, Length, Pixels, - Point, Rectangle, Shell, Size, Widget, + layout, mouse, renderer, touch, Clipboard, Color, Element, Layout, Length, + Pixels, Point, Rectangle, Shell, Size, Widget, }; /// An vertical bar and a handle that selects a single value from a range of @@ -405,8 +405,8 @@ pub fn draw( 0.0, ] .into(), - border_width: style.rail.border_width, - border_color: style.rail.border_color, + border_width: 0.0, + border_color: Color::TRANSPARENT, }, style.rail.colors.1, ); @@ -426,8 +426,8 @@ pub fn draw( style.rail.border_radius, ] .into(), - border_width: style.rail.border_width, - border_color: style.rail.border_color, + border_width: 0.0, + border_color: Color::TRANSPARENT, }, style.rail.colors.0, ); diff --git a/style/src/slider.rs b/style/src/slider.rs index c143915ef5..bb36a176ed 100644 --- a/style/src/slider.rs +++ b/style/src/slider.rs @@ -19,10 +19,6 @@ pub struct Rail { pub size: f32, /// The border radius of the slider. pub border_radius: f32, - /// The border width of the slider. - pub border_width: f32, - /// The border [`Color`] of the slider. - pub border_color: Color, } /// The appearance of the handle of a slider. diff --git a/style/src/theme.rs b/style/src/theme.rs index 417680a1b9..669d915739 100644 --- a/style/src/theme.rs +++ b/style/src/theme.rs @@ -423,8 +423,6 @@ impl slider::StyleSheet for Theme { ), size: 2.0, border_radius: 0.0, - border_width: 0.0, - border_color: Color::TRANSPARENT, }, handle: slider::Handle { color: palette.background.base.color, From 6e6804c5c9d0dba921f2e25d2c888b136d22d35e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 12 Apr 2023 05:25:27 +0200 Subject: [PATCH 61/85] Use a `border_radius` of `2.0` for slider rails in built-in theme --- style/src/theme.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/style/src/theme.rs b/style/src/theme.rs index 669d915739..2eb825e663 100644 --- a/style/src/theme.rs +++ b/style/src/theme.rs @@ -422,7 +422,7 @@ impl slider::StyleSheet for Theme { palette.primary.base.color, ), size: 2.0, - border_radius: 0.0, + border_radius: 2.0, }, handle: slider::Handle { color: palette.background.base.color, From 9b39a17628e92b66ac3649e879478ed23635fa76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 12 Apr 2023 05:27:32 +0200 Subject: [PATCH 62/85] Rename `Rail::size` to `width` --- native/src/widget/slider.rs | 8 ++++---- native/src/widget/vertical_slider.rs | 8 ++++---- style/src/slider.rs | 4 ++-- style/src/theme.rs | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/native/src/widget/slider.rs b/native/src/widget/slider.rs index 5b26ae0188..69c0614076 100644 --- a/native/src/widget/slider.rs +++ b/native/src/widget/slider.rs @@ -401,9 +401,9 @@ pub fn draw( renderer::Quad { bounds: Rectangle { x: bounds.x, - y: rail_y - style.rail.size / 2.0, + y: rail_y - style.rail.width / 2.0, width: offset, - height: style.rail.size, + height: style.rail.width, }, border_radius: [ style.rail.border_radius, @@ -422,9 +422,9 @@ pub fn draw( renderer::Quad { bounds: Rectangle { x: bounds.x + offset, - y: rail_y - style.rail.size / 2.0, + y: rail_y - style.rail.width / 2.0, width: bounds.width - offset, - height: style.rail.size, + height: style.rail.width, }, border_radius: [ 0.0, diff --git a/native/src/widget/vertical_slider.rs b/native/src/widget/vertical_slider.rs index 5fb6022a6f..a06a200f60 100644 --- a/native/src/widget/vertical_slider.rs +++ b/native/src/widget/vertical_slider.rs @@ -393,9 +393,9 @@ pub fn draw( renderer.fill_quad( renderer::Quad { bounds: Rectangle { - x: rail_x - style.rail.size / 2.0, + x: rail_x - style.rail.width / 2.0, y: bounds.y, - width: style.rail.size, + width: style.rail.width, height: offset, }, border_radius: [ @@ -414,9 +414,9 @@ pub fn draw( renderer.fill_quad( renderer::Quad { bounds: Rectangle { - x: rail_x - style.rail.size / 2.0, + x: rail_x - style.rail.width / 2.0, y: bounds.y + offset, - width: style.rail.size, + width: style.rail.width, height: bounds.height - offset, }, border_radius: [ diff --git a/style/src/slider.rs b/style/src/slider.rs index bb36a176ed..d5db1853b6 100644 --- a/style/src/slider.rs +++ b/style/src/slider.rs @@ -15,8 +15,8 @@ pub struct Appearance { pub struct Rail { /// The colors of the rail of the slider. pub colors: (Color, Color), - /// The height of a slider rail and the width of a vertical slider. - pub size: f32, + /// The width of the stroke of a slider rail. + pub width: f32, /// The border radius of the slider. pub border_radius: f32, } diff --git a/style/src/theme.rs b/style/src/theme.rs index 2eb825e663..b195940e92 100644 --- a/style/src/theme.rs +++ b/style/src/theme.rs @@ -421,7 +421,7 @@ impl slider::StyleSheet for Theme { palette.primary.base.color, palette.primary.base.color, ), - size: 2.0, + width: 2.0, border_radius: 2.0, }, handle: slider::Handle { From b505b7203563b8a75a65451f47a3386c50864e6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 12 Apr 2023 06:43:58 +0200 Subject: [PATCH 63/85] Move `radio` example to `Radio` docs --- examples/radio/Cargo.toml | 10 ----- examples/radio/src/main.rs | 86 ------------------------------------ native/src/widget/helpers.rs | 2 +- native/src/widget/radio.rs | 37 +++++++++++++--- 4 files changed, 33 insertions(+), 102 deletions(-) delete mode 100644 examples/radio/Cargo.toml delete mode 100644 examples/radio/src/main.rs diff --git a/examples/radio/Cargo.toml b/examples/radio/Cargo.toml deleted file mode 100644 index a8c7f35184..0000000000 --- a/examples/radio/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "radio" -version = "0.1.0" -authors = ["Aaron Honeycutt "] -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -iced = { path = "../.." } diff --git a/examples/radio/src/main.rs b/examples/radio/src/main.rs deleted file mode 100644 index 3b19924e13..0000000000 --- a/examples/radio/src/main.rs +++ /dev/null @@ -1,86 +0,0 @@ -use iced::widget::{column, container, radio}; -use iced::{Element, Length, Sandbox, Settings}; - -pub fn main() -> iced::Result { - Example::run(Settings::default()) -} - -#[derive(Default)] -struct Example { - radio: Option, -} - -#[derive(Debug, Clone, Copy)] -enum Message { - RadioSelected(Choice), -} - -impl Sandbox for Example { - type Message = Message; - - fn new() -> Self { - Default::default() - } - - fn title(&self) -> String { - String::from("Radio - Iced") - } - - fn update(&mut self, message: Message) { - match message { - Message::RadioSelected(value) => { - self.radio = Some(value); - } - } - } - - fn view(&self) -> Element { - let a_checkbox = radio( - "A", - Choice::A, - self.radio, - Message::RadioSelected, - ); - - let b_checkbox = radio( - "B", - Choice::B, - self.radio, - Message::RadioSelected, - ); - - let c_checkbox = radio( - "C", - Choice::C, - self.radio, - Message::RadioSelected, - ); - - let all_checkbox = radio("All of the above", Choice::All, self.radio, Message::RadioSelected); - - let content = column![ - a_checkbox, - b_checkbox, - c_checkbox, - all_checkbox, - ] - .spacing(20) - .padding(20) - .max_width(600); - - container(content) - .width(Length::Fill) - .height(Length::Fill) - .center_x() - .center_y() - .into() - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum Choice { - A, - B, - C, - All, -} diff --git a/native/src/widget/helpers.rs b/native/src/widget/helpers.rs index d13eca7598..24853eaa58 100644 --- a/native/src/widget/helpers.rs +++ b/native/src/widget/helpers.rs @@ -147,7 +147,7 @@ where Renderer::Theme: widget::radio::StyleSheet, V: Copy + Eq, { - widget::Radio::new(value, label, selected, on_click) + widget::Radio::new(label, value, selected, on_click) } /// Creates a new [`Toggler`]. diff --git a/native/src/widget/radio.rs b/native/src/widget/radio.rs index 9daddfbc75..3ca041bf15 100644 --- a/native/src/widget/radio.rs +++ b/native/src/widget/radio.rs @@ -21,10 +21,13 @@ pub use iced_style::radio::{Appearance, StyleSheet}; /// # type Radio = /// # iced_native::widget::Radio; /// # +/// # use iced_native::column; /// #[derive(Debug, Clone, Copy, PartialEq, Eq)] /// pub enum Choice { /// A, /// B, +/// C, +/// All, /// } /// /// #[derive(Debug, Clone, Copy)] @@ -34,12 +37,36 @@ pub use iced_style::radio::{Appearance, StyleSheet}; /// /// let selected_choice = Some(Choice::A); /// -/// Radio::new(Choice::A, "This is A", selected_choice, Message::RadioSelected); +/// let a = Radio::new( +/// "A", +/// Choice::A, +/// selected_choice, +/// Message::RadioSelected, +/// ); /// -/// Radio::new(Choice::B, "This is B", selected_choice, Message::RadioSelected); -/// ``` +/// let b = Radio::new( +/// "B", +/// Choice::B, +/// selected_choice, +/// Message::RadioSelected, +/// ); +/// +/// let c = Radio::new( +/// "C", +/// Choice::C, +/// selected_choice, +/// Message::RadioSelected, +/// ); +/// +/// let all = Radio::new( +/// "All of the above", +/// Choice::All, +/// selected_choice, +/// Message::RadioSelected +/// ); /// -/// ![Radio buttons drawn by `iced_wgpu`](https://github.com/iced-rs/iced/blob/7760618fb112074bc40b148944521f312152012a/docs/images/radio.png?raw=true) +/// let content = column![a, b, c, all]; +/// ``` #[allow(missing_debug_implementations)] pub struct Radio where @@ -78,8 +105,8 @@ where /// * a function that will be called when the [`Radio`] is selected. It /// receives the value of the radio and must produce a `Message`. pub fn new( - value: V, label: impl Into, + value: V, selected: Option, f: F, ) -> Self From 5a056ce0510343621305474af74ade1db028c01a Mon Sep 17 00:00:00 2001 From: Night_Hunter Date: Wed, 12 Apr 2023 18:47:53 +1200 Subject: [PATCH 64/85] add action set icon while running (#1590) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * set windows icon live action * change get icon to insto raw * remove mobile docs * format * fix format * add file methods to Icon * Rename action to `ChangeIcon` and tidy up `Icon` modules * Fix documentation of `icon::Error` * Remove unnecessary `\` in `icon` documentation * Remove `etc.` from `Icon` documentation --------- Co-authored-by: Héctor Ramón Jiménez --- native/Cargo.toml | 1 + native/src/window.rs | 3 + native/src/window/action.rs | 21 +++- native/src/window/icon.rs | 80 ++++++++++++++ src/window/icon.rs | 209 +++++++++--------------------------- winit/src/application.rs | 3 + winit/src/conversion.rs | 9 ++ winit/src/settings.rs | 7 +- winit/src/window.rs | 9 +- 9 files changed, 178 insertions(+), 164 deletions(-) create mode 100644 native/src/window/icon.rs diff --git a/native/Cargo.toml b/native/Cargo.toml index 3f92783eb9..1eedf0dac5 100644 --- a/native/Cargo.toml +++ b/native/Cargo.toml @@ -14,6 +14,7 @@ debug = [] twox-hash = { version = "1.5", default-features = false } unicode-segmentation = "1.6" num-traits = "0.2" +thiserror = "1" [dependencies.iced_core] version = "0.8" diff --git a/native/src/window.rs b/native/src/window.rs index a5cdc8ceda..1ae89dba81 100644 --- a/native/src/window.rs +++ b/native/src/window.rs @@ -5,8 +5,11 @@ mod mode; mod redraw_request; mod user_attention; +pub mod icon; + pub use action::Action; pub use event::Event; +pub use icon::Icon; pub use mode::Mode; pub use redraw_request::RedrawRequest; pub use user_attention::UserAttention; diff --git a/native/src/window/action.rs b/native/src/window/action.rs index ce36d129d7..095a8eec2a 100644 --- a/native/src/window/action.rs +++ b/native/src/window/action.rs @@ -1,4 +1,4 @@ -use crate::window::{Mode, UserAttention}; +use crate::window::{Icon, Mode, UserAttention}; use iced_futures::MaybeSend; use std::fmt; @@ -78,6 +78,21 @@ pub enum Action { ChangeAlwaysOnTop(bool), /// Fetch an identifier unique to the window. FetchId(Box T + 'static>), + /// Changes the window [`Icon`]. + /// + /// On Windows and X11, this is typically the small icon in the top-left + /// corner of the titlebar. + /// + /// ## Platform-specific + /// + /// - **Web / Wayland / macOS:** Unsupported. + /// + /// - **Windows:** Sets `ICON_SMALL`. The base size for a window icon is 16x16, but it's + /// recommended to account for screen scaling and pick a multiple of that, i.e. 32x32. + /// + /// - **X11:** Has no universal guidelines for icon sizes, so you're at the whims of the WM. That + /// said, it's usually in the same ballpark as on Windows. + ChangeIcon(Icon), } impl Action { @@ -108,6 +123,7 @@ impl Action { Action::ChangeAlwaysOnTop(on_top) } Self::FetchId(o) => Action::FetchId(Box::new(move |s| f(o(s)))), + Self::ChangeIcon(icon) => Action::ChangeIcon(icon), } } } @@ -142,6 +158,9 @@ impl fmt::Debug for Action { write!(f, "Action::AlwaysOnTop({on_top})") } Self::FetchId(_) => write!(f, "Action::FetchId"), + Self::ChangeIcon(_icon) => { + write!(f, "Action::ChangeIcon(icon)") + } } } } diff --git a/native/src/window/icon.rs b/native/src/window/icon.rs new file mode 100644 index 0000000000..31868ecff6 --- /dev/null +++ b/native/src/window/icon.rs @@ -0,0 +1,80 @@ +//! Change the icon of a window. +use crate::Size; + +use std::mem; + +/// Builds an [`Icon`] from its RGBA pixels in the sRGB color space. +pub fn from_rgba( + rgba: Vec, + width: u32, + height: u32, +) -> Result { + const PIXEL_SIZE: usize = mem::size_of::() * 4; + + if rgba.len() % PIXEL_SIZE != 0 { + return Err(Error::ByteCountNotDivisibleBy4 { + byte_count: rgba.len(), + }); + } + + let pixel_count = rgba.len() / PIXEL_SIZE; + + if pixel_count != (width * height) as usize { + return Err(Error::DimensionsVsPixelCount { + width, + height, + width_x_height: (width * height) as usize, + pixel_count, + }); + } + + Ok(Icon { + rgba, + size: Size::new(width, height), + }) +} + +/// An window icon normally used for the titlebar or taskbar. +#[derive(Debug, Clone)] +pub struct Icon { + rgba: Vec, + size: Size, +} + +impl Icon { + /// Returns the raw data of the [`Icon`]. + pub fn into_raw(self) -> (Vec, Size) { + (self.rgba, self.size) + } +} + +#[derive(Debug, thiserror::Error)] +/// An error produced when using [`Icon::from_rgba`] with invalid arguments. +pub enum Error { + /// Produced when the length of the `rgba` argument isn't divisible by 4, thus `rgba` can't be + /// safely interpreted as 32bpp RGBA pixels. + #[error( + "The provided RGBA data (with length {byte_count}) isn't divisible \ + by 4. Therefore, it cannot be safely interpreted as 32bpp RGBA pixels" + )] + ByteCountNotDivisibleBy4 { + /// The length of the provided RGBA data. + byte_count: usize, + }, + /// Produced when the number of pixels (`rgba.len() / 4`) isn't equal to `width * height`. + /// At least one of your arguments is incorrect. + #[error( + "The number of RGBA pixels ({pixel_count}) does not match the \ + provided dimensions ({width}x{height})." + )] + DimensionsVsPixelCount { + /// The provided width. + width: u32, + /// The provided height. + height: u32, + /// The product of `width` and `height`. + width_x_height: usize, + /// The amount of pixels of the provided RGBA data. + pixel_count: usize, + }, +} diff --git a/src/window/icon.rs b/src/window/icon.rs index 659d2b64bb..e09dd04831 100644 --- a/src/window/icon.rs +++ b/src/window/icon.rs @@ -1,175 +1,66 @@ //! Attach an icon to the window of your application. -use std::fmt; +pub use crate::runtime::window::icon::*; + +use crate::runtime::window::icon; + use std::io; -#[cfg(feature = "image_rs")] +#[cfg(feature = "image")] use std::path::Path; -/// The icon of a window. -#[derive(Clone)] -pub struct Icon(iced_winit::winit::window::Icon); +/// Creates an icon from an image file. +/// +/// This will return an error in case the file is missing at run-time. You may prefer [`Self::from_file_data`] instead. +#[cfg(feature = "image")] +#[cfg_attr(docsrs, doc(cfg(feature = "image")))] +pub fn from_file>(icon_path: P) -> Result { + let icon = image_rs::io::Reader::open(icon_path)?.decode()?.to_rgba8(); -impl fmt::Debug for Icon { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("Icon").field(&format_args!("_")).finish() - } + Ok(icon::from_rgba(icon.to_vec(), icon.width(), icon.height())?) } -impl Icon { - /// Creates an icon from 32bpp RGBA data. - pub fn from_rgba( - rgba: Vec, - width: u32, - height: u32, - ) -> Result { - let raw = - iced_winit::winit::window::Icon::from_rgba(rgba, width, height)?; - - Ok(Icon(raw)) - } - - /// Creates an icon from an image file. - /// - /// This will return an error in case the file is missing at run-time. You may prefer [`Self::from_file_data`] instead. - #[cfg(feature = "image_rs")] - pub fn from_file>(icon_path: P) -> Result { - let icon = image_rs::io::Reader::open(icon_path)?.decode()?.to_rgba8(); - - Self::from_rgba(icon.to_vec(), icon.width(), icon.height()) - } - - /// Creates an icon from the content of an image file. - /// - /// This content can be included in your application at compile-time, e.g. using the `include_bytes!` macro. \ - /// You can pass an explicit file format. Otherwise, the file format will be guessed at runtime. - #[cfg(feature = "image_rs")] - pub fn from_file_data( - data: &[u8], - explicit_format: Option, - ) -> Result { - let mut icon = image_rs::io::Reader::new(std::io::Cursor::new(data)); - let icon_with_format = match explicit_format { - Some(format) => { - icon.set_format(format); - icon - } - None => icon.with_guessed_format()?, - }; +/// Creates an icon from the content of an image file. +/// +/// This content can be included in your application at compile-time, e.g. using the `include_bytes!` macro. +/// You can pass an explicit file format. Otherwise, the file format will be guessed at runtime. +#[cfg(feature = "image")] +#[cfg_attr(docsrs, doc(cfg(feature = "image")))] +pub fn from_file_data( + data: &[u8], + explicit_format: Option, +) -> Result { + let mut icon = image_rs::io::Reader::new(std::io::Cursor::new(data)); + let icon_with_format = match explicit_format { + Some(format) => { + icon.set_format(format); + icon + } + None => icon.with_guessed_format()?, + }; - let pixels = icon_with_format.decode()?.to_rgba8(); + let pixels = icon_with_format.decode()?.to_rgba8(); - Self::from_rgba(pixels.to_vec(), pixels.width(), pixels.height()) - } + Ok(icon::from_rgba( + pixels.to_vec(), + pixels.width(), + pixels.height(), + )?) } -/// An error produced when using `Icon::from_rgba` with invalid arguments. -#[derive(Debug)] +/// An error produced when creating an [`Icon`]. +#[derive(Debug, thiserror::Error)] pub enum Error { - /// The provided RGBA data isn't divisble by 4. - /// - /// Therefore, it cannot be safely interpreted as 32bpp RGBA pixels. - InvalidData { - /// The length of the provided RGBA data. - byte_count: usize, - }, - - /// The number of RGBA pixels does not match the provided dimensions. - DimensionsMismatch { - /// The provided width. - width: u32, - /// The provided height. - height: u32, - /// The amount of pixels of the provided RGBA data. - pixel_count: usize, - }, + /// The [`Icon`] is not valid. + #[error("The icon is invalid: {0}")] + InvalidError(#[from] icon::Error), /// The underlying OS failed to create the icon. - OsError(io::Error), - - /// The `image` crate reported an error - #[cfg(feature = "image_rs")] - ImageError(image_rs::error::ImageError), -} - -impl From for Error { - fn from(os_error: std::io::Error) -> Self { - Error::OsError(os_error) - } -} - -impl From for Error { - fn from(error: iced_winit::winit::window::BadIcon) -> Self { - use iced_winit::winit::window::BadIcon; - - match error { - BadIcon::ByteCountNotDivisibleBy4 { byte_count } => { - Error::InvalidData { byte_count } - } - BadIcon::DimensionsVsPixelCount { - width, - height, - pixel_count, - .. - } => Error::DimensionsMismatch { - width, - height, - pixel_count, - }, - BadIcon::OsError(os_error) => Error::OsError(os_error), - } - } -} - -impl From for iced_winit::winit::window::Icon { - fn from(icon: Icon) -> Self { - icon.0 - } -} - -#[cfg(feature = "image_rs")] -impl From for Error { - fn from(image_error: image_rs::error::ImageError) -> Self { - Self::ImageError(image_error) - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Error::InvalidData { byte_count } => { - write!( - f, - "The provided RGBA data (with length {byte_count:?}) isn't divisble by \ - 4. Therefore, it cannot be safely interpreted as 32bpp RGBA \ - pixels." - ) - } - Error::DimensionsMismatch { - width, - height, - pixel_count, - } => { - write!( - f, - "The number of RGBA pixels ({pixel_count:?}) does not match the provided \ - dimensions ({width:?}x{height:?})." - ) - } - Error::OsError(e) => write!( - f, - "The underlying OS failed to create the window \ - icon: {e:?}" - ), - #[cfg(feature = "image_rs")] - Error::ImageError(e) => { - write!(f, "Unable to create icon from a file: {e:?}") - } - } - } -} - -impl std::error::Error for Error { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - Some(self) - } + #[error("The underlying OS failted to create the window icon: {0}")] + OsError(#[from] io::Error), + + /// The `image` crate reported an error. + #[cfg(feature = "image")] + #[cfg_attr(docsrs, doc(cfg(feature = "image")))] + #[error("Unable to create icon from a file: {0}")] + ImageError(#[from] image_rs::error::ImageError), } diff --git a/winit/src/application.rs b/winit/src/application.rs index 31654f26ee..dd3457853b 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -770,6 +770,9 @@ pub fn run_command( mode, )); } + window::Action::ChangeIcon(icon) => { + window.set_window_icon(conversion::icon(icon)) + } window::Action::FetchMode(tag) => { let mode = if window.is_visible().unwrap_or(true) { conversion::mode(window.fullscreen()) diff --git a/winit/src/conversion.rs b/winit/src/conversion.rs index d2dc9c06aa..cf066ef64a 100644 --- a/winit/src/conversion.rs +++ b/winit/src/conversion.rs @@ -510,6 +510,15 @@ pub fn user_attention( } } +/// Converts some [`Icon`] into it's `winit` counterpart. +/// +/// Returns `None` if there is an error during the conversion. +pub fn icon(icon: window::Icon) -> Option { + let (pixels, size) = icon.into_raw(); + + winit::window::Icon::from_rgba(pixels, size.width, size.height).ok() +} + // As defined in: http://www.unicode.org/faq/private_use.html pub(crate) fn is_private_use_character(c: char) -> bool { matches!( diff --git a/winit/src/settings.rs b/winit/src/settings.rs index 78d580001a..6658773d52 100644 --- a/winit/src/settings.rs +++ b/winit/src/settings.rs @@ -92,7 +92,7 @@ pub struct Window { pub always_on_top: bool, /// The window icon, which is also usually used in the taskbar - pub icon: Option, + pub icon: Option, /// Platform specific settings. pub platform_specific: platform::PlatformSpecific, @@ -134,8 +134,9 @@ impl Window { .with_resizable(self.resizable) .with_decorations(self.decorations) .with_transparent(self.transparent) - .with_window_icon(self.icon) - .with_always_on_top(self.always_on_top); + .with_window_icon(self.icon.and_then(conversion::icon)) + .with_always_on_top(self.always_on_top) + .with_visible(self.visible); if let Some(position) = conversion::position( primary_monitor.as_ref(), diff --git a/winit/src/window.rs b/winit/src/window.rs index 961562bd7b..ba0180c8a7 100644 --- a/winit/src/window.rs +++ b/winit/src/window.rs @@ -2,7 +2,9 @@ use crate::command::{self, Command}; use iced_native::window; -pub use window::{frames, Event, Mode, RedrawRequest, UserAttention}; +pub use window::{ + frames, icon, Event, Icon, Mode, RedrawRequest, UserAttention, +}; /// Closes the current window and exits the application. pub fn close() -> Command { @@ -104,3 +106,8 @@ pub fn fetch_id( f, )))) } + +/// Changes the [`Icon`] of the window. +pub fn change_icon(icon: Icon) -> Command { + Command::single(command::Action::Window(window::Action::ChangeIcon(icon))) +} From ee32af2606e7c24cbe9e1c221af0ed1a2c44be50 Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Wed, 12 Apr 2023 10:32:35 -0700 Subject: [PATCH 65/85] Don't publish redundant on_scroll offsets --- native/src/widget/scrollable.rs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index d9cdf29618..23fa20f4aa 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -895,7 +895,7 @@ pub fn draw( } fn notify_on_scroll( - state: &State, + state: &mut State, on_scroll: &Option Message + '_>>, bounds: Rectangle, content_bounds: Rectangle, @@ -916,7 +916,21 @@ fn notify_on_scroll( .absolute(bounds.height, content_bounds.height) / (content_bounds.height - bounds.height); - shell.publish(on_scroll(RelativeOffset { x, y })) + let new_offset = RelativeOffset { x, y }; + + // Don't publish redundant offsets to shell + if let Some(prev_offset) = state.last_notified { + let unchanged = |a: f32, b: f32| (a - b).abs() <= f32::EPSILON; + + if unchanged(prev_offset.x, new_offset.x) + && unchanged(prev_offset.y, new_offset.y) + { + return; + } + } + + shell.publish(on_scroll(new_offset)); + state.last_notified = Some(new_offset); } } @@ -929,6 +943,7 @@ pub struct State { offset_x: Offset, x_scroller_grabbed_at: Option, keyboard_modifiers: keyboard::Modifiers, + last_notified: Option, } impl Default for State { @@ -940,6 +955,7 @@ impl Default for State { offset_x: Offset::Absolute(0.0), x_scroller_grabbed_at: None, keyboard_modifiers: keyboard::Modifiers::default(), + last_notified: None, } } } From 4125c034f5c512365c6ef73678dc0801db79b249 Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Wed, 12 Apr 2023 12:47:24 -0700 Subject: [PATCH 66/85] Include NaN in unchaged logic --- native/src/widget/scrollable.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index 23fa20f4aa..c0590b1eb5 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -920,7 +920,9 @@ fn notify_on_scroll( // Don't publish redundant offsets to shell if let Some(prev_offset) = state.last_notified { - let unchanged = |a: f32, b: f32| (a - b).abs() <= f32::EPSILON; + let unchanged = |a: f32, b: f32| { + (a - b).abs() <= f32::EPSILON || (a.is_nan() && b.is_nan()) + }; if unchanged(prev_offset.x, new_offset.x) && unchanged(prev_offset.y, new_offset.y) From 66939b22b0ee7e1dc0e5ef31594fae5810ffa7b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Wed, 12 Apr 2023 23:40:36 +0200 Subject: [PATCH 67/85] Remove `max_width` and `max_height` calls in `scrollable::layout` --- native/src/widget/scrollable.rs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index 45cd2a0e5f..8e8138cd9c 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -405,15 +405,7 @@ pub fn layout( horizontal_enabled: bool, layout_content: impl FnOnce(&Renderer, &layout::Limits) -> layout::Node, ) -> layout::Node { - let limits = limits - .max_height(f32::INFINITY) - .max_width(if horizontal_enabled { - f32::INFINITY - } else { - limits.max().width - }) - .width(width) - .height(height); + let limits = limits.width(width).height(height); let child_limits = layout::Limits::new( Size::new(limits.min().width, 0.0), From d5453c62e9bdbf0cea030b009c41b892b700496d Mon Sep 17 00:00:00 2001 From: Elham Aryanpur Date: Wed, 12 Apr 2023 23:38:21 +0300 Subject: [PATCH 68/85] Update `wgpu` to `0.15` --- examples/integration_wgpu/src/main.rs | 37 +++++++++++++++++---------- wgpu/Cargo.toml | 4 +-- wgpu/src/image/atlas.rs | 2 ++ wgpu/src/triangle/msaa.rs | 2 ++ wgpu/src/window/compositor.rs | 25 +++++++++++++++--- 5 files changed, 50 insertions(+), 20 deletions(-) diff --git a/examples/integration_wgpu/src/main.rs b/examples/integration_wgpu/src/main.rs index 2a56b6fa4f..6e86833227 100644 --- a/examples/integration_wgpu/src/main.rs +++ b/examples/integration_wgpu/src/main.rs @@ -23,18 +23,17 @@ use web_sys::HtmlCanvasElement; #[cfg(target_arch = "wasm32")] use winit::platform::web::WindowBuilderExtWebSys; -pub fn main() { +pub fn main() -> Result<(), Box> { #[cfg(target_arch = "wasm32")] let canvas_element = { - console_log::init_with_level(log::Level::Debug) - .expect("could not initialize logger"); + console_log::init_with_level(log::Level::Debug)?; + std::panic::set_hook(Box::new(console_error_panic_hook::hook)); web_sys::window() .and_then(|win| win.document()) .and_then(|doc| doc.get_element_by_id("iced_canvas")) - .and_then(|element| element.dyn_into::().ok()) - .expect("Canvas with id `iced_canvas` is missing") + .and_then(|element| element.dyn_into::().ok())? }; #[cfg(not(target_arch = "wasm32"))] env_logger::init(); @@ -45,11 +44,10 @@ pub fn main() { #[cfg(target_arch = "wasm32")] let window = winit::window::WindowBuilder::new() .with_canvas(Some(canvas_element)) - .build(&event_loop) - .expect("Failed to build winit window"); + .build(&event_loop)?; #[cfg(not(target_arch = "wasm32"))] - let window = winit::window::Window::new(&event_loop).unwrap(); + let window = winit::window::Window::new(&event_loop)?; let physical_size = window.inner_size(); let mut viewport = Viewport::with_physical_size( @@ -61,7 +59,6 @@ pub fn main() { let mut clipboard = Clipboard::connect(&window); // Initialize wgpu - #[cfg(target_arch = "wasm32")] let default_backend = wgpu::Backends::GL; #[cfg(not(target_arch = "wasm32"))] @@ -70,8 +67,12 @@ pub fn main() { let backend = wgpu::util::backend_bits_from_env().unwrap_or(default_backend); - let instance = wgpu::Instance::new(backend); - let surface = unsafe { instance.create_surface(&window) }; + let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { + backends: backend, + ..Default::default() + }); + + let surface = unsafe { instance.create_surface(&window) }?; let (format, (device, queue)) = futures::executor::block_on(async { let adapter = wgpu::util::initialize_adapter_from_env_or_default( @@ -93,9 +94,15 @@ pub fn main() { ( surface - .get_supported_formats(&adapter) - .first() + .get_capabilities(&adapter) + .formats + .iter() + .filter(|format| format.describe().srgb) .copied() + .next() + .or_else(|| { + surface.get_capabilities(&adapter).formats.first().copied() + }) .expect("Get preferred format"), adapter .request_device( @@ -120,6 +127,7 @@ pub fn main() { height: physical_size.height, present_mode: wgpu::PresentMode::AutoVsync, alpha_mode: wgpu::CompositeAlphaMode::Auto, + view_formats: vec![], }, ); @@ -214,7 +222,8 @@ pub fn main() { width: size.width, height: size.height, present_mode: wgpu::PresentMode::AutoVsync, - alpha_mode: wgpu::CompositeAlphaMode::Auto + alpha_mode: wgpu::CompositeAlphaMode::Auto, + view_formats: vec![] }, ); diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index f1e22cf682..4dcd07f771 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -28,8 +28,8 @@ spirv = ["wgpu/spirv"] webgl = ["wgpu/webgl"] [dependencies] -wgpu = "0.14" -wgpu_glyph = "0.18" +wgpu = "0.15" +wgpu_glyph = "0.19" glyph_brush = "0.7" raw-window-handle = "0.5" log = "0.4" diff --git a/wgpu/src/image/atlas.rs b/wgpu/src/image/atlas.rs index eafe2f9656..82504147e5 100644 --- a/wgpu/src/image/atlas.rs +++ b/wgpu/src/image/atlas.rs @@ -39,6 +39,7 @@ impl Atlas { sample_count: 1, dimension: wgpu::TextureDimension::D2, format: wgpu::TextureFormat::Rgba8UnormSrgb, + view_formats: &[], usage: wgpu::TextureUsages::COPY_DST | wgpu::TextureUsages::COPY_SRC | wgpu::TextureUsages::TEXTURE_BINDING, @@ -247,6 +248,7 @@ impl Atlas { sample_count: 1, dimension: wgpu::TextureDimension::D2, format: wgpu::TextureFormat::Rgba8UnormSrgb, + view_formats: &[], usage: wgpu::TextureUsages::COPY_DST | wgpu::TextureUsages::COPY_SRC | wgpu::TextureUsages::TEXTURE_BINDING, diff --git a/wgpu/src/triangle/msaa.rs b/wgpu/src/triangle/msaa.rs index e76f7d5eaa..d24f8e1abb 100644 --- a/wgpu/src/triangle/msaa.rs +++ b/wgpu/src/triangle/msaa.rs @@ -222,6 +222,7 @@ impl Targets { sample_count, dimension: wgpu::TextureDimension::D2, format, + view_formats: &[], usage: wgpu::TextureUsages::RENDER_ATTACHMENT, }); @@ -232,6 +233,7 @@ impl Targets { sample_count: 1, dimension: wgpu::TextureDimension::D2, format, + view_formats: &[], usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING, }); diff --git a/wgpu/src/window/compositor.rs b/wgpu/src/window/compositor.rs index 6d0c36f6d3..d66aca71f5 100644 --- a/wgpu/src/window/compositor.rs +++ b/wgpu/src/window/compositor.rs @@ -31,7 +31,10 @@ impl Compositor { settings: Settings, compatible_window: Option<&W>, ) -> Option { - let instance = wgpu::Instance::new(settings.internal_backend); + let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { + backends: settings.internal_backend, + ..Default::default() + }); log::info!("{:#?}", settings); @@ -46,7 +49,7 @@ impl Compositor { #[allow(unsafe_code)] let compatible_surface = compatible_window - .map(|window| unsafe { instance.create_surface(window) }); + .and_then(|window| unsafe { instance.create_surface(window).ok() }); let adapter = instance .request_adapter(&wgpu::RequestAdapterOptions { @@ -63,7 +66,18 @@ impl Compositor { log::info!("Selected: {:#?}", adapter.get_info()); let format = compatible_surface.as_ref().and_then(|surface| { - surface.get_supported_formats(&adapter).first().copied() + surface + .get_capabilities(&adapter) + .formats + .iter() + .filter(|format| format.describe().srgb) + .copied() + .next() + .or_else(|| { + log::warn!("No sRGB format found!"); + + surface.get_capabilities(&adapter).formats.first().copied() + }) })?; log::info!("Selected format: {:?}", format); @@ -144,7 +158,9 @@ impl iced_graphics::window::Compositor for Compositor { ) -> wgpu::Surface { #[allow(unsafe_code)] unsafe { - self.instance.create_surface(window) + self.instance + .create_surface(window) + .expect("Create surface") } } @@ -163,6 +179,7 @@ impl iced_graphics::window::Compositor for Compositor { width, height, alpha_mode: wgpu::CompositeAlphaMode::Auto, + view_formats: vec![], }, ); } From b677345ac1b1d087bc7f331c9c8c5be06933ba6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 13 Apr 2023 05:42:56 +0200 Subject: [PATCH 69/85] Get surface capabilities only once in `iced_wgpu` --- examples/integration_wgpu/src/main.rs | 9 ++++----- wgpu/src/window/compositor.rs | 7 ++++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/integration_wgpu/src/main.rs b/examples/integration_wgpu/src/main.rs index 6e86833227..8e0056f3b3 100644 --- a/examples/integration_wgpu/src/main.rs +++ b/examples/integration_wgpu/src/main.rs @@ -92,17 +92,16 @@ pub fn main() -> Result<(), Box> { #[cfg(not(target_arch = "wasm32"))] let needed_limits = wgpu::Limits::default(); + let capabilities = surface.get_capabilities(&adapter); + ( - surface - .get_capabilities(&adapter) + capabilities .formats .iter() .filter(|format| format.describe().srgb) .copied() .next() - .or_else(|| { - surface.get_capabilities(&adapter).formats.first().copied() - }) + .or_else(|| capabilities.formats.first().copied()) .expect("Get preferred format"), adapter .request_device( diff --git a/wgpu/src/window/compositor.rs b/wgpu/src/window/compositor.rs index d66aca71f5..d4a5947187 100644 --- a/wgpu/src/window/compositor.rs +++ b/wgpu/src/window/compositor.rs @@ -66,8 +66,9 @@ impl Compositor { log::info!("Selected: {:#?}", adapter.get_info()); let format = compatible_surface.as_ref().and_then(|surface| { - surface - .get_capabilities(&adapter) + let capabilities = surface.get_capabilities(&adapter); + + capabilities .formats .iter() .filter(|format| format.describe().srgb) @@ -76,7 +77,7 @@ impl Compositor { .or_else(|| { log::warn!("No sRGB format found!"); - surface.get_capabilities(&adapter).formats.first().copied() + capabilities.formats.first().copied() }) })?; From 9410fb98275fb51f67c55b676d8b37a5197222b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 13 Apr 2023 05:46:18 +0200 Subject: [PATCH 70/85] Update `env_logger` in `integration_wgpu` example --- examples/integration_wgpu/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/integration_wgpu/Cargo.toml b/examples/integration_wgpu/Cargo.toml index eaa1df7e17..c0e4fd810c 100644 --- a/examples/integration_wgpu/Cargo.toml +++ b/examples/integration_wgpu/Cargo.toml @@ -8,7 +8,7 @@ publish = false [dependencies] iced_winit = { path = "../../winit" } iced_wgpu = { path = "../../wgpu", features = ["webgl"] } -env_logger = "0.8" +env_logger = "0.10" [target.'cfg(target_arch = "wasm32")'.dependencies] console_error_panic_hook = "0.1.7" From 52c84c4975d75baf313243d422d96581daca19bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 13 Apr 2023 05:53:40 +0200 Subject: [PATCH 71/85] Remove `border_radius` support for `slider::Rail` Our current quad shader may produce weird-looking results with non-integral scaling factors. --- native/src/widget/slider.rs | 16 ++-------------- native/src/widget/vertical_slider.rs | 16 ++-------------- style/src/slider.rs | 2 -- style/src/theme.rs | 1 - 4 files changed, 4 insertions(+), 31 deletions(-) diff --git a/native/src/widget/slider.rs b/native/src/widget/slider.rs index 69c0614076..199dc9b300 100644 --- a/native/src/widget/slider.rs +++ b/native/src/widget/slider.rs @@ -405,13 +405,7 @@ pub fn draw( width: offset, height: style.rail.width, }, - border_radius: [ - style.rail.border_radius, - 0.0, - 0.0, - style.rail.border_radius, - ] - .into(), + border_radius: Default::default(), border_width: 0.0, border_color: Color::TRANSPARENT, }, @@ -426,13 +420,7 @@ pub fn draw( width: bounds.width - offset, height: style.rail.width, }, - border_radius: [ - 0.0, - style.rail.border_radius, - style.rail.border_radius, - 0.0, - ] - .into(), + border_radius: Default::default(), border_width: 0.0, border_color: Color::TRANSPARENT, }, diff --git a/native/src/widget/vertical_slider.rs b/native/src/widget/vertical_slider.rs index a06a200f60..b445b9ec17 100644 --- a/native/src/widget/vertical_slider.rs +++ b/native/src/widget/vertical_slider.rs @@ -398,13 +398,7 @@ pub fn draw( width: style.rail.width, height: offset, }, - border_radius: [ - style.rail.border_radius, - style.rail.border_radius, - 0.0, - 0.0, - ] - .into(), + border_radius: Default::default(), border_width: 0.0, border_color: Color::TRANSPARENT, }, @@ -419,13 +413,7 @@ pub fn draw( width: style.rail.width, height: bounds.height - offset, }, - border_radius: [ - 0.0, - 0.0, - style.rail.border_radius, - style.rail.border_radius, - ] - .into(), + border_radius: Default::default(), border_width: 0.0, border_color: Color::TRANSPARENT, }, diff --git a/style/src/slider.rs b/style/src/slider.rs index d5db1853b6..884d3871ef 100644 --- a/style/src/slider.rs +++ b/style/src/slider.rs @@ -17,8 +17,6 @@ pub struct Rail { pub colors: (Color, Color), /// The width of the stroke of a slider rail. pub width: f32, - /// The border radius of the slider. - pub border_radius: f32, } /// The appearance of the handle of a slider. diff --git a/style/src/theme.rs b/style/src/theme.rs index b195940e92..6711fd7201 100644 --- a/style/src/theme.rs +++ b/style/src/theme.rs @@ -422,7 +422,6 @@ impl slider::StyleSheet for Theme { palette.primary.base.color, ), width: 2.0, - border_radius: 2.0, }, handle: slider::Handle { color: palette.background.base.color, From 2be79d7b6be377c26f2d328058177179377fba46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 13 Apr 2023 06:02:30 +0200 Subject: [PATCH 72/85] Fix handle position of sliders --- native/src/widget/slider.rs | 8 ++++---- native/src/widget/vertical_slider.rs | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/native/src/widget/slider.rs b/native/src/widget/slider.rs index 199dc9b300..ba46783467 100644 --- a/native/src/widget/slider.rs +++ b/native/src/widget/slider.rs @@ -391,7 +391,7 @@ pub fn draw( let offset = if range_start >= range_end { 0.0 } else { - (bounds.width - handle_width) * (value - range_start) + (bounds.width - handle_width / 2.0) * (value - range_start) / (range_end - range_start) }; @@ -402,7 +402,7 @@ pub fn draw( bounds: Rectangle { x: bounds.x, y: rail_y - style.rail.width / 2.0, - width: offset, + width: offset + handle_width / 2.0, height: style.rail.width, }, border_radius: Default::default(), @@ -415,7 +415,7 @@ pub fn draw( renderer.fill_quad( renderer::Quad { bounds: Rectangle { - x: bounds.x + offset, + x: bounds.x + offset + handle_width / 2.0, y: rail_y - style.rail.width / 2.0, width: bounds.width - offset, height: style.rail.width, @@ -430,7 +430,7 @@ pub fn draw( renderer.fill_quad( renderer::Quad { bounds: Rectangle { - x: bounds.x + offset.round(), + x: bounds.x + offset, y: rail_y - handle_height / 2.0, width: handle_width, height: handle_height, diff --git a/native/src/widget/vertical_slider.rs b/native/src/widget/vertical_slider.rs index b445b9ec17..5af696763c 100644 --- a/native/src/widget/vertical_slider.rs +++ b/native/src/widget/vertical_slider.rs @@ -384,7 +384,7 @@ pub fn draw( let offset = if range_start >= range_end { 0.0 } else { - (bounds.height - handle_width) * (value - range_end) + (bounds.height - handle_width / 2.0) * (value - range_end) / (range_start - range_end) }; @@ -396,7 +396,7 @@ pub fn draw( x: rail_x - style.rail.width / 2.0, y: bounds.y, width: style.rail.width, - height: offset, + height: offset + handle_width / 2.0, }, border_radius: Default::default(), border_width: 0.0, @@ -409,7 +409,7 @@ pub fn draw( renderer::Quad { bounds: Rectangle { x: rail_x - style.rail.width / 2.0, - y: bounds.y + offset, + y: bounds.y + offset + handle_width / 2.0, width: style.rail.width, height: bounds.height - offset, }, @@ -424,7 +424,7 @@ pub fn draw( renderer::Quad { bounds: Rectangle { x: rail_x - handle_height / 2.0, - y: bounds.y + offset.round(), + y: bounds.y + offset, width: handle_height, height: handle_width, }, From db4b899fd215f0607474006a9aef0361e74d481f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 13 Apr 2023 06:03:44 +0200 Subject: [PATCH 73/85] Fix Wasm target for `integration_wgpu` --- examples/integration_wgpu/src/main.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/integration_wgpu/src/main.rs b/examples/integration_wgpu/src/main.rs index 8e0056f3b3..3ac458b3d2 100644 --- a/examples/integration_wgpu/src/main.rs +++ b/examples/integration_wgpu/src/main.rs @@ -33,7 +33,8 @@ pub fn main() -> Result<(), Box> { web_sys::window() .and_then(|win| win.document()) .and_then(|doc| doc.get_element_by_id("iced_canvas")) - .and_then(|element| element.dyn_into::().ok())? + .and_then(|element| element.dyn_into::().ok()) + .expect("Canvas with id `iced_canvas` is missing") }; #[cfg(not(target_arch = "wasm32"))] env_logger::init(); From dfc1868179d96236ddf2a9eb590832d810afb6c3 Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy Date: Mon, 12 Dec 2022 01:53:45 +0100 Subject: [PATCH 74/85] feat(native): Add MouseListener widget --- native/src/widget.rs | 3 + native/src/widget/helpers.rs | 10 + native/src/widget/mouse_listener.rs | 403 ++++++++++++++++++++++++++++ src/widget.rs | 8 + 4 files changed, 424 insertions(+) create mode 100644 native/src/widget/mouse_listener.rs diff --git a/native/src/widget.rs b/native/src/widget.rs index 2b3ca7be07..4eb3d1ba04 100644 --- a/native/src/widget.rs +++ b/native/src/widget.rs @@ -17,6 +17,7 @@ pub mod column; pub mod container; pub mod helpers; pub mod image; +pub mod mouse_listener; pub mod operation; pub mod pane_grid; pub mod pick_list; @@ -51,6 +52,8 @@ pub use helpers::*; #[doc(no_inline)] pub use image::Image; #[doc(no_inline)] +pub use mouse_listener::MouseListener; +#[doc(no_inline)] pub use pane_grid::PaneGrid; #[doc(no_inline)] pub use pick_list::PickList; diff --git a/native/src/widget/helpers.rs b/native/src/widget/helpers.rs index b25e064db3..88986d6086 100644 --- a/native/src/widget/helpers.rs +++ b/native/src/widget/helpers.rs @@ -314,3 +314,13 @@ where { widget::Svg::new(handle) } + +/// A container intercepting mouse events. +pub fn mouse_listener<'a, Message, Renderer>( + widget: impl Into>, +) -> widget::MouseListener<'a, Message, Renderer> +where + Renderer: crate::Renderer, +{ + widget::MouseListener::new(widget) +} diff --git a/native/src/widget/mouse_listener.rs b/native/src/widget/mouse_listener.rs new file mode 100644 index 0000000000..0add7c7188 --- /dev/null +++ b/native/src/widget/mouse_listener.rs @@ -0,0 +1,403 @@ +//! A container for capturing mouse events. + +use crate::event::{self, Event}; +use crate::layout; +use crate::mouse; +use crate::overlay; +use crate::renderer; +use crate::touch; +use crate::widget::{tree, Operation, Tree}; +use crate::{ + Clipboard, Element, Layout, Length, Point, Rectangle, Shell, Widget, +}; + +use std::u32; + +/// Emit messages on mouse events. +#[allow(missing_debug_implementations)] +pub struct MouseListener<'a, Message, Renderer> { + content: Element<'a, Message, Renderer>, + + /// Sets the message to emit on a left mouse button press. + on_press: Option, + + /// Sets the message to emit on a left mouse button release. + on_release: Option, + + /// Sets the message to emit on a right mouse button press. + on_right_press: Option, + + /// Sets the message to emit on a right mouse button release. + on_right_release: Option, + + /// Sets the message to emit on a middle mouse button press. + on_middle_press: Option, + + /// Sets the message to emit on a middle mouse button release. + on_middle_release: Option, + + /// Sets the message to emit when the mouse enters the widget. + on_mouse_enter: Option, + + /// Sets the messsage to emit when the mouse exits the widget. + on_mouse_exit: Option, +} + +impl<'a, Message, Renderer> MouseListener<'a, Message, Renderer> { + /// The message to emit on a left button press. + #[must_use] + pub fn on_press(mut self, message: Message) -> Self { + self.on_press = Some(message); + self + } + + /// The message to emit on a left button release. + #[must_use] + pub fn on_release(mut self, message: Message) -> Self { + self.on_release = Some(message); + self + } + + /// The message to emit on a right button press. + #[must_use] + pub fn on_right_press(mut self, message: Message) -> Self { + self.on_right_press = Some(message); + self + } + + /// The message to emit on a right button release. + #[must_use] + pub fn on_right_release(mut self, message: Message) -> Self { + self.on_right_release = Some(message); + self + } + + /// The message to emit on a middle button press. + #[must_use] + pub fn on_middle_press(mut self, message: Message) -> Self { + self.on_middle_press = Some(message); + self + } + + /// The message to emit on a middle button release. + #[must_use] + pub fn on_middle_release(mut self, message: Message) -> Self { + self.on_middle_release = Some(message); + self + } + + /// The message to emit when the mouse enters the widget. + #[must_use] + pub fn on_mouse_enter(mut self, message: Message) -> Self { + self.on_mouse_enter = Some(message); + self + } + + /// The messsage to emit when the mouse exits the widget. + #[must_use] + pub fn on_mouse_exit(mut self, message: Message) -> Self { + self.on_mouse_exit = Some(message); + self + } +} + +/// Local state of the [`MouseListener`]. +#[derive(Default)] +struct State { + hovered: bool, +} + +impl<'a, Message, Renderer> MouseListener<'a, Message, Renderer> { + /// Creates an empty [`MouseListener`]. + pub fn new(content: impl Into>) -> Self { + MouseListener { + content: content.into(), + on_press: None, + on_release: None, + on_right_press: None, + on_right_release: None, + on_middle_press: None, + on_middle_release: None, + on_mouse_enter: None, + on_mouse_exit: None, + } + } +} + +impl<'a, Message, Renderer> Widget + for MouseListener<'a, Message, Renderer> +where + Renderer: crate::Renderer, + Message: Clone, +{ + fn children(&self) -> Vec { + vec![Tree::new(&self.content)] + } + + fn diff(&self, tree: &mut Tree) { + tree.diff_children(std::slice::from_ref(&self.content)); + } + + fn width(&self) -> Length { + self.content.as_widget().width() + } + + fn height(&self) -> Length { + self.content.as_widget().height() + } + + fn layout( + &self, + renderer: &Renderer, + limits: &layout::Limits, + ) -> layout::Node { + layout( + renderer, + limits, + Widget::::width(self), + Widget::::height(self), + u32::MAX, + u32::MAX, + |renderer, limits| { + self.content.as_widget().layout(renderer, limits) + }, + ) + } + + fn operate( + &self, + tree: &mut Tree, + layout: Layout<'_>, + renderer: &Renderer, + operation: &mut dyn Operation, + ) { + operation.container(None, &mut |operation| { + self.content.as_widget().operate( + &mut tree.children[0], + layout.children().next().unwrap(), + renderer, + operation, + ); + }); + } + + fn on_event( + &mut self, + tree: &mut Tree, + event: Event, + layout: Layout<'_>, + cursor_position: Point, + renderer: &Renderer, + clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + ) -> event::Status { + if let event::Status::Captured = self.content.as_widget_mut().on_event( + &mut tree.children[0], + event.clone(), + layout.children().next().unwrap(), + cursor_position, + renderer, + clipboard, + shell, + ) { + return event::Status::Captured; + } + + update( + self, + &event, + layout, + cursor_position, + shell, + tree.state.downcast_mut::(), + ) + } + + fn mouse_interaction( + &self, + tree: &Tree, + layout: Layout<'_>, + cursor_position: Point, + viewport: &Rectangle, + renderer: &Renderer, + ) -> mouse::Interaction { + self.content.as_widget().mouse_interaction( + &tree.children[0], + layout.children().next().unwrap(), + cursor_position, + viewport, + renderer, + ) + } + + fn draw( + &self, + tree: &Tree, + renderer: &mut Renderer, + theme: &Renderer::Theme, + renderer_style: &renderer::Style, + layout: Layout<'_>, + cursor_position: Point, + viewport: &Rectangle, + ) { + self.content.as_widget().draw( + &tree.children[0], + renderer, + theme, + renderer_style, + layout.children().next().unwrap(), + cursor_position, + viewport, + ); + } + + fn overlay<'b>( + &'b mut self, + tree: &'b mut Tree, + layout: Layout<'_>, + renderer: &Renderer, + ) -> Option> { + self.content.as_widget_mut().overlay( + &mut tree.children[0], + layout.children().next().unwrap(), + renderer, + ) + } + + fn tag(&self) -> tree::Tag { + tree::Tag::of::() + } + + fn state(&self) -> tree::State { + tree::State::new(State::default()) + } +} + +impl<'a, Message, Renderer> From> + for Element<'a, Message, Renderer> +where + Message: 'a + Clone, + Renderer: 'a + crate::Renderer, +{ + fn from( + listener: MouseListener<'a, Message, Renderer>, + ) -> Element<'a, Message, Renderer> { + Element::new(listener) + } +} + +/// Processes the given [`Event`] and updates the [`State`] of an [`MouseListener`] +/// accordingly. +fn update( + widget: &mut MouseListener<'_, Message, Renderer>, + event: &Event, + layout: Layout<'_>, + cursor_position: Point, + shell: &mut Shell<'_, Message>, + state: &mut State, +) -> event::Status { + let hovered = state.hovered; + + if !layout.bounds().contains(cursor_position) { + if hovered { + state.hovered = false; + if let Some(message) = widget.on_mouse_exit.clone() { + shell.publish(message); + return event::Status::Captured; + } + } + + return event::Status::Ignored; + } + + state.hovered = true; + + if !hovered { + if let Some(message) = widget.on_mouse_enter.clone() { + shell.publish(message); + return event::Status::Captured; + } + } + + if let Some(message) = widget.on_press.clone() { + if let Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) + | Event::Touch(touch::Event::FingerPressed { .. }) = event + { + shell.publish(message); + return event::Status::Captured; + } + } + + if let Some(message) = widget.on_release.clone() { + if let Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) + | Event::Touch(touch::Event::FingerLifted { .. }) = event + { + shell.publish(message); + return event::Status::Captured; + } + } + + if let Some(message) = widget.on_right_press.clone() { + if let Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Right)) = + event + { + shell.publish(message); + return event::Status::Captured; + } + } + + if let Some(message) = widget.on_right_release.clone() { + if let Event::Mouse(mouse::Event::ButtonReleased( + mouse::Button::Right, + )) = event + { + shell.publish(message); + return event::Status::Captured; + } + } + + if let Some(message) = widget.on_middle_press.clone() { + if let Event::Mouse(mouse::Event::ButtonPressed( + mouse::Button::Middle, + )) = event + { + shell.publish(message); + return event::Status::Captured; + } + } + + if let Some(message) = widget.on_middle_release.clone() { + if let Event::Mouse(mouse::Event::ButtonReleased( + mouse::Button::Middle, + )) = event + { + shell.publish(message); + return event::Status::Captured; + } + } + + event::Status::Ignored +} + +/// Computes the layout of a [`MouseListener`]. +pub fn layout( + renderer: &Renderer, + limits: &layout::Limits, + width: Length, + height: Length, + max_height: u32, + max_width: u32, + layout_content: impl FnOnce(&Renderer, &layout::Limits) -> layout::Node, +) -> layout::Node { + let limits = limits + .loose() + .max_height(max_height) + .max_width(max_width) + .width(width) + .height(height); + + let content = layout_content(renderer, &limits); + let size = limits.resolve(content.size()); + + layout::Node::with_children(size, vec![content]) +} diff --git a/src/widget.rs b/src/widget.rs index c0ac716f17..15b34ffa4e 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -47,6 +47,14 @@ pub mod container { iced_native::widget::Container<'a, Message, Renderer>; } +pub mod mouse_listener { + //! Intercept mouse events on a widget. + + /// A container intercepting mouse events. + pub type MouseListener<'a, Message, Renderer = crate::Renderer> = + iced_native::widget::MouseListener<'a, Message, Renderer>; +} + pub mod pane_grid { //! Let your users split regions of your application and organize layout dynamically. //! From 28b0f7abf46ba6f6250e883104a6100e3e647172 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 13 Apr 2023 06:28:33 +0200 Subject: [PATCH 75/85] Delegate `layout` directly to `content` in `MouseListener` --- native/src/widget/mouse_listener.rs | 59 ++++++----------------------- 1 file changed, 11 insertions(+), 48 deletions(-) diff --git a/native/src/widget/mouse_listener.rs b/native/src/widget/mouse_listener.rs index 0add7c7188..c4e91cba37 100644 --- a/native/src/widget/mouse_listener.rs +++ b/native/src/widget/mouse_listener.rs @@ -11,8 +11,6 @@ use crate::{ Clipboard, Element, Layout, Length, Point, Rectangle, Shell, Widget, }; -use std::u32; - /// Emit messages on mouse events. #[allow(missing_debug_implementations)] pub struct MouseListener<'a, Message, Renderer> { @@ -151,17 +149,7 @@ where renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { - layout( - renderer, - limits, - Widget::::width(self), - Widget::::height(self), - u32::MAX, - u32::MAX, - |renderer, limits| { - self.content.as_widget().layout(renderer, limits) - }, - ) + self.content.as_widget().layout(renderer, limits) } fn operate( @@ -171,14 +159,12 @@ where renderer: &Renderer, operation: &mut dyn Operation, ) { - operation.container(None, &mut |operation| { - self.content.as_widget().operate( - &mut tree.children[0], - layout.children().next().unwrap(), - renderer, - operation, - ); - }); + self.content.as_widget().operate( + &mut tree.children[0], + layout, + renderer, + operation, + ); } fn on_event( @@ -194,7 +180,7 @@ where if let event::Status::Captured = self.content.as_widget_mut().on_event( &mut tree.children[0], event.clone(), - layout.children().next().unwrap(), + layout, cursor_position, renderer, clipboard, @@ -223,7 +209,7 @@ where ) -> mouse::Interaction { self.content.as_widget().mouse_interaction( &tree.children[0], - layout.children().next().unwrap(), + layout, cursor_position, viewport, renderer, @@ -245,7 +231,7 @@ where renderer, theme, renderer_style, - layout.children().next().unwrap(), + layout, cursor_position, viewport, ); @@ -259,7 +245,7 @@ where ) -> Option> { self.content.as_widget_mut().overlay( &mut tree.children[0], - layout.children().next().unwrap(), + layout, renderer, ) } @@ -378,26 +364,3 @@ fn update( event::Status::Ignored } - -/// Computes the layout of a [`MouseListener`]. -pub fn layout( - renderer: &Renderer, - limits: &layout::Limits, - width: Length, - height: Length, - max_height: u32, - max_width: u32, - layout_content: impl FnOnce(&Renderer, &layout::Limits) -> layout::Node, -) -> layout::Node { - let limits = limits - .loose() - .max_height(max_height) - .max_width(max_width) - .width(width) - .height(height); - - let content = layout_content(renderer, &limits); - let size = limits.resolve(content.size()); - - layout::Node::with_children(size, vec![content]) -} From d508ed26371edb3438ee4d1c6c9358713acd3bde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 13 Apr 2023 06:29:24 +0200 Subject: [PATCH 76/85] Remove redundant comments in `MouseListener` fields --- native/src/widget/mouse_listener.rs | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/native/src/widget/mouse_listener.rs b/native/src/widget/mouse_listener.rs index c4e91cba37..0ac7f82634 100644 --- a/native/src/widget/mouse_listener.rs +++ b/native/src/widget/mouse_listener.rs @@ -15,29 +15,13 @@ use crate::{ #[allow(missing_debug_implementations)] pub struct MouseListener<'a, Message, Renderer> { content: Element<'a, Message, Renderer>, - - /// Sets the message to emit on a left mouse button press. on_press: Option, - - /// Sets the message to emit on a left mouse button release. on_release: Option, - - /// Sets the message to emit on a right mouse button press. on_right_press: Option, - - /// Sets the message to emit on a right mouse button release. on_right_release: Option, - - /// Sets the message to emit on a middle mouse button press. on_middle_press: Option, - - /// Sets the message to emit on a middle mouse button release. on_middle_release: Option, - - /// Sets the message to emit when the mouse enters the widget. on_mouse_enter: Option, - - /// Sets the messsage to emit when the mouse exits the widget. on_mouse_exit: Option, } From 8bcb68d785cb1731894a00374970529cc59b0e76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 13 Apr 2023 06:30:09 +0200 Subject: [PATCH 77/85] Move `tag` and `state` definitions in `MouseListener` --- native/src/widget/mouse_listener.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/native/src/widget/mouse_listener.rs b/native/src/widget/mouse_listener.rs index 0ac7f82634..3db2184e47 100644 --- a/native/src/widget/mouse_listener.rs +++ b/native/src/widget/mouse_listener.rs @@ -112,6 +112,14 @@ where Renderer: crate::Renderer, Message: Clone, { + fn tag(&self) -> tree::Tag { + tree::Tag::of::() + } + + fn state(&self) -> tree::State { + tree::State::new(State::default()) + } + fn children(&self) -> Vec { vec![Tree::new(&self.content)] } @@ -233,14 +241,6 @@ where renderer, ) } - - fn tag(&self) -> tree::Tag { - tree::Tag::of::() - } - - fn state(&self) -> tree::State { - tree::State::new(State::default()) - } } impl<'a, Message, Renderer> From> From f247528725982e3214df3b06c153347fe4945b0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 13 Apr 2023 06:34:17 +0200 Subject: [PATCH 78/85] Ignore mouse movements events in `MouseListener` These should be ignored generally, since they are considered passive user actions. --- native/src/widget/mouse_listener.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/native/src/widget/mouse_listener.rs b/native/src/widget/mouse_listener.rs index 3db2184e47..f9c3acacc5 100644 --- a/native/src/widget/mouse_listener.rs +++ b/native/src/widget/mouse_listener.rs @@ -271,9 +271,9 @@ fn update( if !layout.bounds().contains(cursor_position) { if hovered { state.hovered = false; + if let Some(message) = widget.on_mouse_exit.clone() { shell.publish(message); - return event::Status::Captured; } } @@ -285,7 +285,8 @@ fn update( if !hovered { if let Some(message) = widget.on_mouse_enter.clone() { shell.publish(message); - return event::Status::Captured; + + return event::Status::Ignored; } } @@ -294,6 +295,7 @@ fn update( | Event::Touch(touch::Event::FingerPressed { .. }) = event { shell.publish(message); + return event::Status::Captured; } } @@ -303,6 +305,7 @@ fn update( | Event::Touch(touch::Event::FingerLifted { .. }) = event { shell.publish(message); + return event::Status::Captured; } } @@ -312,6 +315,7 @@ fn update( event { shell.publish(message); + return event::Status::Captured; } } @@ -322,6 +326,7 @@ fn update( )) = event { shell.publish(message); + return event::Status::Captured; } } @@ -332,6 +337,7 @@ fn update( )) = event { shell.publish(message); + return event::Status::Captured; } } @@ -342,6 +348,7 @@ fn update( )) = event { shell.publish(message); + return event::Status::Captured; } } From 29971c9d71fe81b12eae56eeaf87c6b6890c83f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 13 Apr 2023 06:44:09 +0200 Subject: [PATCH 79/85] Avoid returning on mouse enter in `MouseListener` The event that triggers a mouse enter could be a mouse button press/release. --- native/src/widget/mouse_listener.rs | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/native/src/widget/mouse_listener.rs b/native/src/widget/mouse_listener.rs index f9c3acacc5..a7a06bcc31 100644 --- a/native/src/widget/mouse_listener.rs +++ b/native/src/widget/mouse_listener.rs @@ -86,7 +86,7 @@ impl<'a, Message, Renderer> MouseListener<'a, Message, Renderer> { /// Local state of the [`MouseListener`]. #[derive(Default)] struct State { - hovered: bool, + is_hovered: bool, } impl<'a, Message, Renderer> MouseListener<'a, Message, Renderer> { @@ -266,27 +266,23 @@ fn update( shell: &mut Shell<'_, Message>, state: &mut State, ) -> event::Status { - let hovered = state.hovered; + let was_hovered = state.is_hovered; - if !layout.bounds().contains(cursor_position) { - if hovered { - state.hovered = false; + state.is_hovered = layout.bounds().contains(cursor_position); - if let Some(message) = widget.on_mouse_exit.clone() { - shell.publish(message); + if !state.is_hovered { + if was_hovered { + if let Some(message) = widget.on_mouse_exit.as_ref() { + shell.publish(message.clone()); } } return event::Status::Ignored; } - state.hovered = true; - - if !hovered { - if let Some(message) = widget.on_mouse_enter.clone() { - shell.publish(message); - - return event::Status::Ignored; + if !was_hovered { + if let Some(message) = widget.on_mouse_enter.as_ref() { + shell.publish(message.clone()); } } From 6b359b496cf435c26f982ee0e52d7324c79ad1d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 13 Apr 2023 06:44:51 +0200 Subject: [PATCH 80/85] Avoid cloning messages unnecessarily in `MouseListener` --- native/src/widget/mouse_listener.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/native/src/widget/mouse_listener.rs b/native/src/widget/mouse_listener.rs index a7a06bcc31..00e20c04fa 100644 --- a/native/src/widget/mouse_listener.rs +++ b/native/src/widget/mouse_listener.rs @@ -286,64 +286,64 @@ fn update( } } - if let Some(message) = widget.on_press.clone() { + if let Some(message) = widget.on_press.as_ref() { if let Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) | Event::Touch(touch::Event::FingerPressed { .. }) = event { - shell.publish(message); + shell.publish(message.clone()); return event::Status::Captured; } } - if let Some(message) = widget.on_release.clone() { + if let Some(message) = widget.on_release.as_ref() { if let Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) | Event::Touch(touch::Event::FingerLifted { .. }) = event { - shell.publish(message); + shell.publish(message.clone()); return event::Status::Captured; } } - if let Some(message) = widget.on_right_press.clone() { + if let Some(message) = widget.on_right_press.as_ref() { if let Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Right)) = event { - shell.publish(message); + shell.publish(message.clone()); return event::Status::Captured; } } - if let Some(message) = widget.on_right_release.clone() { + if let Some(message) = widget.on_right_release.as_ref() { if let Event::Mouse(mouse::Event::ButtonReleased( mouse::Button::Right, )) = event { - shell.publish(message); + shell.publish(message.clone()); return event::Status::Captured; } } - if let Some(message) = widget.on_middle_press.clone() { + if let Some(message) = widget.on_middle_press.as_ref() { if let Event::Mouse(mouse::Event::ButtonPressed( mouse::Button::Middle, )) = event { - shell.publish(message); + shell.publish(message.clone()); return event::Status::Captured; } } - if let Some(message) = widget.on_middle_release.clone() { + if let Some(message) = widget.on_middle_release.as_ref() { if let Event::Mouse(mouse::Event::ButtonReleased( mouse::Button::Middle, )) = event { - shell.publish(message); + shell.publish(message.clone()); return event::Status::Captured; } From f55a97b738096d85086858026234ee95dcd79289 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 13 Apr 2023 06:46:33 +0200 Subject: [PATCH 81/85] Rename `MouseListener` to `MouseArea` --- native/src/widget.rs | 4 ++-- native/src/widget/helpers.rs | 6 ++--- .../{mouse_listener.rs => mouse_area.rs} | 24 +++++++++---------- src/widget.rs | 6 ++--- 4 files changed, 20 insertions(+), 20 deletions(-) rename native/src/widget/{mouse_listener.rs => mouse_area.rs} (93%) diff --git a/native/src/widget.rs b/native/src/widget.rs index 4eb3d1ba04..6849da1762 100644 --- a/native/src/widget.rs +++ b/native/src/widget.rs @@ -17,7 +17,7 @@ pub mod column; pub mod container; pub mod helpers; pub mod image; -pub mod mouse_listener; +pub mod mouse_area; pub mod operation; pub mod pane_grid; pub mod pick_list; @@ -52,7 +52,7 @@ pub use helpers::*; #[doc(no_inline)] pub use image::Image; #[doc(no_inline)] -pub use mouse_listener::MouseListener; +pub use mouse_area::MouseArea; #[doc(no_inline)] pub use pane_grid::PaneGrid; #[doc(no_inline)] diff --git a/native/src/widget/helpers.rs b/native/src/widget/helpers.rs index 88986d6086..5f44e22c1e 100644 --- a/native/src/widget/helpers.rs +++ b/native/src/widget/helpers.rs @@ -316,11 +316,11 @@ where } /// A container intercepting mouse events. -pub fn mouse_listener<'a, Message, Renderer>( +pub fn mouse_area<'a, Message, Renderer>( widget: impl Into>, -) -> widget::MouseListener<'a, Message, Renderer> +) -> widget::MouseArea<'a, Message, Renderer> where Renderer: crate::Renderer, { - widget::MouseListener::new(widget) + widget::MouseArea::new(widget) } diff --git a/native/src/widget/mouse_listener.rs b/native/src/widget/mouse_area.rs similarity index 93% rename from native/src/widget/mouse_listener.rs rename to native/src/widget/mouse_area.rs index 00e20c04fa..1b8b318c62 100644 --- a/native/src/widget/mouse_listener.rs +++ b/native/src/widget/mouse_area.rs @@ -13,7 +13,7 @@ use crate::{ /// Emit messages on mouse events. #[allow(missing_debug_implementations)] -pub struct MouseListener<'a, Message, Renderer> { +pub struct MouseArea<'a, Message, Renderer> { content: Element<'a, Message, Renderer>, on_press: Option, on_release: Option, @@ -25,7 +25,7 @@ pub struct MouseListener<'a, Message, Renderer> { on_mouse_exit: Option, } -impl<'a, Message, Renderer> MouseListener<'a, Message, Renderer> { +impl<'a, Message, Renderer> MouseArea<'a, Message, Renderer> { /// The message to emit on a left button press. #[must_use] pub fn on_press(mut self, message: Message) -> Self { @@ -83,16 +83,16 @@ impl<'a, Message, Renderer> MouseListener<'a, Message, Renderer> { } } -/// Local state of the [`MouseListener`]. +/// Local state of the [`MouseArea`]. #[derive(Default)] struct State { is_hovered: bool, } -impl<'a, Message, Renderer> MouseListener<'a, Message, Renderer> { - /// Creates an empty [`MouseListener`]. +impl<'a, Message, Renderer> MouseArea<'a, Message, Renderer> { + /// Creates an empty [`MouseArea`]. pub fn new(content: impl Into>) -> Self { - MouseListener { + MouseArea { content: content.into(), on_press: None, on_release: None, @@ -107,7 +107,7 @@ impl<'a, Message, Renderer> MouseListener<'a, Message, Renderer> { } impl<'a, Message, Renderer> Widget - for MouseListener<'a, Message, Renderer> + for MouseArea<'a, Message, Renderer> where Renderer: crate::Renderer, Message: Clone, @@ -243,23 +243,23 @@ where } } -impl<'a, Message, Renderer> From> +impl<'a, Message, Renderer> From> for Element<'a, Message, Renderer> where Message: 'a + Clone, Renderer: 'a + crate::Renderer, { fn from( - listener: MouseListener<'a, Message, Renderer>, + area: MouseArea<'a, Message, Renderer>, ) -> Element<'a, Message, Renderer> { - Element::new(listener) + Element::new(area) } } -/// Processes the given [`Event`] and updates the [`State`] of an [`MouseListener`] +/// Processes the given [`Event`] and updates the [`State`] of an [`MouseArea`] /// accordingly. fn update( - widget: &mut MouseListener<'_, Message, Renderer>, + widget: &mut MouseArea<'_, Message, Renderer>, event: &Event, layout: Layout<'_>, cursor_position: Point, diff --git a/src/widget.rs b/src/widget.rs index 15b34ffa4e..d06fdafb24 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -47,12 +47,12 @@ pub mod container { iced_native::widget::Container<'a, Message, Renderer>; } -pub mod mouse_listener { +pub mod mouse_area { //! Intercept mouse events on a widget. /// A container intercepting mouse events. - pub type MouseListener<'a, Message, Renderer = crate::Renderer> = - iced_native::widget::MouseListener<'a, Message, Renderer>; + pub type MouseArea<'a, Message, Renderer = crate::Renderer> = + iced_native::widget::MouseArea<'a, Message, Renderer>; } pub mod pane_grid { From 020f1120e332b450f12ccf7aa3f4a52c5ee282a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 13 Apr 2023 06:50:00 +0200 Subject: [PATCH 82/85] Fix documentation of `MouseArea::new` --- native/src/widget/mouse_area.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/native/src/widget/mouse_area.rs b/native/src/widget/mouse_area.rs index 1b8b318c62..5bf096d306 100644 --- a/native/src/widget/mouse_area.rs +++ b/native/src/widget/mouse_area.rs @@ -90,7 +90,7 @@ struct State { } impl<'a, Message, Renderer> MouseArea<'a, Message, Renderer> { - /// Creates an empty [`MouseArea`]. + /// Creates a [`MouseArea`] with the given content. pub fn new(content: impl Into>) -> Self { MouseArea { content: content.into(), From 0c39112a2e7c63e7f8c2a03e67216b3736cb3aeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 13 Apr 2023 06:58:53 +0200 Subject: [PATCH 83/85] Remove support for `on_mouse_enter` and `on_mouse_exit` in `MouseArea` These need continuity guarantees (e.g. mandatory widget id), which we don't have yet! --- native/src/widget/mouse_area.rs | 48 +++------------------------------ 1 file changed, 3 insertions(+), 45 deletions(-) diff --git a/native/src/widget/mouse_area.rs b/native/src/widget/mouse_area.rs index 5bf096d306..69cfddbfde 100644 --- a/native/src/widget/mouse_area.rs +++ b/native/src/widget/mouse_area.rs @@ -21,8 +21,6 @@ pub struct MouseArea<'a, Message, Renderer> { on_right_release: Option, on_middle_press: Option, on_middle_release: Option, - on_mouse_enter: Option, - on_mouse_exit: Option, } impl<'a, Message, Renderer> MouseArea<'a, Message, Renderer> { @@ -67,26 +65,12 @@ impl<'a, Message, Renderer> MouseArea<'a, Message, Renderer> { self.on_middle_release = Some(message); self } - - /// The message to emit when the mouse enters the widget. - #[must_use] - pub fn on_mouse_enter(mut self, message: Message) -> Self { - self.on_mouse_enter = Some(message); - self - } - - /// The messsage to emit when the mouse exits the widget. - #[must_use] - pub fn on_mouse_exit(mut self, message: Message) -> Self { - self.on_mouse_exit = Some(message); - self - } } /// Local state of the [`MouseArea`]. #[derive(Default)] struct State { - is_hovered: bool, + // TODO: Support on_mouse_enter and on_mouse_exit } impl<'a, Message, Renderer> MouseArea<'a, Message, Renderer> { @@ -100,8 +84,6 @@ impl<'a, Message, Renderer> MouseArea<'a, Message, Renderer> { on_right_release: None, on_middle_press: None, on_middle_release: None, - on_mouse_enter: None, - on_mouse_exit: None, } } } @@ -181,14 +163,7 @@ where return event::Status::Captured; } - update( - self, - &event, - layout, - cursor_position, - shell, - tree.state.downcast_mut::(), - ) + update(self, &event, layout, cursor_position, shell) } fn mouse_interaction( @@ -264,28 +239,11 @@ fn update( layout: Layout<'_>, cursor_position: Point, shell: &mut Shell<'_, Message>, - state: &mut State, ) -> event::Status { - let was_hovered = state.is_hovered; - - state.is_hovered = layout.bounds().contains(cursor_position); - - if !state.is_hovered { - if was_hovered { - if let Some(message) = widget.on_mouse_exit.as_ref() { - shell.publish(message.clone()); - } - } - + if !layout.bounds().contains(cursor_position) { return event::Status::Ignored; } - if !was_hovered { - if let Some(message) = widget.on_mouse_enter.as_ref() { - shell.publish(message.clone()); - } - } - if let Some(message) = widget.on_press.as_ref() { if let Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) | Event::Touch(touch::Event::FingerPressed { .. }) = event From b1a5341a45200defc84673b8edeeb99ec20ebb04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 13 Apr 2023 08:25:46 +0200 Subject: [PATCH 84/85] Add script to generate a release summary Courtesy of GPT-4 --- docs/release_summary.py | 52 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 docs/release_summary.py diff --git a/docs/release_summary.py b/docs/release_summary.py new file mode 100644 index 0000000000..cd4593b5ac --- /dev/null +++ b/docs/release_summary.py @@ -0,0 +1,52 @@ +import re +import sys +import requests +from typing import List, Tuple + +if len(sys.argv) < 3: + print("Usage: python release_summary.py ") + exit(1) + +TOKEN = sys.argv[1] +HEADERS = {"Authorization": f"Bearer {TOKEN}"} +PR_COMMIT_REGEX = re.compile(r"(?i)Merge pull request #(\d+).*") + +def get_merged_prs_since_release(repo: str, previous_release_branch: str) -> List[Tuple[str, int, str, str]]: + prs = [] + compare_url = f"https://api.github.com/repos/{repo}/compare/{previous_release_branch}...master" + compare_response = requests.get(compare_url, headers=HEADERS) + + if compare_response.status_code == 200: + compare_data = compare_response.json() + for commit in compare_data["commits"]: + match = PR_COMMIT_REGEX.search(commit["commit"]["message"]) + if match: + pr_number = int(match.group(1)) + pr_url = f"https://api.github.com/repos/{repo}/pulls/{pr_number}" + pr_response = requests.get(pr_url, headers=HEADERS) + if pr_response.status_code == 200: + pr_data = pr_response.json() + prs.append((pr_data["title"], pr_number, pr_data["html_url"], pr_data["user"]["login"])) + else: + print(f"Error fetching PR {pr_number}: {pr_response.status_code}") + else: + print(f"Error comparing branches: {compare_response.status_code}") + + return prs + +def print_pr_list(prs: List[Tuple[str, int, str, str]]): + for pr in prs: + print(f"- {pr[0]}. [#{pr[1]}]({pr[2]})") + +def print_authors(prs: List[Tuple[str, int, str, str]]): + authors = set(pr[3] for pr in prs) + print("\nAuthors:") + for author in sorted(authors, key=str.casefold): + print(f"- @{author}") + +if __name__ == "__main__": + repo = "iced-rs/iced" + previous_release_branch = sys.argv[2] + merged_prs = get_merged_prs_since_release(repo, previous_release_branch) + print_pr_list(merged_prs) + print_authors(merged_prs) From c79cc2d2b3df99f69b048c68e503916c779a1102 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Thu, 13 Apr 2023 08:31:17 +0200 Subject: [PATCH 85/85] Bump versions :tada: --- CHANGELOG.md | 56 +++++++++++++++++++++++++++++++++- Cargo.toml | 18 +++++------ README.md | 4 +-- core/Cargo.toml | 2 +- core/README.md | 2 +- core/src/lib.rs | 2 +- futures/src/subscription.rs | 6 ++-- glow/Cargo.toml | 6 ++-- glow/README.md | 2 +- glow/src/lib.rs | 2 +- glutin/Cargo.toml | 8 ++--- glutin/README.md | 2 +- graphics/Cargo.toml | 6 ++-- lazy/Cargo.toml | 4 +-- native/Cargo.toml | 6 ++-- native/README.md | 2 +- native/src/lib.rs | 4 +-- native/src/subscription.rs | 2 +- native/src/user_interface.rs | 4 +-- native/src/widget.rs | 10 +++--- native/src/widget/pane_grid.rs | 2 +- src/application.rs | 18 +++++------ src/lib.rs | 8 ++--- src/sandbox.rs | 22 ++++++------- src/widget.rs | 2 +- style/Cargo.toml | 4 +-- wgpu/Cargo.toml | 6 ++-- wgpu/README.md | 2 +- wgpu/src/lib.rs | 2 +- winit/Cargo.toml | 6 ++-- winit/README.md | 2 +- winit/src/conversion.rs | 12 ++++---- winit/src/lib.rs | 2 +- 33 files changed, 145 insertions(+), 91 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d9d1fb351..077f4af3d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,59 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.9.0] - 2023-04-13 +### Added +- `MouseArea` widget. [#1594](https://github.com/iced-rs/iced/pull/1594) +- `channel` helper in `subscription`. [#1786](https://github.com/iced-rs/iced/pull/1786) +- Configurable `width` for `Scrollable`. [#1749](https://github.com/iced-rs/iced/pull/1749) +- Support for disabled `TextInput`. [#1744](https://github.com/iced-rs/iced/pull/1744) +- Platform-specific window settings. [#1730](https://github.com/iced-rs/iced/pull/1730) +- Left and right colors for sliders. [#1643](https://github.com/iced-rs/iced/pull/1643) +- Icon for `TextInput`. [#1702](https://github.com/iced-rs/iced/pull/1702) +- Mouse over scrollbar flag for `scrollable::StyleSheet`. [#1669](https://github.com/iced-rs/iced/pull/1669) +- Better example for `Radio`. [#1762](https://github.com/iced-rs/iced/pull/1762) + +### Changed +- `wgpu` has been updated to `0.15` in `iced_wgpu`. [#1789](https://github.com/iced-rs/iced/pull/1789) +- `resvg` has been updated to `0.29` in `iced_graphics`. [#1733](https://github.com/iced-rs/iced/pull/1733) +- `subscription::run` now takes a function pointer. [#1723](https://github.com/iced-rs/iced/pull/1723) + +### Fixed +- Redundant `on_scroll` messages for `Scrollable`. [#1788](https://github.com/iced-rs/iced/pull/1788) +- Outdated items in `ROADMAP.md` [#1782](https://github.com/iced-rs/iced/pull/1782) +- Colons in shader labels causing compilation issues in `iced_wgpu`. [#1779](https://github.com/iced-rs/iced/pull/1779) +- Re-expose winit features for window servers in Linux. [#1777](https://github.com/iced-rs/iced/pull/1777) +- Replacement of application node in Wasm. [#1765](https://github.com/iced-rs/iced/pull/1765) +- `clippy` lints for Rust 1.68. [#1755](https://github.com/iced-rs/iced/pull/1755) +- Unnecessary `Component` rebuilds. [#1754](https://github.com/iced-rs/iced/pull/1754) +- Incorrect package name in checkbox example docs. [#1750](https://github.com/iced-rs/iced/pull/1750) +- Fullscreen only working on primary monitor. [#1742](https://github.com/iced-rs/iced/pull/1742) +- `Padding::fit` on irregular values for an axis. [#1734](https://github.com/iced-rs/iced/pull/1734) +- `Debug` implementation of `Font` displaying its bytes. [#1731](https://github.com/iced-rs/iced/pull/1731) +- Sliders bleeding over their rail. [#1721](https://github.com/iced-rs/iced/pull/1721) + +### Removed +- `Fill` variant for `Alignment`. [#1735](https://github.com/iced-rs/iced/pull/1735) + +Many thanks to... + +- @ahoneybun +- @bq-wrongway +- @bungoboingo +- @casperstorm +- @Davidster +- @ElhamAryanpur +- @FinnPerry +- @GyulyVGC +- @JungleTryne +- @lupd +- @mmstick +- @nicksenger +- @Night-Hunter-NF +- @tarkah +- @traxys +- @Xaeroxe + ## [0.8.0] - 2023-02-18 ### Added - Generic pixel units. [#1711](https://github.com/iced-rs/iced/pull/1711) @@ -414,7 +467,8 @@ Many thanks to... ### Added - First release! :tada: -[Unreleased]: https://github.com/iced-rs/iced/compare/0.8.0...HEAD +[Unreleased]: https://github.com/iced-rs/iced/compare/0.9.0...HEAD +[0.9.0]: https://github.com/iced-rs/iced/compare/0.8.0...0.9.0 [0.8.0]: https://github.com/iced-rs/iced/compare/0.7.0...0.8.0 [0.7.0]: https://github.com/iced-rs/iced/compare/0.6.0...0.7.0 [0.6.0]: https://github.com/iced-rs/iced/compare/0.5.0...0.6.0 diff --git a/Cargo.toml b/Cargo.toml index d26ec2b6b8..1f8eb017aa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iced" -version = "0.8.0" +version = "0.9.0" authors = ["Héctor Ramón Jiménez "] edition = "2021" description = "A cross-platform GUI library inspired by Elm" @@ -66,13 +66,13 @@ members = [ ] [dependencies] -iced_core = { version = "0.8", path = "core" } +iced_core = { version = "0.9", path = "core" } iced_futures = { version = "0.6", path = "futures" } -iced_native = { version = "0.9", path = "native" } -iced_graphics = { version = "0.7", path = "graphics" } -iced_winit = { version = "0.8", path = "winit", features = ["application"] } -iced_glutin = { version = "0.7", path = "glutin", optional = true } -iced_glow = { version = "0.7", path = "glow", optional = true } +iced_native = { version = "0.10", path = "native" } +iced_graphics = { version = "0.8", path = "graphics" } +iced_winit = { version = "0.9", path = "winit", features = ["application"] } +iced_glutin = { version = "0.8", path = "glutin", optional = true } +iced_glow = { version = "0.8", path = "glow", optional = true } thiserror = "1.0" [dependencies.image_rs] @@ -81,10 +81,10 @@ package = "image" optional = true [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -iced_wgpu = { version = "0.9", path = "wgpu", optional = true } +iced_wgpu = { version = "0.10", path = "wgpu", optional = true } [target.'cfg(target_arch = "wasm32")'.dependencies] -iced_wgpu = { version = "0.9", path = "wgpu", features = ["webgl"], optional = true } +iced_wgpu = { version = "0.10", path = "wgpu", features = ["webgl"], optional = true } [package.metadata.docs.rs] rustdoc-args = ["--cfg", "docsrs"] diff --git a/README.md b/README.md index a5ebf230b8..fe2a4a640e 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ __Iced is currently experimental software.__ [Take a look at the roadmap], Add `iced` as a dependency in your `Cargo.toml`: ```toml -iced = "0.8" +iced = "0.9" ``` If your project is using a Rust edition older than 2021, then you will need to @@ -215,7 +215,7 @@ cargo run --features iced/glow --package game_of_life and then use it in your project with ```toml -iced = { version = "0.8", default-features = false, features = ["glow"] } +iced = { version = "0.9", default-features = false, features = ["glow"] } ``` __NOTE:__ Chances are you have hardware that supports at least OpenGL 2.1 or OpenGL ES 2.0, diff --git a/core/Cargo.toml b/core/Cargo.toml index 0d6310d3bd..3a00a53e2a 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iced_core" -version = "0.8.1" +version = "0.9.0" authors = ["Héctor Ramón Jiménez "] edition = "2021" description = "The essential concepts of Iced" diff --git a/core/README.md b/core/README.md index 64d92e7840..519e060886 100644 --- a/core/README.md +++ b/core/README.md @@ -18,7 +18,7 @@ This crate is meant to be a starting point for an Iced runtime. Add `iced_core` as a dependency in your `Cargo.toml`: ```toml -iced_core = "0.8" +iced_core = "0.9" ``` __Iced moves fast and the `master` branch can contain breaking changes!__ If diff --git a/core/src/lib.rs b/core/src/lib.rs index d3596b4d04..da3cb87459 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -7,7 +7,7 @@ //! ![The foundations of the Iced ecosystem](https://github.com/iced-rs/iced/blob/0525d76ff94e828b7b21634fa94a747022001c83/docs/graphs/foundations.png?raw=true) //! //! [Iced]: https://github.com/iced-rs/iced -//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.8/native +//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.9/native //! [`iced_web`]: https://github.com/iced-rs/iced_web #![doc( html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg" diff --git a/futures/src/subscription.rs b/futures/src/subscription.rs index fe53fd7e1e..18c66a5ae4 100644 --- a/futures/src/subscription.rs +++ b/futures/src/subscription.rs @@ -126,9 +126,9 @@ impl std::fmt::Debug for Subscription { /// - [`stopwatch`], a watch with start/stop and reset buttons showcasing how /// to listen to time. /// -/// [examples]: https://github.com/iced-rs/iced/tree/0.8/examples -/// [`download_progress`]: https://github.com/iced-rs/iced/tree/0.8/examples/download_progress -/// [`stopwatch`]: https://github.com/iced-rs/iced/tree/0.8/examples/stopwatch +/// [examples]: https://github.com/iced-rs/iced/tree/0.9/examples +/// [`download_progress`]: https://github.com/iced-rs/iced/tree/0.9/examples/download_progress +/// [`stopwatch`]: https://github.com/iced-rs/iced/tree/0.9/examples/stopwatch pub trait Recipe { /// The events that will be produced by a [`Subscription`] with this /// [`Recipe`]. diff --git a/glow/Cargo.toml b/glow/Cargo.toml index 1a848ab75c..facc668a4f 100644 --- a/glow/Cargo.toml +++ b/glow/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iced_glow" -version = "0.7.0" +version = "0.8.0" authors = ["Héctor Ramón Jiménez "] edition = "2021" description = "A glow renderer for iced" @@ -34,11 +34,11 @@ bytemuck = "1.4" log = "0.4" [dependencies.iced_native] -version = "0.9" +version = "0.10" path = "../native" [dependencies.iced_graphics] -version = "0.7" +version = "0.8" path = "../graphics" features = ["font-fallback", "font-icons", "opengl"] diff --git a/glow/README.md b/glow/README.md index 95c9d62a92..aa3d4d1246 100644 --- a/glow/README.md +++ b/glow/README.md @@ -28,7 +28,7 @@ Currently, `iced_glow` supports the following primitives: Add `iced_glow` as a dependency in your `Cargo.toml`: ```toml -iced_glow = "0.7" +iced_glow = "0.8" ``` __Iced moves fast and the `master` branch can contain breaking changes!__ If diff --git a/glow/src/lib.rs b/glow/src/lib.rs index 9e7de0d91a..4614abfd57 100644 --- a/glow/src/lib.rs +++ b/glow/src/lib.rs @@ -3,7 +3,7 @@ //! ![The native path of the Iced ecosystem](https://github.com/iced-rs/iced/blob/0525d76ff94e828b7b21634fa94a747022001c83/docs/graphs/native.png?raw=true) //! //! [`glow`]: https://github.com/grovesNL/glow -//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.8/native +//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.9/native #![doc( html_logo_url = "https://raw.githubusercontent.com/iced-rs/iced/9ab6923e943f784985e9ef9ca28b10278297225d/docs/logo.svg" )] diff --git a/glutin/Cargo.toml b/glutin/Cargo.toml index 10d3778be9..2858a088a9 100644 --- a/glutin/Cargo.toml +++ b/glutin/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iced_glutin" -version = "0.7.0" +version = "0.8.0" authors = ["Héctor Ramón Jiménez "] edition = "2021" description = "A glutin runtime for Iced" @@ -24,16 +24,16 @@ git = "https://github.com/iced-rs/glutin" rev = "da8d291486b4c9bec12487a46c119c4b1d386abf" [dependencies.iced_native] -version = "0.9" +version = "0.10" path = "../native" [dependencies.iced_winit] -version = "0.8" +version = "0.9" path = "../winit" features = ["application"] [dependencies.iced_graphics] -version = "0.7" +version = "0.8" path = "../graphics" features = ["opengl"] diff --git a/glutin/README.md b/glutin/README.md index 45e8ee6b4f..9c0ac4590d 100644 --- a/glutin/README.md +++ b/glutin/README.md @@ -20,7 +20,7 @@ It exposes a renderer-agnostic `Application` trait that can be implemented and t Add `iced_glutin` as a dependency in your `Cargo.toml`: ```toml -iced_glutin = "0.7" +iced_glutin = "0.8" ``` __Iced moves fast and the `master` branch can contain breaking changes!__ If diff --git a/graphics/Cargo.toml b/graphics/Cargo.toml index a37c99a20e..57b291e75b 100644 --- a/graphics/Cargo.toml +++ b/graphics/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iced_graphics" -version = "0.7.0" +version = "0.8.0" authors = ["Héctor Ramón Jiménez "] edition = "2021" description = "A bunch of backend-agnostic types that can be leveraged to build a renderer for Iced" @@ -44,11 +44,11 @@ version = "1.4" features = ["derive"] [dependencies.iced_native] -version = "0.9" +version = "0.10" path = "../native" [dependencies.iced_style] -version = "0.7" +version = "0.8" path = "../style" [dependencies.lyon] diff --git a/lazy/Cargo.toml b/lazy/Cargo.toml index c739b312f8..8ca83ad0ee 100644 --- a/lazy/Cargo.toml +++ b/lazy/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iced_lazy" -version = "0.5.0" +version = "0.6.0" authors = ["Héctor Ramón Jiménez "] edition = "2021" description = "Lazy widgets for Iced" @@ -14,5 +14,5 @@ categories = ["gui"] ouroboros = "0.13" [dependencies.iced_native] -version = "0.9" +version = "0.10" path = "../native" diff --git a/native/Cargo.toml b/native/Cargo.toml index 1eedf0dac5..869c1bb068 100644 --- a/native/Cargo.toml +++ b/native/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iced_native" -version = "0.9.1" +version = "0.10.1" authors = ["Héctor Ramón Jiménez "] edition = "2021" description = "A renderer-agnostic library for native GUIs" @@ -17,7 +17,7 @@ num-traits = "0.2" thiserror = "1" [dependencies.iced_core] -version = "0.8" +version = "0.9" path = "../core" [dependencies.iced_futures] @@ -26,5 +26,5 @@ path = "../futures" features = ["thread-pool"] [dependencies.iced_style] -version = "0.7" +version = "0.8" path = "../style" diff --git a/native/README.md b/native/README.md index 996daa7687..cf36b22470 100644 --- a/native/README.md +++ b/native/README.md @@ -28,7 +28,7 @@ To achieve this, it introduces a bunch of reusable interfaces: Add `iced_native` as a dependency in your `Cargo.toml`: ```toml -iced_native = "0.9" +iced_native = "0.10" ``` __Iced moves fast and the `master` branch can contain breaking changes!__ If diff --git a/native/src/lib.rs b/native/src/lib.rs index ebdc849021..dc77950c27 100644 --- a/native/src/lib.rs +++ b/native/src/lib.rs @@ -23,8 +23,8 @@ //! - Build a new renderer, see the [renderer] module. //! - Build a custom widget, start at the [`Widget`] trait. //! -//! [`iced_core`]: https://github.com/iced-rs/iced/tree/0.8/core -//! [`iced_winit`]: https://github.com/iced-rs/iced/tree/0.8/winit +//! [`iced_core`]: https://github.com/iced-rs/iced/tree/0.9/core +//! [`iced_winit`]: https://github.com/iced-rs/iced/tree/0.9/winit //! [`druid`]: https://github.com/xi-editor/druid //! [`raw-window-handle`]: https://github.com/rust-windowing/raw-window-handle //! [renderer]: crate::renderer diff --git a/native/src/subscription.rs b/native/src/subscription.rs index 0ff5e3201e..115ffc421e 100644 --- a/native/src/subscription.rs +++ b/native/src/subscription.rs @@ -229,7 +229,7 @@ where /// Check out the [`websocket`] example, which showcases this pattern to maintain a WebSocket /// connection open. /// -/// [`websocket`]: https://github.com/iced-rs/iced/tree/0.8/examples/websocket +/// [`websocket`]: https://github.com/iced-rs/iced/tree/0.9/examples/websocket pub fn channel( id: I, size: usize, diff --git a/native/src/user_interface.rs b/native/src/user_interface.rs index 68ccda55dd..e5c90bbbfb 100644 --- a/native/src/user_interface.rs +++ b/native/src/user_interface.rs @@ -21,8 +21,8 @@ use crate::{ /// The [`integration_opengl`] & [`integration_wgpu`] examples use a /// [`UserInterface`] to integrate Iced in an existing graphical application. /// -/// [`integration_opengl`]: https://github.com/iced-rs/iced/tree/0.8/examples/integration_opengl -/// [`integration_wgpu`]: https://github.com/iced-rs/iced/tree/0.8/examples/integration_wgpu +/// [`integration_opengl`]: https://github.com/iced-rs/iced/tree/0.9/examples/integration_opengl +/// [`integration_wgpu`]: https://github.com/iced-rs/iced/tree/0.9/examples/integration_wgpu #[allow(missing_debug_implementations)] pub struct UserInterface<'a, Message, Renderer> { root: Element<'a, Message, Renderer>, diff --git a/native/src/widget.rs b/native/src/widget.rs index 6849da1762..6b83f1fae2 100644 --- a/native/src/widget.rs +++ b/native/src/widget.rs @@ -113,12 +113,12 @@ use crate::{Clipboard, Layout, Length, Point, Rectangle, Shell}; /// - [`geometry`], a custom widget showcasing how to draw geometry with the /// `Mesh2D` primitive in [`iced_wgpu`]. /// -/// [examples]: https://github.com/iced-rs/iced/tree/0.8/examples -/// [`bezier_tool`]: https://github.com/iced-rs/iced/tree/0.8/examples/bezier_tool -/// [`custom_widget`]: https://github.com/iced-rs/iced/tree/0.8/examples/custom_widget -/// [`geometry`]: https://github.com/iced-rs/iced/tree/0.8/examples/geometry +/// [examples]: https://github.com/iced-rs/iced/tree/0.9/examples +/// [`bezier_tool`]: https://github.com/iced-rs/iced/tree/0.9/examples/bezier_tool +/// [`custom_widget`]: https://github.com/iced-rs/iced/tree/0.9/examples/custom_widget +/// [`geometry`]: https://github.com/iced-rs/iced/tree/0.9/examples/geometry /// [`lyon`]: https://github.com/nical/lyon -/// [`iced_wgpu`]: https://github.com/iced-rs/iced/tree/0.8/wgpu +/// [`iced_wgpu`]: https://github.com/iced-rs/iced/tree/0.9/wgpu pub trait Widget where Renderer: crate::Renderer, diff --git a/native/src/widget/pane_grid.rs b/native/src/widget/pane_grid.rs index bcb17ebddc..06ece7f4a3 100644 --- a/native/src/widget/pane_grid.rs +++ b/native/src/widget/pane_grid.rs @@ -6,7 +6,7 @@ //! The [`pane_grid` example] showcases how to use a [`PaneGrid`] with resizing, //! drag and drop, and hotkey support. //! -//! [`pane_grid` example]: https://github.com/iced-rs/iced/tree/0.8/examples/pane_grid +//! [`pane_grid` example]: https://github.com/iced-rs/iced/tree/0.9/examples/pane_grid mod axis; mod configuration; mod content; diff --git a/src/application.rs b/src/application.rs index 1db5c93fda..54d2e391bf 100644 --- a/src/application.rs +++ b/src/application.rs @@ -39,15 +39,15 @@ pub use iced_native::application::{Appearance, StyleSheet}; /// to listen to time. /// - [`todos`], a todos tracker inspired by [TodoMVC]. /// -/// [The repository has a bunch of examples]: https://github.com/iced-rs/iced/tree/0.8/examples -/// [`clock`]: https://github.com/iced-rs/iced/tree/0.8/examples/clock -/// [`download_progress`]: https://github.com/iced-rs/iced/tree/0.8/examples/download_progress -/// [`events`]: https://github.com/iced-rs/iced/tree/0.8/examples/events -/// [`game_of_life`]: https://github.com/iced-rs/iced/tree/0.8/examples/game_of_life -/// [`pokedex`]: https://github.com/iced-rs/iced/tree/0.8/examples/pokedex -/// [`solar_system`]: https://github.com/iced-rs/iced/tree/0.8/examples/solar_system -/// [`stopwatch`]: https://github.com/iced-rs/iced/tree/0.8/examples/stopwatch -/// [`todos`]: https://github.com/iced-rs/iced/tree/0.8/examples/todos +/// [The repository has a bunch of examples]: https://github.com/iced-rs/iced/tree/0.9/examples +/// [`clock`]: https://github.com/iced-rs/iced/tree/0.9/examples/clock +/// [`download_progress`]: https://github.com/iced-rs/iced/tree/0.9/examples/download_progress +/// [`events`]: https://github.com/iced-rs/iced/tree/0.9/examples/events +/// [`game_of_life`]: https://github.com/iced-rs/iced/tree/0.9/examples/game_of_life +/// [`pokedex`]: https://github.com/iced-rs/iced/tree/0.9/examples/pokedex +/// [`solar_system`]: https://github.com/iced-rs/iced/tree/0.9/examples/solar_system +/// [`stopwatch`]: https://github.com/iced-rs/iced/tree/0.9/examples/stopwatch +/// [`todos`]: https://github.com/iced-rs/iced/tree/0.9/examples/todos /// [`Sandbox`]: crate::Sandbox /// [`Canvas`]: crate::widget::Canvas /// [PokéAPI]: https://pokeapi.co/ diff --git a/src/lib.rs b/src/lib.rs index 318852f903..62e5140fa5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,13 +24,13 @@ //! [scrollables]: https://gfycat.com/perkybaggybaboon-rust-gui //! [Debug overlay with performance metrics]: https://gfycat.com/incredibledarlingbee //! [Modular ecosystem]: https://github.com/iced-rs/iced/blob/master/ECOSYSTEM.md -//! [renderer-agnostic native runtime]: https://github.com/iced-rs/iced/tree/0.8/native +//! [renderer-agnostic native runtime]: https://github.com/iced-rs/iced/tree/0.9/native //! [`wgpu`]: https://github.com/gfx-rs/wgpu-rs -//! [built-in renderer]: https://github.com/iced-rs/iced/tree/0.8/wgpu -//! [windowing shell]: https://github.com/iced-rs/iced/tree/0.8/winit +//! [built-in renderer]: https://github.com/iced-rs/iced/tree/0.9/wgpu +//! [windowing shell]: https://github.com/iced-rs/iced/tree/0.9/winit //! [`dodrio`]: https://github.com/fitzgen/dodrio //! [web runtime]: https://github.com/iced-rs/iced_web -//! [examples]: https://github.com/iced-rs/iced/tree/0.8/examples +//! [examples]: https://github.com/iced-rs/iced/tree/0.9/examples //! [repository]: https://github.com/iced-rs/iced //! //! # Overview diff --git a/src/sandbox.rs b/src/sandbox.rs index e8ed0f8199..cca327b646 100644 --- a/src/sandbox.rs +++ b/src/sandbox.rs @@ -34,19 +34,19 @@ use crate::{Application, Command, Element, Error, Settings, Subscription}; /// - [`tour`], a simple UI tour that can run both on native platforms and the /// web! /// -/// [The repository has a bunch of examples]: https://github.com/iced-rs/iced/tree/0.8/examples -/// [`bezier_tool`]: https://github.com/iced-rs/iced/tree/0.8/examples/bezier_tool -/// [`counter`]: https://github.com/iced-rs/iced/tree/0.8/examples/counter -/// [`custom_widget`]: https://github.com/iced-rs/iced/tree/0.8/examples/custom_widget -/// [`geometry`]: https://github.com/iced-rs/iced/tree/0.8/examples/geometry -/// [`pane_grid`]: https://github.com/iced-rs/iced/tree/0.8/examples/pane_grid -/// [`progress_bar`]: https://github.com/iced-rs/iced/tree/0.8/examples/progress_bar -/// [`styling`]: https://github.com/iced-rs/iced/tree/0.8/examples/styling -/// [`svg`]: https://github.com/iced-rs/iced/tree/0.8/examples/svg -/// [`tour`]: https://github.com/iced-rs/iced/tree/0.8/examples/tour +/// [The repository has a bunch of examples]: https://github.com/iced-rs/iced/tree/0.9/examples +/// [`bezier_tool`]: https://github.com/iced-rs/iced/tree/0.9/examples/bezier_tool +/// [`counter`]: https://github.com/iced-rs/iced/tree/0.9/examples/counter +/// [`custom_widget`]: https://github.com/iced-rs/iced/tree/0.9/examples/custom_widget +/// [`geometry`]: https://github.com/iced-rs/iced/tree/0.9/examples/geometry +/// [`pane_grid`]: https://github.com/iced-rs/iced/tree/0.9/examples/pane_grid +/// [`progress_bar`]: https://github.com/iced-rs/iced/tree/0.9/examples/progress_bar +/// [`styling`]: https://github.com/iced-rs/iced/tree/0.9/examples/styling +/// [`svg`]: https://github.com/iced-rs/iced/tree/0.9/examples/svg +/// [`tour`]: https://github.com/iced-rs/iced/tree/0.9/examples/tour /// [`Canvas widget`]: crate::widget::Canvas /// [the overview]: index.html#overview -/// [`iced_wgpu`]: https://github.com/iced-rs/iced/tree/0.8/wgpu +/// [`iced_wgpu`]: https://github.com/iced-rs/iced/tree/0.9/wgpu /// [`Svg` widget]: crate::widget::Svg /// [Ghostscript Tiger]: https://commons.wikimedia.org/wiki/File:Ghostscript_Tiger.svg /// diff --git a/src/widget.rs b/src/widget.rs index d06fdafb24..440278110d 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -64,7 +64,7 @@ pub mod pane_grid { //! The [`pane_grid` example] showcases how to use a [`PaneGrid`] with resizing, //! drag and drop, and hotkey support. //! - //! [`pane_grid` example]: https://github.com/iced-rs/iced/tree/0.8/examples/pane_grid + //! [`pane_grid` example]: https://github.com/iced-rs/iced/tree/0.9/examples/pane_grid pub use iced_native::widget::pane_grid::{ Axis, Configuration, Direction, DragEvent, Line, Node, Pane, ResizeEvent, Split, State, StyleSheet, diff --git a/style/Cargo.toml b/style/Cargo.toml index 3b54f1ecee..0bb354e0d9 100644 --- a/style/Cargo.toml +++ b/style/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iced_style" -version = "0.7.0" +version = "0.8.0" authors = ["Héctor Ramón Jiménez "] edition = "2021" description = "The default set of styles of Iced" @@ -11,7 +11,7 @@ keywords = ["gui", "ui", "graphics", "interface", "widgets"] categories = ["gui"] [dependencies.iced_core] -version = "0.8" +version = "0.9" path = "../core" features = ["palette"] diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index 4dcd07f771..3478ef5957 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iced_wgpu" -version = "0.9.0" +version = "0.10.0" authors = ["Héctor Ramón Jiménez "] edition = "2021" description = "A wgpu renderer for Iced" @@ -42,11 +42,11 @@ version = "1.9" features = ["derive"] [dependencies.iced_native] -version = "0.9" +version = "0.10" path = "../native" [dependencies.iced_graphics] -version = "0.7" +version = "0.8" path = "../graphics" features = ["font-fallback", "font-icons"] diff --git a/wgpu/README.md b/wgpu/README.md index 3e6af103ea..f8c88374e0 100644 --- a/wgpu/README.md +++ b/wgpu/README.md @@ -30,7 +30,7 @@ Currently, `iced_wgpu` supports the following primitives: Add `iced_wgpu` as a dependency in your `Cargo.toml`: ```toml -iced_wgpu = "0.9" +iced_wgpu = "0.10" ``` __Iced moves fast and the `master` branch can contain breaking changes!__ If diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 1a2936814c..969e319924 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -16,7 +16,7 @@ //! - Meshes of triangles, useful to draw geometry freely. //! //! [Iced]: https://github.com/iced-rs/iced -//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.8/native +//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.9/native //! [`wgpu`]: https://github.com/gfx-rs/wgpu-rs //! [WebGPU API]: https://gpuweb.github.io/gpuweb/ //! [`wgpu_glyph`]: https://github.com/hecrj/wgpu_glyph diff --git a/winit/Cargo.toml b/winit/Cargo.toml index 255b28bbbc..8788b66743 100644 --- a/winit/Cargo.toml +++ b/winit/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iced_winit" -version = "0.8.0" +version = "0.9.1" authors = ["Héctor Ramón Jiménez "] edition = "2021" description = "A winit runtime for Iced" @@ -34,11 +34,11 @@ rev = "940457522e9fb9f5dac228b0ecfafe0138b4048c" default-features = false [dependencies.iced_native] -version = "0.9" +version = "0.10" path = "../native" [dependencies.iced_graphics] -version = "0.7" +version = "0.8" path = "../graphics" [dependencies.iced_futures] diff --git a/winit/README.md b/winit/README.md index 83810473e4..9130797027 100644 --- a/winit/README.md +++ b/winit/README.md @@ -20,7 +20,7 @@ It exposes a renderer-agnostic `Application` trait that can be implemented and t Add `iced_winit` as a dependency in your `Cargo.toml`: ```toml -iced_winit = "0.8" +iced_winit = "0.9" ``` __Iced moves fast and the `master` branch can contain breaking changes!__ If diff --git a/winit/src/conversion.rs b/winit/src/conversion.rs index cf066ef64a..e416c07307 100644 --- a/winit/src/conversion.rs +++ b/winit/src/conversion.rs @@ -1,7 +1,7 @@ //! Convert [`winit`] types into [`iced_native`] types, and viceversa. //! //! [`winit`]: https://github.com/rust-windowing/winit -//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.8/native +//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.9/native use crate::keyboard; use crate::mouse; use crate::touch; @@ -218,7 +218,7 @@ pub fn mode(mode: Option) -> window::Mode { /// Converts a `MouseCursor` from [`iced_native`] to a [`winit`] cursor icon. /// /// [`winit`]: https://github.com/rust-windowing/winit -/// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.8/native +/// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.9/native pub fn mouse_interaction( interaction: mouse::Interaction, ) -> winit::window::CursorIcon { @@ -243,7 +243,7 @@ pub fn mouse_interaction( /// Converts a `MouseButton` from [`winit`] to an [`iced_native`] mouse button. /// /// [`winit`]: https://github.com/rust-windowing/winit -/// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.8/native +/// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.9/native pub fn mouse_button(mouse_button: winit::event::MouseButton) -> mouse::Button { match mouse_button { winit::event::MouseButton::Left => mouse::Button::Left, @@ -259,7 +259,7 @@ pub fn mouse_button(mouse_button: winit::event::MouseButton) -> mouse::Button { /// modifiers state. /// /// [`winit`]: https://github.com/rust-windowing/winit -/// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.8/native +/// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.9/native pub fn modifiers( modifiers: winit::event::ModifiersState, ) -> keyboard::Modifiers { @@ -286,7 +286,7 @@ pub fn cursor_position( /// Converts a `Touch` from [`winit`] to an [`iced_native`] touch event. /// /// [`winit`]: https://github.com/rust-windowing/winit -/// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.8/native +/// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.9/native pub fn touch_event( touch: winit::event::Touch, scale_factor: f64, @@ -317,7 +317,7 @@ pub fn touch_event( /// Converts a `VirtualKeyCode` from [`winit`] to an [`iced_native`] key code. /// /// [`winit`]: https://github.com/rust-windowing/winit -/// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.8/native +/// [`iced_native`]: https://github.com/iced-rs/iced/tree/0.9/native pub fn key_code( virtual_keycode: winit::event::VirtualKeyCode, ) -> keyboard::KeyCode { diff --git a/winit/src/lib.rs b/winit/src/lib.rs index 3a33e1741b..6b6c6045cb 100644 --- a/winit/src/lib.rs +++ b/winit/src/lib.rs @@ -11,7 +11,7 @@ //! Additionally, a [`conversion`] module is available for users that decide to //! implement a custom event loop. //! -//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.8/native +//! [`iced_native`]: https://github.com/iced-rs/iced/tree/0.9/native //! [`winit`]: https://github.com/rust-windowing/winit //! [`conversion`]: crate::conversion #![doc(