From c8981d00963dac0dc03e6351255ba8823519162c Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 9 Feb 2025 11:10:54 +0100 Subject: Implement `sipper` support through `Task::sip` :tada: --- Cargo.lock | 10 ++++++++++ Cargo.toml | 1 + runtime/Cargo.toml | 3 ++- runtime/src/task.rs | 23 ++++++++++++++++++++++- 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c5e9443f..2b4efc1c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2592,6 +2592,7 @@ dependencies = [ "iced_core", "iced_futures", "raw-window-handle 0.6.2", + "sipper", "thiserror 1.0.69", ] @@ -5227,6 +5228,15 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" +[[package]] +name = "sipper" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d936de9741a68cb9452b683ffcc1fce44be7a79446ac5918319a42738da2d165" +dependencies = [ + "futures", +] + [[package]] name = "skrifa" version = "0.22.3" diff --git a/Cargo.toml b/Cargo.toml index c95fbd1e..021ac4d4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -172,6 +172,7 @@ raw-window-handle = "0.6" resvg = "0.42" rustc-hash = "2.0" sha2 = "0.10" +sipper = "0.0.4" smol = "1.0" smol_str = "0.2" softbuffer = "0.4" diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 703c3ed9..fc212ef8 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -23,5 +23,6 @@ iced_core.workspace = true iced_futures.workspace = true iced_futures.features = ["thread-pool"] -thiserror.workspace = true raw-window-handle.workspace = true +sipper.workspace = true +thiserror.workspace = true diff --git a/runtime/src/task.rs b/runtime/src/task.rs index 22cfb63e..fb46ca45 100644 --- a/runtime/src/task.rs +++ b/runtime/src/task.rs @@ -3,7 +3,6 @@ use crate::core::widget; use crate::futures::futures::channel::mpsc; use crate::futures::futures::channel::oneshot; use crate::futures::futures::future::{self, FutureExt}; -use crate::futures::futures::never::Never; use crate::futures::futures::stream::{self, Stream, StreamExt}; use crate::futures::{boxed_stream, BoxStream, MaybeSend}; use crate::Action; @@ -11,6 +10,9 @@ use crate::Action; use std::future::Future; use std::sync::Arc; +#[doc(no_inline)] +pub use sipper::{sipper, stream, Never, Sender, Sipper, Straw}; + /// A set of concurrent actions to be performed by the iced runtime. /// /// A [`Task`] _may_ produce a bunch of values of type `T`. @@ -57,6 +59,25 @@ impl Task { Self::stream(stream.map(f)) } + /// Creates a [`Task`] that runs the given [`Sipper`] to completion, mapping + /// progress with the first closure and the output with the second one. + pub fn sip( + sipper: S, + on_progress: impl Fn(Progress) -> T + Send + 'static, + on_output: impl FnOnce(Output) -> T + Send + 'static, + ) -> Self + where + S: Sipper + Send + 'static, + S::Future: Send + 'static, + Output: Send, + Progress: Send, + T: Send + 'static, + { + Self::stream(stream(sipper::sipper(move |sender| async move { + on_output(sipper.map(on_progress).run(sender).await) + }))) + } + /// Combines the given tasks and produces a single [`Task`] that will run all of them /// in parallel. pub fn batch(tasks: impl IntoIterator) -> Self -- cgit From 7ba2e3913395b212c7789569e1d683e7766cc348 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 9 Feb 2025 21:05:42 +0100 Subject: Update `sipper` and relax `Send` requirements --- Cargo.lock | 5 +++-- Cargo.toml | 2 +- runtime/src/task.rs | 13 +++++-------- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2b4efc1c..db1a397a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5230,11 +5230,12 @@ checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" [[package]] name = "sipper" -version = "0.0.4" +version = "0.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d936de9741a68cb9452b683ffcc1fce44be7a79446ac5918319a42738da2d165" +checksum = "273c7573677e3835339763d44dd6ee0683b92108d04b311108809e9819f4bc13" dependencies = [ "futures", + "pin-project-lite", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 021ac4d4..e8772772 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -172,7 +172,7 @@ raw-window-handle = "0.6" resvg = "0.42" rustc-hash = "2.0" sha2 = "0.10" -sipper = "0.0.4" +sipper = "0.0.5" smol = "1.0" smol_str = "0.2" softbuffer = "0.4" diff --git a/runtime/src/task.rs b/runtime/src/task.rs index fb46ca45..989f0ed5 100644 --- a/runtime/src/task.rs +++ b/runtime/src/task.rs @@ -63,18 +63,15 @@ impl Task { /// progress with the first closure and the output with the second one. pub fn sip( sipper: S, - on_progress: impl Fn(Progress) -> T + Send + 'static, - on_output: impl FnOnce(Output) -> T + Send + 'static, + on_progress: impl Fn(Progress) -> T + MaybeSend + 'static, + on_output: impl FnOnce(Output) -> T + MaybeSend + 'static, ) -> Self where - S: Sipper + Send + 'static, - S::Future: Send + 'static, - Output: Send, - Progress: Send, - T: Send + 'static, + S: Sipper + MaybeSend + 'static, + T: MaybeSend + 'static, { Self::stream(stream(sipper::sipper(move |sender| async move { - on_output(sipper.map(on_progress).run(sender).await) + on_output(sipper.with(on_progress).run(sender).await) }))) } -- cgit From 6154395be0668af42ac597c9db35e5e198c264c9 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 10 Feb 2025 17:47:18 +0100 Subject: Use `sipper::Core` in `Task::sip` --- runtime/src/task.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/runtime/src/task.rs b/runtime/src/task.rs index 989f0ed5..bfc36d75 100644 --- a/runtime/src/task.rs +++ b/runtime/src/task.rs @@ -61,13 +61,13 @@ impl Task { /// Creates a [`Task`] that runs the given [`Sipper`] to completion, mapping /// progress with the first closure and the output with the second one. - pub fn sip( + pub fn sip( sipper: S, - on_progress: impl Fn(Progress) -> T + MaybeSend + 'static, - on_output: impl FnOnce(Output) -> T + MaybeSend + 'static, + on_progress: impl Fn(S::Progress) -> T + MaybeSend + 'static, + on_output: impl FnOnce(::Output) -> T + MaybeSend + 'static, ) -> Self where - S: Sipper + MaybeSend + 'static, + S: sipper::Core + MaybeSend + 'static, T: MaybeSend + 'static, { Self::stream(stream(sipper::sipper(move |sender| async move { -- cgit From 0ff5f9e8a7bc8d56d6ca8335a954550560a4eaf5 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 11 Feb 2025 00:39:37 +0100 Subject: Update `sipper` to `0.1.0` --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index db1a397a..a86070db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5230,9 +5230,9 @@ checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" [[package]] name = "sipper" -version = "0.0.5" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "273c7573677e3835339763d44dd6ee0683b92108d04b311108809e9819f4bc13" +checksum = "1bccb4192828b3d9a08e0b5a73f17795080dfb278b50190216e3ae2132cf4f95" dependencies = [ "futures", "pin-project-lite", diff --git a/Cargo.toml b/Cargo.toml index e8772772..e990cf59 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -172,7 +172,7 @@ raw-window-handle = "0.6" resvg = "0.42" rustc-hash = "2.0" sha2 = "0.10" -sipper = "0.0.5" +sipper = "0.1.0" smol = "1.0" smol_str = "0.2" softbuffer = "0.4" -- cgit From 54ffbbf043e5f064f554d82a22d71699a1c75f0c Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 11 Feb 2025 00:55:59 +0100 Subject: Export `sipper` types in `task` module --- src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 849b51e9..e4649938 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -519,7 +519,9 @@ pub use Length::{Fill, FillPortion, Shrink}; pub mod task { //! Create runtime tasks. - pub use crate::runtime::task::{Handle, Task}; + pub use crate::runtime::task::{ + sipper, stream, Handle, Never, Sipper, Straw, Task, + }; } pub mod clipboard { -- cgit From 05618ea9b39048ca905918682e99c2d139683078 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 11 Feb 2025 00:56:14 +0100 Subject: Use `sipper` in `download_progress` --- examples/download_progress/src/download.rs | 23 ++++++------- examples/download_progress/src/main.rs | 52 ++++++++++++++++-------------- 2 files changed, 37 insertions(+), 38 deletions(-) diff --git a/examples/download_progress/src/download.rs b/examples/download_progress/src/download.rs index d63fb906..5b81f7a2 100644 --- a/examples/download_progress/src/download.rs +++ b/examples/download_progress/src/download.rs @@ -1,16 +1,14 @@ -use iced::futures::{SinkExt, Stream, StreamExt}; -use iced::stream::try_channel; +use iced::futures::StreamExt; +use iced::task::{sipper, Straw}; use std::sync::Arc; -pub fn download( - url: impl AsRef, -) -> impl Stream> { - try_channel(1, move |mut output| async move { +pub fn download(url: impl AsRef) -> impl Straw<(), Progress, Error> { + sipper(move |mut progress| async move { let response = reqwest::get(url.as_ref()).await?; let total = response.content_length().ok_or(Error::NoContentLength)?; - let _ = output.send(Progress::Downloading { percent: 0.0 }).await; + let _ = progress.send(Progress { percent: 0.0 }).await; let mut byte_stream = response.bytes_stream(); let mut downloaded = 0; @@ -19,23 +17,20 @@ pub fn download( let bytes = next_bytes?; downloaded += bytes.len(); - let _ = output - .send(Progress::Downloading { + let _ = progress + .send(Progress { percent: 100.0 * downloaded as f32 / total as f32, }) .await; } - let _ = output.send(Progress::Finished).await; - Ok(()) }) } #[derive(Debug, Clone)] -pub enum Progress { - Downloading { percent: f32 }, - Finished, +pub struct Progress { + pub percent: f32, } #[derive(Debug, Clone)] diff --git a/examples/download_progress/src/main.rs b/examples/download_progress/src/main.rs index f4b07203..8f59f1dc 100644 --- a/examples/download_progress/src/main.rs +++ b/examples/download_progress/src/main.rs @@ -25,7 +25,7 @@ struct Example { pub enum Message { Add, Download(usize), - DownloadProgressed(usize, Result), + DownloadUpdated(usize, Update), } impl Example { @@ -52,15 +52,13 @@ impl Example { let task = download.start(); - task.map(move |progress| { - Message::DownloadProgressed(index, progress) - }) + task.map(move |update| Message::DownloadUpdated(index, update)) } - Message::DownloadProgressed(id, progress) => { + Message::DownloadUpdated(id, update) => { if let Some(download) = self.downloads.iter_mut().find(|download| download.id == id) { - download.progress(progress); + download.update(update); } Task::none() @@ -95,6 +93,12 @@ struct Download { state: State, } +#[derive(Debug, Clone)] +pub enum Update { + Downloading(download::Progress), + Finished(Result<(), download::Error>), +} + #[derive(Debug)] enum State { Idle, @@ -111,18 +115,20 @@ impl Download { } } - pub fn start( - &mut self, - ) -> Task> { + pub fn start(&mut self) -> Task { match self.state { State::Idle { .. } | State::Finished { .. } | State::Errored { .. } => { - let (task, handle) = Task::stream(download( - "https://huggingface.co/\ + let (task, handle) = Task::sip( + download( + "https://huggingface.co/\ mattshumer/Reflection-Llama-3.1-70B/\ resolve/main/model-00001-of-00162.safetensors", - )) + ), + Update::Downloading, + Update::Finished, + ) .abortable(); self.state = State::Downloading { @@ -136,20 +142,18 @@ impl Download { } } - pub fn progress( - &mut self, - new_progress: Result, - ) { + pub fn update(&mut self, update: Update) { if let State::Downloading { progress, .. } = &mut self.state { - match new_progress { - Ok(download::Progress::Downloading { percent }) => { - *progress = percent; - } - Ok(download::Progress::Finished) => { - self.state = State::Finished; + match update { + Update::Downloading(new_progress) => { + *progress = new_progress.percent; } - Err(_error) => { - self.state = State::Errored; + Update::Finished(result) => { + self.state = if result.is_ok() { + State::Finished + } else { + State::Errored + }; } } } -- cgit From f37d068af52700570f863960c3432df85a244a7c Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 11 Feb 2025 01:27:51 +0100 Subject: Rewrite `websocket` example using `sipper` --- examples/websocket/src/echo.rs | 98 ++++++++++++++----------------------- futures/src/backend/native/tokio.rs | 11 ++--- futures/src/lib.rs | 1 - src/lib.rs | 1 - 4 files changed, 43 insertions(+), 68 deletions(-) diff --git a/examples/websocket/src/echo.rs b/examples/websocket/src/echo.rs index 14652936..6fa4c54a 100644 --- a/examples/websocket/src/echo.rs +++ b/examples/websocket/src/echo.rs @@ -1,73 +1,63 @@ pub mod server; use iced::futures; -use iced::stream; +use iced::task::{sipper, Never, Sipper}; use iced::widget::text; use futures::channel::mpsc; use futures::sink::SinkExt; -use futures::stream::{Stream, StreamExt}; +use futures::stream::StreamExt; use async_tungstenite::tungstenite; use std::fmt; -pub fn connect() -> impl Stream { - stream::channel(100, |mut output| async move { - let mut state = State::Disconnected; - +pub fn connect() -> impl Sipper { + sipper(|mut output| async move { loop { - match &mut state { - State::Disconnected => { - const ECHO_SERVER: &str = "ws://127.0.0.1:3030"; + const ECHO_SERVER: &str = "ws://127.0.0.1:3030"; - match async_tungstenite::tokio::connect_async(ECHO_SERVER) - .await - { - Ok((websocket, _)) => { - let (sender, receiver) = mpsc::channel(100); + let (mut websocket, mut input) = + match async_tungstenite::tokio::connect_async(ECHO_SERVER).await + { + Ok((websocket, _)) => { + let (sender, receiver) = mpsc::channel(100); - let _ = output - .send(Event::Connected(Connection(sender))) - .await; + let _ = output + .send(Event::Connected(Connection(sender))) + .await; - state = State::Connected(websocket, receiver); - } - Err(_) => { - tokio::time::sleep( - tokio::time::Duration::from_secs(1), - ) + (websocket.fuse(), receiver) + } + Err(_) => { + tokio::time::sleep(tokio::time::Duration::from_secs(1)) .await; - let _ = output.send(Event::Disconnected).await; - } + let _ = output.send(Event::Disconnected).await; + + continue; } - } - 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, + }; + + loop { + futures::select! { + received = 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; + break; } + 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_err() { - let _ = output.send(Event::Disconnected).await; - - state = State::Disconnected; - } + if result.is_err() { + let _ = output.send(Event::Disconnected).await; } } } @@ -76,18 +66,6 @@ pub fn connect() -> impl Stream { }) } -#[derive(Debug)] -#[allow(clippy::large_enum_variant)] -enum State { - Disconnected, - Connected( - async_tungstenite::WebSocketStream< - async_tungstenite::tokio::ConnectStream, - >, - mpsc::Receiver, - ), -} - #[derive(Debug, Clone)] pub enum Event { Connected(Connection), diff --git a/futures/src/backend/native/tokio.rs b/futures/src/backend/native/tokio.rs index e0be83a6..c38ef566 100644 --- a/futures/src/backend/native/tokio.rs +++ b/futures/src/backend/native/tokio.rs @@ -23,11 +23,10 @@ impl crate::Executor for Executor { pub mod time { //! Listen and react to time. use crate::core::time::{Duration, Instant}; - use crate::stream; use crate::subscription::Subscription; use crate::MaybeSend; - use futures::SinkExt; + use futures::stream; use std::future::Future; /// Returns a [`Subscription`] that produces messages at a set interval. @@ -66,12 +65,12 @@ pub mod time { let f = *f; let interval = *interval; - stream::channel(1, move |mut output| async move { - loop { - let _ = output.send(f().await).await; - + stream::unfold(0, move |i| async move { + if i > 0 { tokio::time::sleep(interval).await; } + + Some((f().await, i + 1)) }) }) } diff --git a/futures/src/lib.rs b/futures/src/lib.rs index 31738823..a874a618 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -15,7 +15,6 @@ pub mod backend; pub mod event; pub mod executor; pub mod keyboard; -pub mod stream; pub mod subscription; pub use executor::Executor; diff --git a/src/lib.rs b/src/lib.rs index e4649938..79992a3e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -478,7 +478,6 @@ use iced_winit::core; use iced_winit::runtime; pub use iced_futures::futures; -pub use iced_futures::stream; #[cfg(feature = "highlighter")] pub use iced_highlighter as highlighter; -- cgit From 06ece6a8c30541b7b2b6bcbb370fdffc9f1fcb79 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 11 Feb 2025 01:32:51 +0100 Subject: Remove unneded `let _ =` in `websocket` example --- examples/websocket/src/echo.rs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/examples/websocket/src/echo.rs b/examples/websocket/src/echo.rs index 6fa4c54a..149a260c 100644 --- a/examples/websocket/src/echo.rs +++ b/examples/websocket/src/echo.rs @@ -22,9 +22,7 @@ pub fn connect() -> impl Sipper { Ok((websocket, _)) => { let (sender, receiver) = mpsc::channel(100); - let _ = output - .send(Event::Connected(Connection(sender))) - .await; + output.send(Event::Connected(Connection(sender))).await; (websocket.fuse(), receiver) } @@ -32,8 +30,7 @@ pub fn connect() -> impl Sipper { tokio::time::sleep(tokio::time::Duration::from_secs(1)) .await; - let _ = output.send(Event::Disconnected).await; - + output.send(Event::Disconnected).await; continue; } }; @@ -43,21 +40,20 @@ pub fn connect() -> impl Sipper { received = websocket.select_next_some() => { match received { Ok(tungstenite::Message::Text(message)) => { - let _ = output.send(Event::MessageReceived(Message::User(message))).await; + output.send(Event::MessageReceived(Message::User(message))).await; } Err(_) => { - let _ = output.send(Event::Disconnected).await; + output.send(Event::Disconnected).await; break; } - Ok(_) => continue, + Ok(_) => {}, } } - message = input.select_next_some() => { let result = websocket.send(tungstenite::Message::Text(message.to_string())).await; if result.is_err() { - let _ = output.send(Event::Disconnected).await; + output.send(Event::Disconnected).await; } } } -- cgit From 9f21eae1528fa414adbfb987ce4c851fa58326fe Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 11 Feb 2025 02:34:10 +0100 Subject: Introduce `Task::map_with` --- examples/download_progress/src/main.rs | 2 +- examples/gallery/src/main.rs | 18 +++++++------- futures/src/lib.rs | 1 + runtime/src/task.rs | 44 ++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 5 files changed, 55 insertions(+), 11 deletions(-) diff --git a/examples/download_progress/src/main.rs b/examples/download_progress/src/main.rs index 8f59f1dc..cc7a7800 100644 --- a/examples/download_progress/src/main.rs +++ b/examples/download_progress/src/main.rs @@ -52,7 +52,7 @@ impl Example { let task = download.start(); - task.map(move |update| Message::DownloadUpdated(index, update)) + task.map_with(index, Message::DownloadUpdated) } Message::DownloadUpdated(id, update) => { if let Some(download) = diff --git a/examples/gallery/src/main.rs b/examples/gallery/src/main.rs index ab22679d..441ad924 100644 --- a/examples/gallery/src/main.rs +++ b/examples/gallery/src/main.rs @@ -94,17 +94,15 @@ impl Gallery { return Task::none(); }; - Task::batch(vec![ - Task::perform( + Task::batch([ + Task::future( image.clone().blurhash(Preview::WIDTH, Preview::HEIGHT), - move |result| Message::BlurhashDecoded(id, result), - ), - Task::perform( - image.download(Size::Thumbnail { - width: Preview::WIDTH, - }), - move |result| Message::ThumbnailDownloaded(id, result), - ), + ) + .map_with(id, Message::BlurhashDecoded), + Task::future(image.download(Size::Thumbnail { + width: Preview::WIDTH, + })) + .map_with(id, Message::ThumbnailDownloaded), ]) } Message::ImageDownloaded(Ok(rgba)) => { diff --git a/futures/src/lib.rs b/futures/src/lib.rs index a874a618..31738823 100644 --- a/futures/src/lib.rs +++ b/futures/src/lib.rs @@ -15,6 +15,7 @@ pub mod backend; pub mod event; pub mod executor; pub mod keyboard; +pub mod stream; pub mod subscription; pub use executor::Executor; diff --git a/runtime/src/task.rs b/runtime/src/task.rs index bfc36d75..1a1ef699 100644 --- a/runtime/src/task.rs +++ b/runtime/src/task.rs @@ -98,6 +98,50 @@ impl Task { self.then(move |output| Task::done(f(output))) } + /// Combines a prefix value with the result of the [`Task`] using + /// the provided closure. + /// + /// Sometimes you will want to identify the source or target + /// of some [`Task`] in your UI. This can be achieved through + /// normal means by using [`map`]: + /// + /// ```rust + /// # use iced_runtime::Task; + /// # let task = Task::none(); + /// # enum Message { TaskCompleted(u32, ()) } + /// let id = 123; + /// + /// # let _ = { + /// task.map(move |result| Message::TaskCompleted(id, result)) + /// # }; + /// ``` + /// + /// Quite a mouthful. [`map_with`] lets you write: + /// + /// ```rust + /// # use iced_runtime::Task; + /// # let task = Task::none(); + /// # enum Message { TaskCompleted(u32, ()) } + /// # let id = 123; + /// # let _ = { + /// task.map_with(id, Message::TaskCompleted) + /// # }; + /// ``` + /// + /// Much nicer! + pub fn map_with( + self, + prefix: P, + mut f: impl FnMut(P, T) -> O + MaybeSend + 'static, + ) -> Task + where + T: MaybeSend + 'static, + P: MaybeSend + Clone + 'static, + O: MaybeSend + 'static, + { + self.map(move |result| f(prefix.clone(), result)) + } + /// Performs a new [`Task`] for every output of the current [`Task`] using the /// given closure. /// diff --git a/src/lib.rs b/src/lib.rs index 79992a3e..e4649938 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -478,6 +478,7 @@ use iced_winit::core; use iced_winit::runtime; pub use iced_futures::futures; +pub use iced_futures::stream; #[cfg(feature = "highlighter")] pub use iced_highlighter as highlighter; -- cgit From 0c528be2ea74f9aae1d4ac80b282ba9c16674649 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 11 Feb 2025 03:39:42 +0100 Subject: Introduce `with` helper and use `sipper` in `gallery` example --- Cargo.lock | 1 + core/src/lib.rs | 60 +++++++++++++++++++ examples/download_progress/src/main.rs | 4 +- examples/gallery/Cargo.toml | 1 + examples/gallery/src/civitai.rs | 106 ++++++++++++++++++++------------- examples/gallery/src/main.rs | 30 +++++----- runtime/src/task.rs | 46 +------------- src/lib.rs | 2 +- 8 files changed, 144 insertions(+), 106 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a86070db..5c015e2a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1875,6 +1875,7 @@ dependencies = [ "image", "reqwest", "serde", + "sipper", "tokio", ] diff --git a/core/src/lib.rs b/core/src/lib.rs index d5c221ac..ac0a228f 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -93,3 +93,63 @@ pub use smol_str::SmolStr; pub fn never(never: std::convert::Infallible) -> T { match never {} } + +/// Applies the given prefix value to the provided closure and returns +/// a new closure that takes the other argument. +/// +/// This lets you partially "apply" a function—equivalent to currying, +/// but it only works with binary functions. If you want to apply an +/// arbitrary number of arguments, use the [`with!`] macro instead. +/// +/// # When is this useful? +/// Sometimes you will want to identify the source or target +/// of some message in your user interface. This can be achieved through +/// normal means by defining a closure and moving the identifier +/// inside: +/// +/// ```rust +/// # let element: Option<()> = Some(()); +/// # enum Message { ButtonPressed(u32, ()) } +/// let id = 123; +/// +/// # let _ = { +/// element.map(move |result| Message::ButtonPressed(id, result)) +/// # }; +/// ``` +/// +/// That's quite a mouthful. [`with()`] lets you write: +/// +/// ```rust +/// # use iced_core::with; +/// # let element: Option<()> = Some(()); +/// # enum Message { ButtonPressed(u32, ()) } +/// let id = 123; +/// +/// # let _ = { +/// element.map(with(Message::ButtonPressed, id)) +/// # }; +/// ``` +/// +/// Effectively creating the same closure that partially applies +/// the `id` to the message—but much more concise! +pub fn with( + mut f: impl FnMut(T, R) -> O, + prefix: T, +) -> impl FnMut(R) -> O +where + T: Clone, +{ + move |result| f(prefix.clone(), result) +} + +/// Applies the given prefix values to the provided closure in the first +/// argument and returns a new closure that takes its last argument. +/// +/// This is variadic version of [`with()`] which works with any number of +/// arguments. +#[macro_export] +macro_rules! with { + ($f:expr, $($x:expr),+ $(,)?) => { + move |result| $f($($x),+, result) + }; +} diff --git a/examples/download_progress/src/main.rs b/examples/download_progress/src/main.rs index cc7a7800..9b316f52 100644 --- a/examples/download_progress/src/main.rs +++ b/examples/download_progress/src/main.rs @@ -4,7 +4,7 @@ use download::download; use iced::task; use iced::widget::{button, center, column, progress_bar, text, Column}; -use iced::{Center, Element, Right, Task}; +use iced::{with, Center, Element, Right, Task}; pub fn main() -> iced::Result { iced::application( @@ -52,7 +52,7 @@ impl Example { let task = download.start(); - task.map_with(index, Message::DownloadUpdated) + task.map(with(Message::DownloadUpdated, index)) } Message::DownloadUpdated(id, update) => { if let Some(download) = diff --git a/examples/gallery/Cargo.toml b/examples/gallery/Cargo.toml index c9dc1e9d..6e8aba06 100644 --- a/examples/gallery/Cargo.toml +++ b/examples/gallery/Cargo.toml @@ -17,6 +17,7 @@ serde.features = ["derive"] bytes.workspace = true image.workspace = true +sipper.workspace = true tokio.workspace = true blurhash = "0.2.3" diff --git a/examples/gallery/src/civitai.rs b/examples/gallery/src/civitai.rs index 18d2a040..04589030 100644 --- a/examples/gallery/src/civitai.rs +++ b/examples/gallery/src/civitai.rs @@ -1,5 +1,6 @@ use bytes::Bytes; use serde::Deserialize; +use sipper::{sipper, Straw}; use tokio::task; use std::fmt; @@ -45,58 +46,72 @@ impl Image { self, width: u32, height: u32, - ) -> Result { + ) -> Result { task::spawn_blocking(move || { let pixels = blurhash::decode(&self.hash, width, height, 1.0)?; - Ok::<_, Error>(Rgba { - width, - height, - pixels: Bytes::from(pixels), + Ok::<_, Error>(Blurhash { + rgba: Rgba { + width, + height, + pixels: Bytes::from(pixels), + }, }) }) .await? } - pub async fn download(self, size: Size) -> Result { - let client = reqwest::Client::new(); - - let bytes = client - .get(match size { - Size::Original => self.url, - Size::Thumbnail { width } => self - .url - .split("/") - .map(|part| { - if part.starts_with("width=") { - format!("width={}", width * 2) // High DPI - } else { - part.to_owned() - } - }) - .collect::>() - .join("/"), + pub fn download(self, size: Size) -> impl Straw { + sipper(move |mut sender| async move { + let client = reqwest::Client::new(); + + if let Size::Thumbnail { width, height } = size { + let image = self.clone(); + + drop(task::spawn(async move { + if let Ok(blurhash) = image.blurhash(width, height).await { + sender.send(blurhash).await; + } + })); + } + + let bytes = client + .get(match size { + Size::Original => self.url, + Size::Thumbnail { width, .. } => self + .url + .split("/") + .map(|part| { + if part.starts_with("width=") { + format!("width={}", width * 2) // High DPI + } else { + part.to_owned() + } + }) + .collect::>() + .join("/"), + }) + .send() + .await? + .error_for_status()? + .bytes() + .await?; + + let image = task::spawn_blocking(move || { + Ok::<_, Error>( + image::ImageReader::new(io::Cursor::new(bytes)) + .with_guessed_format()? + .decode()? + .to_rgba8(), + ) }) - .send() - .await? - .error_for_status()? - .bytes() - .await?; + .await??; - let image = task::spawn_blocking(move || { - Ok::<_, Error>( - image::ImageReader::new(io::Cursor::new(bytes)) - .with_guessed_format()? - .decode()? - .to_rgba8(), - ) - }) - .await??; - - Ok(Rgba { - width: image.width(), - height: image.height(), - pixels: Bytes::from(image.into_raw()), + Ok(Rgba { + width: image.width(), + height: image.height(), + pixels: Bytes::from(image.into_raw()), + }) }) } } @@ -106,6 +121,11 @@ impl Image { )] pub struct Id(u32); +#[derive(Debug, Clone)] +pub struct Blurhash { + pub rgba: Rgba, +} + #[derive(Clone)] pub struct Rgba { pub width: u32, @@ -125,7 +145,7 @@ impl fmt::Debug for Rgba { #[derive(Debug, Clone, Copy)] pub enum Size { Original, - Thumbnail { width: u32 }, + Thumbnail { width: u32, height: u32 }, } #[derive(Debug, Clone)] diff --git a/examples/gallery/src/main.rs b/examples/gallery/src/main.rs index 441ad924..0ed2a862 100644 --- a/examples/gallery/src/main.rs +++ b/examples/gallery/src/main.rs @@ -14,7 +14,8 @@ use iced::widget::{ }; use iced::window; use iced::{ - color, Animation, ContentFit, Element, Fill, Subscription, Task, Theme, + color, with, Animation, ContentFit, Element, Fill, Subscription, Task, + Theme, }; use std::collections::HashMap; @@ -40,7 +41,7 @@ enum Message { ImageDownloaded(Result), ThumbnailDownloaded(Id, Result), ThumbnailHovered(Id, bool), - BlurhashDecoded(Id, Result), + BlurhashDecoded(Id, civitai::Blurhash), Open(Id), Close, Animate(Instant), @@ -94,16 +95,14 @@ impl Gallery { return Task::none(); }; - Task::batch([ - Task::future( - image.clone().blurhash(Preview::WIDTH, Preview::HEIGHT), - ) - .map_with(id, Message::BlurhashDecoded), - Task::future(image.download(Size::Thumbnail { + Task::sip( + image.download(Size::Thumbnail { width: Preview::WIDTH, - })) - .map_with(id, Message::ThumbnailDownloaded), - ]) + height: Preview::HEIGHT, + }), + with(Message::BlurhashDecoded, id), + with(Message::ThumbnailDownloaded, id), + ) } Message::ImageDownloaded(Ok(rgba)) => { self.viewer.show(rgba); @@ -129,9 +128,11 @@ impl Gallery { Task::none() } - Message::BlurhashDecoded(id, Ok(rgba)) => { + Message::BlurhashDecoded(id, blurhash) => { if !self.previews.contains_key(&id) { - let _ = self.previews.insert(id, Preview::loading(rgba)); + let _ = self + .previews + .insert(id, Preview::loading(blurhash.rgba)); } Task::none() @@ -165,8 +166,7 @@ impl Gallery { } Message::ImagesListed(Err(error)) | Message::ImageDownloaded(Err(error)) - | Message::ThumbnailDownloaded(_, Err(error)) - | Message::BlurhashDecoded(_, Err(error)) => { + | Message::ThumbnailDownloaded(_, Err(error)) => { dbg!(error); Task::none() diff --git a/runtime/src/task.rs b/runtime/src/task.rs index 1a1ef699..022483f7 100644 --- a/runtime/src/task.rs +++ b/runtime/src/task.rs @@ -63,7 +63,7 @@ impl Task { /// progress with the first closure and the output with the second one. pub fn sip( sipper: S, - on_progress: impl Fn(S::Progress) -> T + MaybeSend + 'static, + on_progress: impl FnMut(S::Progress) -> T + MaybeSend + 'static, on_output: impl FnOnce(::Output) -> T + MaybeSend + 'static, ) -> Self where @@ -98,50 +98,6 @@ impl Task { self.then(move |output| Task::done(f(output))) } - /// Combines a prefix value with the result of the [`Task`] using - /// the provided closure. - /// - /// Sometimes you will want to identify the source or target - /// of some [`Task`] in your UI. This can be achieved through - /// normal means by using [`map`]: - /// - /// ```rust - /// # use iced_runtime::Task; - /// # let task = Task::none(); - /// # enum Message { TaskCompleted(u32, ()) } - /// let id = 123; - /// - /// # let _ = { - /// task.map(move |result| Message::TaskCompleted(id, result)) - /// # }; - /// ``` - /// - /// Quite a mouthful. [`map_with`] lets you write: - /// - /// ```rust - /// # use iced_runtime::Task; - /// # let task = Task::none(); - /// # enum Message { TaskCompleted(u32, ()) } - /// # let id = 123; - /// # let _ = { - /// task.map_with(id, Message::TaskCompleted) - /// # }; - /// ``` - /// - /// Much nicer! - pub fn map_with( - self, - prefix: P, - mut f: impl FnMut(P, T) -> O + MaybeSend + 'static, - ) -> Task - where - T: MaybeSend + 'static, - P: MaybeSend + Clone + 'static, - O: MaybeSend + 'static, - { - self.map(move |result| f(prefix.clone(), result)) - } - /// Performs a new [`Task`] for every output of the current [`Task`] using the /// given closure. /// diff --git a/src/lib.rs b/src/lib.rs index e4649938..ce4a1e9d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -505,7 +505,7 @@ pub use crate::core::gradient; pub use crate::core::padding; pub use crate::core::theme; pub use crate::core::{ - never, Alignment, Animation, Background, Border, Color, ContentFit, + never, with, Alignment, Animation, Background, Border, Color, ContentFit, Degrees, Gradient, Length, Padding, Pixels, Point, Radians, Rectangle, Rotation, Settings, Shadow, Size, Theme, Transformation, Vector, }; -- cgit From d2d93d09166a013924deda9f03e92887651cdbf7 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 11 Feb 2025 03:42:44 +0100 Subject: Fix typo in `with!` documentation --- core/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/lib.rs b/core/src/lib.rs index ac0a228f..a6d4edc7 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -145,7 +145,7 @@ where /// Applies the given prefix values to the provided closure in the first /// argument and returns a new closure that takes its last argument. /// -/// This is variadic version of [`with()`] which works with any number of +/// This is a variadic version of [`with()`] which works with any number of /// arguments. #[macro_export] macro_rules! with { -- cgit From 080db348495f6e92f13391d3b222c5a2769b5fc1 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 11 Feb 2025 03:48:49 +0100 Subject: Make `with` take a `Copy` prefix Use the `with!` macro otherwise! --- core/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/lib.rs b/core/src/lib.rs index a6d4edc7..a5540787 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -137,9 +137,9 @@ pub fn with( prefix: T, ) -> impl FnMut(R) -> O where - T: Clone, + T: Copy, { - move |result| f(prefix.clone(), result) + move |result| f(prefix, result) } /// Applies the given prefix values to the provided closure in the first -- cgit From eab723866e1dc94ebd6d7c5c0c3ef191c80bcf59 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 11 Feb 2025 10:36:45 +0100 Subject: Replace `with` function with `Function` trait --- core/src/lib.rs | 107 ++++++++++++++++----------------- examples/download_progress/src/main.rs | 4 +- examples/gallery/src/main.rs | 6 +- examples/game_of_life/src/main.rs | 14 ++--- examples/multi_window/src/main.rs | 8 ++- examples/todos/src/main.rs | 9 +-- src/lib.rs | 6 +- 7 files changed, 75 insertions(+), 79 deletions(-) diff --git a/core/src/lib.rs b/core/src/lib.rs index a5540787..cd05cb25 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -94,62 +94,59 @@ pub fn never(never: std::convert::Infallible) -> T { match never {} } -/// Applies the given prefix value to the provided closure and returns -/// a new closure that takes the other argument. +/// A trait extension for binary functions (`Fn(A, B) -> O`). /// -/// This lets you partially "apply" a function—equivalent to currying, -/// but it only works with binary functions. If you want to apply an -/// arbitrary number of arguments, use the [`with!`] macro instead. -/// -/// # When is this useful? -/// Sometimes you will want to identify the source or target -/// of some message in your user interface. This can be achieved through -/// normal means by defining a closure and moving the identifier -/// inside: -/// -/// ```rust -/// # let element: Option<()> = Some(()); -/// # enum Message { ButtonPressed(u32, ()) } -/// let id = 123; -/// -/// # let _ = { -/// element.map(move |result| Message::ButtonPressed(id, result)) -/// # }; -/// ``` -/// -/// That's quite a mouthful. [`with()`] lets you write: -/// -/// ```rust -/// # use iced_core::with; -/// # let element: Option<()> = Some(()); -/// # enum Message { ButtonPressed(u32, ()) } -/// let id = 123; -/// -/// # let _ = { -/// element.map(with(Message::ButtonPressed, id)) -/// # }; -/// ``` -/// -/// Effectively creating the same closure that partially applies -/// the `id` to the message—but much more concise! -pub fn with( - mut f: impl FnMut(T, R) -> O, - prefix: T, -) -> impl FnMut(R) -> O -where - T: Copy, -{ - move |result| f(prefix, result) +/// It enables you to use a bunch of nifty functional programming paradigms +/// that work well with iced. +pub trait Function { + /// Applies the given first argument to a binary function and returns + /// a new function that takes the other argument. + /// + /// This lets you partially "apply" a function—equivalent to currying, + /// but it only works with binary functions. If you want to apply an + /// arbitrary number of arguments, create a little struct for them. + /// + /// # When is this useful? + /// Sometimes you will want to identify the source or target + /// of some message in your user interface. This can be achieved through + /// normal means by defining a closure and moving the identifier + /// inside: + /// + /// ```rust + /// # let element: Option<()> = Some(()); + /// # enum Message { ButtonPressed(u32, ()) } + /// let id = 123; + /// + /// # let _ = { + /// element.map(move |result| Message::ButtonPressed(id, result)) + /// # }; + /// ``` + /// + /// That's quite a mouthful. [`with`] lets you write: + /// + /// ```rust + /// # use iced_core::Function; + /// # let element: Option<()> = Some(()); + /// # enum Message { ButtonPressed(u32, ()) } + /// let id = 123; + /// + /// # let _ = { + /// element.map(Message::ButtonPressed.with(id)) + /// # }; + /// ``` + /// + /// Effectively creating the same closure that partially applies + /// the `id` to the message—but much more concise! + fn with(self, prefix: A) -> impl Fn(B) -> O; } -/// Applies the given prefix values to the provided closure in the first -/// argument and returns a new closure that takes its last argument. -/// -/// This is a variadic version of [`with()`] which works with any number of -/// arguments. -#[macro_export] -macro_rules! with { - ($f:expr, $($x:expr),+ $(,)?) => { - move |result| $f($($x),+, result) - }; +impl Function for F +where + F: Fn(A, B) -> O, + Self: Sized, + A: Copy, +{ + fn with(self, prefix: A) -> impl Fn(B) -> O { + move |result| self(prefix, result) + } } diff --git a/examples/download_progress/src/main.rs b/examples/download_progress/src/main.rs index 9b316f52..8082eccd 100644 --- a/examples/download_progress/src/main.rs +++ b/examples/download_progress/src/main.rs @@ -4,7 +4,7 @@ use download::download; use iced::task; use iced::widget::{button, center, column, progress_bar, text, Column}; -use iced::{with, Center, Element, Right, Task}; +use iced::{Center, Element, Function, Right, Task}; pub fn main() -> iced::Result { iced::application( @@ -52,7 +52,7 @@ impl Example { let task = download.start(); - task.map(with(Message::DownloadUpdated, index)) + task.map(Message::DownloadUpdated.with(index)) } Message::DownloadUpdated(id, update) => { if let Some(download) = diff --git a/examples/gallery/src/main.rs b/examples/gallery/src/main.rs index 0ed2a862..abafaf2d 100644 --- a/examples/gallery/src/main.rs +++ b/examples/gallery/src/main.rs @@ -14,7 +14,7 @@ use iced::widget::{ }; use iced::window; use iced::{ - color, with, Animation, ContentFit, Element, Fill, Subscription, Task, + color, Animation, ContentFit, Element, Fill, Function, Subscription, Task, Theme, }; @@ -100,8 +100,8 @@ impl Gallery { width: Preview::WIDTH, height: Preview::HEIGHT, }), - with(Message::BlurhashDecoded, id), - with(Message::ThumbnailDownloaded, id), + Message::BlurhashDecoded.with(id), + Message::ThumbnailDownloaded.with(id), ) } Message::ImageDownloaded(Ok(rgba)) => { diff --git a/examples/game_of_life/src/main.rs b/examples/game_of_life/src/main.rs index dec3df7f..9516f832 100644 --- a/examples/game_of_life/src/main.rs +++ b/examples/game_of_life/src/main.rs @@ -9,7 +9,7 @@ use iced::time::{self, milliseconds}; use iced::widget::{ button, checkbox, column, container, pick_list, row, slider, text, }; -use iced::{Center, Element, Fill, Subscription, Task, Theme}; +use iced::{Center, Element, Fill, Function, Subscription, Task, Theme}; pub fn main() -> iced::Result { tracing_subscriber::fmt::init(); @@ -37,7 +37,7 @@ struct GameOfLife { #[derive(Debug, Clone)] enum Message { - Grid(grid::Message, usize), + Grid(usize, grid::Message), Tick, TogglePlayback, ToggleGrid(bool), @@ -61,7 +61,7 @@ impl GameOfLife { fn update(&mut self, message: Message) -> Task { match message { - Message::Grid(message, version) => { + Message::Grid(version, message) => { if version == self.version { self.grid.update(message); } @@ -78,9 +78,7 @@ impl GameOfLife { let version = self.version; - return Task::perform(task, move |message| { - Message::Grid(message, version) - }); + return Task::perform(task, Message::Grid.with(version)); } } Message::TogglePlayback => { @@ -129,9 +127,7 @@ impl GameOfLife { ); let content = column![ - self.grid - .view() - .map(move |message| Message::Grid(message, version)), + self.grid.view().map(Message::Grid.with(version)), controls, ] .height(Fill); diff --git a/examples/multi_window/src/main.rs b/examples/multi_window/src/main.rs index f9021c8d..8cec9d4c 100644 --- a/examples/multi_window/src/main.rs +++ b/examples/multi_window/src/main.rs @@ -3,7 +3,9 @@ use iced::widget::{ text_input, }; use iced::window; -use iced::{Center, Element, Fill, Subscription, Task, Theme, Vector}; +use iced::{ + Center, Element, Fill, Function, Subscription, Task, Theme, Vector, +}; use std::collections::BTreeMap; @@ -169,7 +171,7 @@ impl Window { let scale_input = column![ text("Window scale factor:"), text_input("Window Scale", &self.scale_input) - .on_input(move |msg| { Message::ScaleInputChanged(id, msg) }) + .on_input(Message::ScaleInputChanged.with(id)) .on_submit(Message::ScaleChanged( id, self.scale_input.to_string() @@ -179,7 +181,7 @@ impl Window { let title_input = column![ text("Window title:"), text_input("Window Title", &self.title) - .on_input(move |msg| { Message::TitleChanged(id, msg) }) + .on_input(Message::TitleChanged.with(id)) .id(format!("input-{id}")) ]; diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs index 7faf742e..dfb73d96 100644 --- a/examples/todos/src/main.rs +++ b/examples/todos/src/main.rs @@ -4,7 +4,9 @@ use iced::widget::{ scrollable, text, text_input, Text, }; use iced::window; -use iced::{Center, Element, Fill, Font, Subscription, Task as Command}; +use iced::{ + Center, Element, Fill, Font, Function, Subscription, Task as Command, +}; use serde::{Deserialize, Serialize}; use uuid::Uuid; @@ -215,9 +217,8 @@ impl Todos { .map(|(i, task)| { ( task.id, - task.view(i).map(move |message| { - Message::TaskMessage(i, message) - }), + task.view(i) + .map(Message::TaskMessage.with(i)), ) }), ) diff --git a/src/lib.rs b/src/lib.rs index ce4a1e9d..441826d6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -505,9 +505,9 @@ pub use crate::core::gradient; pub use crate::core::padding; pub use crate::core::theme; pub use crate::core::{ - never, with, Alignment, Animation, Background, Border, Color, ContentFit, - Degrees, Gradient, Length, Padding, Pixels, Point, Radians, Rectangle, - Rotation, Settings, Shadow, Size, Theme, Transformation, Vector, + never, Alignment, Animation, Background, Border, Color, ContentFit, + Degrees, Function, Gradient, Length, Padding, Pixels, Point, Radians, + Rectangle, Rotation, Settings, Shadow, Size, Theme, Transformation, Vector, }; pub use crate::runtime::exit; pub use iced_futures::Subscription; -- cgit From 7d99e4d07e0fa6b60b802b18312b825c9c09fe29 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 11 Feb 2025 20:25:48 +0100 Subject: Use `Function` trait in `Element::map` example --- core/src/element.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/src/element.rs b/core/src/element.rs index ede9e16c..b7d51aeb 100644 --- a/core/src/element.rs +++ b/core/src/element.rs @@ -93,6 +93,7 @@ impl<'a, Message, Theme, Renderer> Element<'a, Message, Theme, Renderer> { /// /// ```no_run /// # mod iced { + /// # pub use iced_core::Function; /// # pub type Element<'a, Message> = iced_core::Element<'a, Message, iced_core::Theme, ()>; /// # /// # pub mod widget { @@ -119,7 +120,7 @@ impl<'a, Message, Theme, Renderer> Element<'a, Message, Theme, Renderer> { /// use counter::Counter; /// /// use iced::widget::row; - /// use iced::Element; + /// use iced::{Element, Function}; /// /// struct ManyCounters { /// counters: Vec, @@ -142,7 +143,7 @@ impl<'a, Message, Theme, Renderer> Element<'a, Message, Theme, Renderer> { /// // Here we turn our `Element` into /// // an `Element` by combining the `index` and the /// // message of the `element`. - /// counter.map(move |message| Message::Counter(index, message)) + /// counter.map(Message::Counter.with(index)) /// }), /// ) /// .into() -- cgit From 6f48671042088a9cbde701f83e58667025349307 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 11 Feb 2025 20:28:19 +0100 Subject: Fix broken link in `Function` documentation --- core/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/lib.rs b/core/src/lib.rs index cd05cb25..03cc0632 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -122,7 +122,7 @@ pub trait Function { /// # }; /// ``` /// - /// That's quite a mouthful. [`with`] lets you write: + /// That's quite a mouthful. [`with`](Self::with) lets you write: /// /// ```rust /// # use iced_core::Function; -- cgit From e78c757cad5619c77a588054b42e9b87335d6e86 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 11 Feb 2025 23:18:16 +0100 Subject: Use short version notation for `sipper` --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index e990cf59..abdb2f80 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -172,7 +172,7 @@ raw-window-handle = "0.6" resvg = "0.42" rustc-hash = "2.0" sha2 = "0.10" -sipper = "0.1.0" +sipper = "0.1" smol = "1.0" smol_str = "0.2" softbuffer = "0.4" -- cgit