Use Task chaining to simplify multi_window example

This commit is contained in:
Héctor Ramón Jiménez 2024-06-14 03:04:51 +02:00
parent b21e4567dc
commit 88b9384402
No known key found for this signature in database
GPG key ID: 7CC46565708259A7
4 changed files with 107 additions and 96 deletions

View file

@ -1,16 +1,15 @@
use iced::event;
use iced::executor; use iced::executor;
use iced::multi_window::{self, Application}; use iced::multi_window::{self, Application};
use iced::widget::{ use iced::widget::{
button, center, column, container, scrollable, text, text_input, button, center, column, container, horizontal_space, scrollable, text,
text_input,
}; };
use iced::window; use iced::window;
use iced::{ use iced::{
Alignment, Element, Length, Point, Settings, Subscription, Task, Theme, Alignment, Element, Length, Settings, Subscription, Task, Theme, Vector,
Vector,
}; };
use std::collections::HashMap; use std::collections::BTreeMap;
fn main() -> iced::Result { fn main() -> iced::Result {
Example::run(Settings::default()) Example::run(Settings::default())
@ -18,8 +17,7 @@ fn main() -> iced::Result {
#[derive(Default)] #[derive(Default)]
struct Example { struct Example {
windows: HashMap<window::Id, Window>, windows: BTreeMap<window::Id, Window>,
next_window_pos: window::Position,
} }
#[derive(Debug)] #[derive(Debug)]
@ -33,13 +31,12 @@ struct Window {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
enum Message { enum Message {
OpenWindow,
WindowOpened(window::Id),
WindowClosed(window::Id),
ScaleInputChanged(window::Id, String), ScaleInputChanged(window::Id, String),
ScaleChanged(window::Id, String), ScaleChanged(window::Id, String),
TitleChanged(window::Id, String), TitleChanged(window::Id, String),
CloseWindow(window::Id),
WindowOpened(window::Id, Option<Point>),
WindowClosed(window::Id),
NewWindow,
} }
impl multi_window::Application for Example { impl multi_window::Application for Example {
@ -51,8 +48,7 @@ impl multi_window::Application for Example {
fn new(_flags: ()) -> (Self, Task<Message>) { fn new(_flags: ()) -> (Self, Task<Message>) {
( (
Example { Example {
windows: HashMap::from([(window::Id::MAIN, Window::new(1))]), windows: BTreeMap::from([(window::Id::MAIN, Window::new(1))]),
next_window_pos: window::Position::Default,
}, },
Task::none(), Task::none(),
) )
@ -62,48 +58,36 @@ impl multi_window::Application for Example {
self.windows self.windows
.get(&window) .get(&window)
.map(|window| window.title.clone()) .map(|window| window.title.clone())
.unwrap_or("Example".to_string()) .unwrap_or_default()
} }
fn update(&mut self, message: Message) -> Task<Message> { fn update(&mut self, message: Message) -> Task<Message> {
match message { match message {
Message::ScaleInputChanged(id, scale) => { Message::OpenWindow => {
let window = let Some(last_window) = self.windows.keys().last() else {
self.windows.get_mut(&id).expect("Window not found!"); return Task::none();
window.scale_input = scale; };
Task::none() window::fetch_position(*last_window)
.then(|last_position| {
let position = last_position.map_or(
window::Position::Default,
|last_position| {
window::Position::Specific(
last_position + Vector::new(20.0, 20.0),
)
},
);
window::open(window::Settings {
position,
..window::Settings::default()
})
})
.map(Message::WindowOpened)
} }
Message::ScaleChanged(id, scale) => { Message::WindowOpened(id) => {
let window = self.windows.insert(id, Window::new(self.windows.len() + 1));
self.windows.get_mut(&id).expect("Window not found!");
window.current_scale = scale
.parse::<f64>()
.unwrap_or(window.current_scale)
.clamp(0.5, 5.0);
Task::none()
}
Message::TitleChanged(id, title) => {
let window =
self.windows.get_mut(&id).expect("Window not found.");
window.title = title;
Task::none()
}
Message::CloseWindow(id) => window::close(id),
Message::WindowClosed(id) => {
self.windows.remove(&id);
Task::none()
}
Message::WindowOpened(id, position) => {
if let Some(position) = position {
self.next_window_pos = window::Position::Specific(
position + Vector::new(20.0, 20.0),
);
}
if let Some(window) = self.windows.get(&id) { if let Some(window) = self.windows.get(&id) {
text_input::focus(window.input_id.clone()) text_input::focus(window.input_id.clone())
@ -111,30 +95,52 @@ impl multi_window::Application for Example {
Task::none() Task::none()
} }
} }
Message::NewWindow => { Message::WindowClosed(id) => {
let count = self.windows.len() + 1; self.windows.remove(&id);
let (id, spawn_window) = window::open(window::Settings { Task::none()
position: self.next_window_pos, }
exit_on_close_request: count % 2 == 0, Message::ScaleInputChanged(id, scale) => {
..Default::default() if let Some(window) = self.windows.get_mut(&id) {
}); window.scale_input = scale;
}
self.windows.insert(id, Window::new(count)); Task::none()
}
Message::ScaleChanged(id, scale) => {
if let Some(window) = self.windows.get_mut(&id) {
window.current_scale = scale
.parse::<f64>()
.unwrap_or(window.current_scale)
.clamp(0.5, 5.0);
}
spawn_window Task::none()
}
Message::TitleChanged(id, title) => {
if let Some(window) = self.windows.get_mut(&id) {
window.title = title;
}
Task::none()
} }
} }
} }
fn view(&self, window: window::Id) -> Element<Message> { fn view(&self, window_id: window::Id) -> Element<Message> {
let content = self.windows.get(&window).unwrap().view(window); if let Some(window) = self.windows.get(&window_id) {
center(window.view(window_id)).into()
center(content).into() } else {
horizontal_space().into()
}
} }
fn theme(&self, window: window::Id) -> Self::Theme { fn theme(&self, window: window::Id) -> Theme {
self.windows.get(&window).unwrap().theme.clone() if let Some(window) = self.windows.get(&window) {
window.theme.clone()
} else {
Theme::default()
}
} }
fn scale_factor(&self, window: window::Id) -> f64 { fn scale_factor(&self, window: window::Id) -> f64 {
@ -145,24 +151,7 @@ impl multi_window::Application for Example {
} }
fn subscription(&self) -> Subscription<Self::Message> { fn subscription(&self) -> Subscription<Self::Message> {
event::listen_with(|event, _, window| { window::closings().map(Message::WindowClosed)
if let iced::Event::Window(window_event) = event {
match window_event {
window::Event::CloseRequested => {
Some(Message::CloseWindow(window))
}
window::Event::Opened { position, .. } => {
Some(Message::WindowOpened(window, position))
}
window::Event::Closed => {
Some(Message::WindowClosed(window))
}
_ => None,
}
} else {
None
}
})
} }
} }
@ -200,7 +189,7 @@ impl Window {
]; ];
let new_window_button = let new_window_button =
button(text("New Window")).on_press(Message::NewWindow); button(text("New Window")).on_press(Message::OpenWindow);
let content = scrollable( let content = scrollable(
column![scale_input, title_input, new_window_button] column![scale_input, title_input, new_window_button]

View file

@ -34,7 +34,7 @@ struct Example {
enum Message { enum Message {
Crop, Crop,
Screenshot, Screenshot,
ScreenshotData(Screenshot), Screenshotted(Screenshot),
Png, Png,
PngSaved(Result<String, PngError>), PngSaved(Result<String, PngError>),
XInputChanged(Option<u32>), XInputChanged(Option<u32>),
@ -48,9 +48,9 @@ impl Example {
match message { match message {
Message::Screenshot => { Message::Screenshot => {
return iced::window::screenshot(window::Id::MAIN) return iced::window::screenshot(window::Id::MAIN)
.map(Message::ScreenshotData); .map(Message::Screenshotted);
} }
Message::ScreenshotData(screenshot) => { Message::Screenshotted(screenshot) => {
self.screenshot = Some(screenshot); self.screenshot = Some(screenshot);
} }
Message::Png => { Message::Png => {

View file

@ -21,7 +21,7 @@ use raw_window_handle::WindowHandle;
#[allow(missing_debug_implementations)] #[allow(missing_debug_implementations)]
pub enum Action { pub enum Action {
/// Opens a new window with some [`Settings`]. /// Opens a new window with some [`Settings`].
Open(Id, Settings), Open(Id, Settings, oneshot::Sender<Id>),
/// Close the window and exits the application. /// Close the window and exits the application.
Close(Id), Close(Id),
@ -155,16 +155,36 @@ pub fn frames() -> Subscription<Instant> {
}) })
} }
/// Opens a new window with the given `settings`. /// Subscribes to all window close requests of the running application.
/// pub fn close_requests() -> Subscription<Id> {
/// Returns the new window [`Id`] alongside the [`Task`]. event::listen_with(|event, _status, id| {
pub fn open<T>(settings: Settings) -> (Id, Task<T>) { if let crate::core::Event::Window(Event::CloseRequested) = event {
Some(id)
} else {
None
}
})
}
/// Subscribes to all window closings of the running application.
pub fn closings() -> Subscription<Id> {
event::listen_with(|event, _status, id| {
if let crate::core::Event::Window(Event::Closed) = event {
Some(id)
} else {
None
}
})
}
/// Opens a new window with the given [`Settings`]; producing the [`Id`]
/// of the new window on completion.
pub fn open(settings: Settings) -> Task<Id> {
let id = Id::unique(); let id = Id::unique();
( Task::oneshot(|channel| {
id, crate::Action::Window(Action::Open(id, settings, channel))
Task::effect(crate::Action::Window(Action::Open(id, settings))), })
)
} }
/// Closes the window with `id`. /// Closes the window with `id`.

View file

@ -1030,7 +1030,7 @@ fn run_action<A, C>(
} }
}, },
Action::Window(action) => match action { Action::Window(action) => match action {
window::Action::Open(id, settings) => { window::Action::Open(id, settings, channel) => {
let monitor = window_manager.last_monitor(); let monitor = window_manager.last_monitor();
control_sender control_sender
@ -1041,6 +1041,8 @@ fn run_action<A, C>(
monitor, monitor,
}) })
.expect("Send control action"); .expect("Send control action");
let _ = channel.send(id);
} }
window::Action::Close(id) => { window::Action::Close(id) => {
let _ = window_manager.remove(id); let _ = window_manager.remove(id);