summaryrefslogtreecommitdiffstats
path: root/winit
diff options
context:
space:
mode:
Diffstat (limited to 'winit')
-rw-r--r--winit/README.md12
-rw-r--r--winit/src/application.rs498
-rw-r--r--winit/src/conversion.rs70
-rw-r--r--winit/src/multi_window.rs633
-rw-r--r--winit/src/proxy.rs27
-rw-r--r--winit/src/system.rs12
6 files changed, 584 insertions, 668 deletions
diff --git a/winit/README.md b/winit/README.md
index 91307970..c60e81f9 100644
--- a/winit/README.md
+++ b/winit/README.md
@@ -15,15 +15,3 @@ It exposes a renderer-agnostic `Application` trait that can be implemented and t
[documentation]: https://docs.rs/iced_winit
[`iced_native`]: ../native
[`winit`]: https://github.com/rust-windowing/winit
-
-## Installation
-Add `iced_winit` as a dependency in your `Cargo.toml`:
-
-```toml
-iced_winit = "0.9"
-```
-
-__Iced moves fast and the `master` branch can contain breaking changes!__ If
-you want to learn about a specific release, check out [the release list].
-
-[the release list]: https://github.com/iced-rs/iced/releases
diff --git a/winit/src/application.rs b/winit/src/application.rs
index 3bc29255..a93878ea 100644
--- a/winit/src/application.rs
+++ b/winit/src/application.rs
@@ -12,13 +12,14 @@ use crate::core::widget::operation;
use crate::core::window;
use crate::core::{Color, Event, Point, Size, Theme};
use crate::futures::futures;
-use crate::futures::{Executor, Runtime, Subscription};
+use crate::futures::subscription::{self, Subscription};
+use crate::futures::{Executor, Runtime};
use crate::graphics;
use crate::graphics::compositor::{self, Compositor};
use crate::runtime::clipboard;
use crate::runtime::program::Program;
use crate::runtime::user_interface::{self, UserInterface};
-use crate::runtime::{Command, Debug};
+use crate::runtime::{Action, Debug, Task};
use crate::{Clipboard, Error, Proxy, Settings};
use futures::channel::mpsc;
@@ -35,7 +36,7 @@ use std::sync::Arc;
/// its own window.
///
/// An [`Application`] can execute asynchronous actions by returning a
-/// [`Command`] in some of its methods.
+/// [`Task`] in some of its methods.
///
/// When using an [`Application`] with the `debug` feature enabled, a debug view
/// can be toggled by pressing `F12`.
@@ -51,10 +52,10 @@ where
///
/// Here is where you should return the initial state of your app.
///
- /// Additionally, you can return a [`Command`] if you need to perform some
+ /// Additionally, you can return a [`Task`] if you need to perform some
/// async action in the background on startup. This is useful if you want to
/// load state from a file, perform an initial HTTP request, etc.
- fn new(flags: Self::Flags) -> (Self, Command<Self::Message>);
+ fn new(flags: Self::Flags) -> (Self, Task<Self::Message>);
/// Returns the current title of the [`Application`].
///
@@ -154,19 +155,23 @@ where
let (proxy, worker) = Proxy::new(event_loop.create_proxy());
- let runtime = {
+ let mut runtime = {
let executor = E::new().map_err(Error::ExecutorCreationFailed)?;
executor.spawn(worker);
Runtime::new(executor, proxy.clone())
};
- let (application, init_command) = {
+ let (application, task) = {
let flags = settings.flags;
runtime.enter(|| A::new(flags))
};
+ if let Some(stream) = task.into_stream() {
+ runtime.run(stream);
+ }
+
let id = settings.id;
let title = application.title();
@@ -182,7 +187,6 @@ where
boot_receiver,
event_receiver,
control_sender,
- init_command,
settings.fonts,
));
@@ -192,13 +196,13 @@ where
instance: std::pin::Pin<Box<F>>,
context: task::Context<'static>,
boot: Option<BootConfig<C>>,
- sender: mpsc::UnboundedSender<winit::event::Event<Message>>,
+ sender: mpsc::UnboundedSender<winit::event::Event<Action<Message>>>,
receiver: mpsc::UnboundedReceiver<winit::event_loop::ControlFlow>,
error: Option<Error>,
#[cfg(target_arch = "wasm32")]
is_booted: std::rc::Rc<std::cell::RefCell<bool>>,
#[cfg(target_arch = "wasm32")]
- queued_events: Vec<winit::event::Event<Message>>,
+ queued_events: Vec<winit::event::Event<Action<Message>>>,
}
struct BootConfig<C> {
@@ -228,7 +232,7 @@ where
queued_events: Vec::new(),
};
- impl<Message, F, C> winit::application::ApplicationHandler<Message>
+ impl<Message, F, C> winit::application::ApplicationHandler<Action<Message>>
for Runner<Message, F, C>
where
F: Future<Output = ()>,
@@ -392,11 +396,11 @@ where
fn user_event(
&mut self,
event_loop: &winit::event_loop::ActiveEventLoop,
- message: Message,
+ action: Action<Message>,
) {
self.process_event(
event_loop,
- winit::event::Event::UserEvent(message),
+ winit::event::Event::UserEvent(action),
);
}
@@ -415,7 +419,7 @@ where
fn process_event(
&mut self,
event_loop: &winit::event_loop::ActiveEventLoop,
- event: winit::event::Event<Message>,
+ event: winit::event::Event<Action<Message>>,
) {
// On Wasm, events may start being processed before the compositor
// boots up. We simply queue them and process them once ready.
@@ -479,15 +483,14 @@ struct Boot<C> {
async fn run_instance<A, E, C>(
mut application: A,
- mut runtime: Runtime<E, Proxy<A::Message>, A::Message>,
+ mut runtime: Runtime<E, Proxy<A::Message>, Action<A::Message>>,
mut proxy: Proxy<A::Message>,
mut debug: Debug,
mut boot: oneshot::Receiver<Boot<C>>,
mut event_receiver: mpsc::UnboundedReceiver<
- winit::event::Event<A::Message>,
+ winit::event::Event<Action<A::Message>>,
>,
mut control_sender: mpsc::UnboundedSender<winit::event_loop::ControlFlow>,
- init_command: Command<A::Message>,
fonts: Vec<Cow<'static, [u8]>>,
) where
A: Application + 'static,
@@ -517,7 +520,7 @@ async fn run_instance<A, E, C>(
let physical_size = state.physical_size();
let mut clipboard = Clipboard::connect(&window);
- let mut cache = user_interface::Cache::default();
+ let cache = user_interface::Cache::default();
let mut surface = compositor.create_surface(
window.clone(),
physical_size.width,
@@ -529,22 +532,12 @@ async fn run_instance<A, E, C>(
window.set_visible(true);
}
- run_command(
- &application,
- &mut compositor,
- &mut surface,
- &mut cache,
- &state,
- &mut renderer,
- init_command,
- &mut runtime,
- &mut clipboard,
- &mut should_exit,
- &mut proxy,
- &mut debug,
- &window,
+ runtime.track(
+ application
+ .subscription()
+ .map(Action::Output)
+ .into_recipes(),
);
- runtime.track(application.subscription().into_recipes());
let mut user_interface = ManuallyDrop::new(build_user_interface(
&application,
@@ -574,16 +567,27 @@ async fn run_instance<A, E, C>(
event::Event::PlatformSpecific(event::PlatformSpecific::MacOS(
event::MacOS::ReceivedUrl(url),
)) => {
- use crate::core::event;
-
- events.push(Event::PlatformSpecific(
- event::PlatformSpecific::MacOS(event::MacOS::ReceivedUrl(
- url,
- )),
+ runtime.broadcast(subscription::Event::PlatformSpecific(
+ subscription::PlatformSpecific::MacOS(
+ subscription::MacOS::ReceivedUrl(url),
+ ),
));
}
- event::Event::UserEvent(message) => {
- messages.push(message);
+ event::Event::UserEvent(action) => {
+ run_action(
+ action,
+ &mut user_interface,
+ &mut compositor,
+ &mut surface,
+ &state,
+ &mut renderer,
+ &mut messages,
+ &mut clipboard,
+ &mut should_exit,
+ &mut debug,
+ &window,
+ );
+
user_events += 1;
}
event::Event::WindowEvent {
@@ -623,7 +627,6 @@ async fn run_instance<A, E, C>(
// Then, we can use the `interface_state` here to decide if a redraw
// is needed right away, or simply wait until a specific time.
let redraw_event = Event::Window(
- window::Id::MAIN,
window::Event::RedrawRequested(Instant::now()),
);
@@ -651,7 +654,11 @@ async fn run_instance<A, E, C>(
_ => ControlFlow::Wait,
});
- runtime.broadcast(redraw_event, core::event::Status::Ignored);
+ runtime.broadcast(subscription::Event::Interaction {
+ window: window::Id::MAIN,
+ event: redraw_event,
+ status: core::event::Status::Ignored,
+ });
debug.draw_started();
let new_mouse_interaction = user_interface.draw(
@@ -714,7 +721,6 @@ async fn run_instance<A, E, C>(
state.update(&window, &window_event, &mut debug);
if let Some(event) = conversion::window_event(
- window::Id::MAIN,
window_event,
state.scale_factor(),
state.modifiers(),
@@ -742,7 +748,11 @@ async fn run_instance<A, E, C>(
for (event, status) in
events.drain(..).zip(statuses.into_iter())
{
- runtime.broadcast(event, status);
+ runtime.broadcast(subscription::Event::Interaction {
+ window: window::Id::MAIN,
+ event,
+ status,
+ });
}
if !messages.is_empty()
@@ -751,21 +761,14 @@ async fn run_instance<A, E, C>(
user_interface::State::Outdated
)
{
- let mut cache =
+ let cache =
ManuallyDrop::into_inner(user_interface).into_cache();
// Update application
update(
&mut application,
- &mut compositor,
- &mut surface,
- &mut cache,
&mut state,
- &mut renderer,
&mut runtime,
- &mut clipboard,
- &mut should_exit,
- &mut proxy,
&mut debug,
&mut messages,
&window,
@@ -850,281 +853,230 @@ where
}
/// Updates an [`Application`] by feeding it the provided messages, spawning any
-/// resulting [`Command`], and tracking its [`Subscription`].
-pub fn update<A: Application, C, E: Executor>(
+/// resulting [`Task`], and tracking its [`Subscription`].
+pub fn update<A: Application, E: Executor>(
application: &mut A,
- compositor: &mut C,
- surface: &mut C::Surface,
- cache: &mut user_interface::Cache,
state: &mut State<A>,
- renderer: &mut A::Renderer,
- runtime: &mut Runtime<E, Proxy<A::Message>, A::Message>,
- clipboard: &mut Clipboard,
- should_exit: &mut bool,
- proxy: &mut Proxy<A::Message>,
+ runtime: &mut Runtime<E, Proxy<A::Message>, Action<A::Message>>,
debug: &mut Debug,
messages: &mut Vec<A::Message>,
window: &winit::window::Window,
) where
- C: Compositor<Renderer = A::Renderer> + 'static,
A::Theme: DefaultStyle,
{
for message in messages.drain(..) {
debug.log_message(&message);
debug.update_started();
- let command = runtime.enter(|| application.update(message));
+ let task = runtime.enter(|| application.update(message));
debug.update_finished();
- run_command(
- application,
- compositor,
- surface,
- cache,
- state,
- renderer,
- command,
- runtime,
- clipboard,
- should_exit,
- proxy,
- debug,
- window,
- );
+ if let Some(stream) = task.into_stream() {
+ runtime.run(stream);
+ }
}
state.synchronize(application, window);
let subscription = application.subscription();
- runtime.track(subscription.into_recipes());
+ runtime.track(subscription.map(Action::Output).into_recipes());
}
-/// Runs the actions of a [`Command`].
-pub fn run_command<A, C, E>(
- application: &A,
+/// Runs the actions of a [`Task`].
+pub fn run_action<A, C>(
+ action: Action<A::Message>,
+ user_interface: &mut UserInterface<'_, A::Message, A::Theme, C::Renderer>,
compositor: &mut C,
surface: &mut C::Surface,
- cache: &mut user_interface::Cache,
state: &State<A>,
renderer: &mut A::Renderer,
- command: Command<A::Message>,
- runtime: &mut Runtime<E, Proxy<A::Message>, A::Message>,
+ messages: &mut Vec<A::Message>,
clipboard: &mut Clipboard,
should_exit: &mut bool,
- proxy: &mut Proxy<A::Message>,
debug: &mut Debug,
window: &winit::window::Window,
) where
A: Application,
- E: Executor,
C: Compositor<Renderer = A::Renderer> + 'static,
A::Theme: DefaultStyle,
{
- use crate::runtime::command;
use crate::runtime::system;
use crate::runtime::window;
- for action in command.actions() {
- match action {
- command::Action::Future(future) => {
- runtime.spawn(future);
+ match action {
+ Action::Clipboard(action) => match action {
+ clipboard::Action::Read { target, channel } => {
+ let _ = channel.send(clipboard.read(target));
}
- command::Action::Stream(stream) => {
- runtime.run(stream);
+ clipboard::Action::Write { target, contents } => {
+ clipboard.write(target, contents);
}
- command::Action::Clipboard(action) => match action {
- clipboard::Action::Read(tag, kind) => {
- let message = tag(clipboard.read(kind));
-
- proxy.send(message);
- }
- clipboard::Action::Write(contents, kind) => {
- clipboard.write(kind, contents);
- }
- },
- command::Action::Window(action) => match action {
- window::Action::Close(_id) => {
- *should_exit = true;
- }
- window::Action::Drag(_id) => {
- let _res = window.drag_window();
- }
- window::Action::Spawn { .. } => {
- log::warn!(
- "Spawning a window is only available with \
+ },
+ Action::Window(action) => match action {
+ window::Action::Close(_id) => {
+ *should_exit = true;
+ }
+ window::Action::Drag(_id) => {
+ let _res = window.drag_window();
+ }
+ window::Action::Open { .. } => {
+ log::warn!(
+ "Spawning a window is only available with \
multi-window applications."
- );
- }
- window::Action::Resize(_id, size) => {
- let _ =
- window.request_inner_size(winit::dpi::LogicalSize {
- width: size.width,
- height: size.height,
- });
- }
- window::Action::FetchSize(_id, callback) => {
- let size =
- window.inner_size().to_logical(window.scale_factor());
+ );
+ }
+ window::Action::Resize(_id, size) => {
+ let _ = window.request_inner_size(winit::dpi::LogicalSize {
+ width: size.width,
+ height: size.height,
+ });
+ }
+ window::Action::FetchSize(_id, channel) => {
+ let size =
+ window.inner_size().to_logical(window.scale_factor());
- proxy.send(callback(Size::new(size.width, size.height)));
- }
- window::Action::FetchMaximized(_id, callback) => {
- proxy.send(callback(window.is_maximized()));
- }
- window::Action::Maximize(_id, maximized) => {
- window.set_maximized(maximized);
- }
- window::Action::FetchMinimized(_id, callback) => {
- proxy.send(callback(window.is_minimized()));
- }
- window::Action::Minimize(_id, minimized) => {
- window.set_minimized(minimized);
- }
- window::Action::FetchPosition(_id, callback) => {
- let position = window
- .inner_position()
- .map(|position| {
- let position = position
- .to_logical::<f32>(window.scale_factor());
-
- Point::new(position.x, position.y)
- })
- .ok();
+ let _ = channel.send(Size::new(size.width, size.height));
+ }
+ window::Action::FetchMaximized(_id, channel) => {
+ let _ = channel.send(window.is_maximized());
+ }
+ window::Action::Maximize(_id, maximized) => {
+ window.set_maximized(maximized);
+ }
+ window::Action::FetchMinimized(_id, channel) => {
+ let _ = channel.send(window.is_minimized());
+ }
+ window::Action::Minimize(_id, minimized) => {
+ window.set_minimized(minimized);
+ }
+ window::Action::FetchPosition(_id, channel) => {
+ let position = window
+ .inner_position()
+ .map(|position| {
+ let position =
+ position.to_logical::<f32>(window.scale_factor());
+
+ Point::new(position.x, position.y)
+ })
+ .ok();
+
+ let _ = channel.send(position);
+ }
+ window::Action::Move(_id, position) => {
+ window.set_outer_position(winit::dpi::LogicalPosition {
+ x: position.x,
+ y: position.y,
+ });
+ }
+ window::Action::ChangeMode(_id, mode) => {
+ window.set_visible(conversion::visible(mode));
+ window.set_fullscreen(conversion::fullscreen(
+ window.current_monitor(),
+ mode,
+ ));
+ }
+ window::Action::ChangeIcon(_id, icon) => {
+ window.set_window_icon(conversion::icon(icon));
+ }
+ window::Action::FetchMode(_id, channel) => {
+ let mode = if window.is_visible().unwrap_or(true) {
+ conversion::mode(window.fullscreen())
+ } else {
+ core::window::Mode::Hidden
+ };
- proxy.send(callback(position));
- }
- window::Action::Move(_id, position) => {
- window.set_outer_position(winit::dpi::LogicalPosition {
- x: position.x,
- y: position.y,
+ let _ = channel.send(mode);
+ }
+ window::Action::ToggleMaximize(_id) => {
+ window.set_maximized(!window.is_maximized());
+ }
+ window::Action::ToggleDecorations(_id) => {
+ window.set_decorations(!window.is_decorated());
+ }
+ window::Action::RequestUserAttention(_id, user_attention) => {
+ window.request_user_attention(
+ user_attention.map(conversion::user_attention),
+ );
+ }
+ window::Action::GainFocus(_id) => {
+ window.focus_window();
+ }
+ window::Action::ChangeLevel(_id, level) => {
+ window.set_window_level(conversion::window_level(level));
+ }
+ window::Action::ShowSystemMenu(_id) => {
+ if let mouse::Cursor::Available(point) = state.cursor() {
+ window.show_window_menu(winit::dpi::LogicalPosition {
+ x: point.x,
+ y: point.y,
});
}
- window::Action::ChangeMode(_id, mode) => {
- window.set_visible(conversion::visible(mode));
- window.set_fullscreen(conversion::fullscreen(
- window.current_monitor(),
- mode,
- ));
- }
- window::Action::ChangeIcon(_id, icon) => {
- window.set_window_icon(conversion::icon(icon));
- }
- window::Action::FetchMode(_id, tag) => {
- let mode = if window.is_visible().unwrap_or(true) {
- conversion::mode(window.fullscreen())
- } else {
- core::window::Mode::Hidden
- };
-
- proxy.send(tag(mode));
- }
- window::Action::ToggleMaximize(_id) => {
- window.set_maximized(!window.is_maximized());
- }
- window::Action::ToggleDecorations(_id) => {
- window.set_decorations(!window.is_decorated());
- }
- window::Action::RequestUserAttention(_id, user_attention) => {
- window.request_user_attention(
- user_attention.map(conversion::user_attention),
- );
- }
- window::Action::GainFocus(_id) => {
- window.focus_window();
- }
- window::Action::ChangeLevel(_id, level) => {
- window.set_window_level(conversion::window_level(level));
- }
- window::Action::ShowSystemMenu(_id) => {
- if let mouse::Cursor::Available(point) = state.cursor() {
- window.show_window_menu(winit::dpi::LogicalPosition {
- x: point.x,
- y: point.y,
- });
- }
- }
- window::Action::FetchId(_id, tag) => {
- proxy.send(tag(window.id().into()));
- }
- window::Action::RunWithHandle(_id, tag) => {
- use window::raw_window_handle::HasWindowHandle;
+ }
+ window::Action::FetchRawId(_id, channel) => {
+ let _ = channel.send(window.id().into());
+ }
+ window::Action::RunWithHandle(_id, f) => {
+ use window::raw_window_handle::HasWindowHandle;
- if let Ok(handle) = window.window_handle() {
- proxy.send(tag(&handle));
- }
+ if let Ok(handle) = window.window_handle() {
+ f(handle);
}
+ }
- window::Action::Screenshot(_id, tag) => {
- let bytes = compositor.screenshot(
- renderer,
- surface,
- state.viewport(),
- state.background_color(),
- &debug.overlay(),
- );
-
- proxy.send(tag(window::Screenshot::new(
- bytes,
- state.physical_size(),
- )));
- }
- },
- command::Action::System(action) => match action {
- system::Action::QueryInformation(_tag) => {
- #[cfg(feature = "system")]
- {
- let graphics_info = compositor.fetch_information();
- let mut proxy = proxy.clone();
-
- let _ = std::thread::spawn(move || {
- let information =
- crate::system::information(graphics_info);
-
- let message = _tag(information);
-
- proxy.send(message);
- });
- }
- }
- },
- command::Action::Widget(action) => {
- let mut current_cache = std::mem::take(cache);
- let mut current_operation = Some(action);
-
- let mut user_interface = build_user_interface(
- application,
- current_cache,
+ window::Action::Screenshot(_id, channel) => {
+ let bytes = compositor.screenshot(
renderer,
- state.logical_size(),
- debug,
+ surface,
+ state.viewport(),
+ state.background_color(),
+ &debug.overlay(),
);
- while let Some(mut operation) = current_operation.take() {
- user_interface.operate(renderer, operation.as_mut());
+ let _ = channel.send(window::Screenshot::new(
+ bytes,
+ state.physical_size(),
+ state.viewport().scale_factor(),
+ ));
+ }
+ },
+ Action::System(action) => match action {
+ system::Action::QueryInformation(_channel) => {
+ #[cfg(feature = "system")]
+ {
+ let graphics_info = compositor.fetch_information();
- match operation.finish() {
- operation::Outcome::None => {}
- operation::Outcome::Some(message) => {
- proxy.send(message);
- }
- operation::Outcome::Chain(next) => {
- current_operation = Some(next);
- }
+ let _ = std::thread::spawn(move || {
+ let information =
+ crate::system::information(graphics_info);
+
+ let _ = _channel.send(information);
+ });
+ }
+ }
+ },
+ Action::Widget(operation) => {
+ let mut current_operation = Some(operation);
+
+ while let Some(mut operation) = current_operation.take() {
+ user_interface.operate(renderer, operation.as_mut());
+
+ match operation.finish() {
+ operation::Outcome::None => {}
+ operation::Outcome::Some(()) => {}
+ operation::Outcome::Chain(next) => {
+ current_operation = Some(next);
}
}
-
- current_cache = user_interface.into_cache();
- *cache = current_cache;
}
- command::Action::LoadFont { bytes, tagger } => {
- // TODO: Error handling (?)
- compositor.load_font(bytes);
+ }
+ Action::LoadFont { bytes, channel } => {
+ // TODO: Error handling (?)
+ compositor.load_font(bytes);
- proxy.send(tagger(Ok(())));
- }
- command::Action::Custom(_) => {
- log::warn!("Unsupported custom action in `iced_winit` shell");
- }
+ let _ = channel.send(Ok(()));
+ }
+ Action::Output(message) => {
+ messages.push(message);
}
}
}
diff --git a/winit/src/conversion.rs b/winit/src/conversion.rs
index d04fc2f1..0ed10c88 100644
--- a/winit/src/conversion.rs
+++ b/winit/src/conversion.rs
@@ -73,11 +73,7 @@ pub fn window_attributes(
#[cfg(target_os = "windows")]
{
use winit::platform::windows::WindowAttributesExtWindows;
- #[allow(unsafe_code)]
- unsafe {
- attributes = attributes
- .with_parent_window(settings.platform_specific.parent);
- }
+
attributes = attributes
.with_drag_and_drop(settings.platform_specific.drag_and_drop);
@@ -126,7 +122,6 @@ pub fn window_attributes(
/// Converts a winit window event into an iced event.
pub fn window_event(
- id: window::Id,
event: winit::event::WindowEvent,
scale_factor: f64,
modifiers: winit::keyboard::ModifiersState,
@@ -137,16 +132,13 @@ pub fn window_event(
WindowEvent::Resized(new_size) => {
let logical_size = new_size.to_logical(scale_factor);
- Some(Event::Window(
- id,
- window::Event::Resized {
- width: logical_size.width,
- height: logical_size.height,
- },
- ))
+ Some(Event::Window(window::Event::Resized {
+ width: logical_size.width,
+ height: logical_size.height,
+ }))
}
WindowEvent::CloseRequested => {
- Some(Event::Window(id, window::Event::CloseRequested))
+ Some(Event::Window(window::Event::CloseRequested))
}
WindowEvent::CursorMoved { position, .. } => {
let position = position.to_logical::<f64>(scale_factor);
@@ -264,22 +256,19 @@ pub fn window_event(
self::modifiers(new_modifiers.state()),
)))
}
- WindowEvent::Focused(focused) => Some(Event::Window(
- id,
- if focused {
- window::Event::Focused
- } else {
- window::Event::Unfocused
- },
- )),
+ WindowEvent::Focused(focused) => Some(Event::Window(if focused {
+ window::Event::Focused
+ } else {
+ window::Event::Unfocused
+ })),
WindowEvent::HoveredFile(path) => {
- Some(Event::Window(id, window::Event::FileHovered(path.clone())))
+ Some(Event::Window(window::Event::FileHovered(path.clone())))
}
WindowEvent::DroppedFile(path) => {
- Some(Event::Window(id, window::Event::FileDropped(path.clone())))
+ Some(Event::Window(window::Event::FileDropped(path.clone())))
}
WindowEvent::HoveredFileCancelled => {
- Some(Event::Window(id, window::Event::FilesHoveredLeft))
+ Some(Event::Window(window::Event::FilesHoveredLeft))
}
WindowEvent::Touch(touch) => {
Some(Event::Touch(touch_event(touch, scale_factor)))
@@ -288,7 +277,7 @@ pub fn window_event(
let winit::dpi::LogicalPosition { x, y } =
position.to_logical(scale_factor);
- Some(Event::Window(id, window::Event::Moved { x, y }))
+ Some(Event::Window(window::Event::Moved { x, y }))
}
_ => None,
}
@@ -323,6 +312,35 @@ pub fn position(
y: f64::from(position.y),
}))
}
+ window::Position::SpecificWith(to_position) => {
+ if let Some(monitor) = monitor {
+ let start = monitor.position();
+
+ let resolution: winit::dpi::LogicalSize<f32> =
+ monitor.size().to_logical(monitor.scale_factor());
+
+ let position = to_position(
+ size,
+ Size::new(resolution.width, resolution.height),
+ );
+
+ let centered: winit::dpi::PhysicalPosition<i32> =
+ winit::dpi::LogicalPosition {
+ x: position.x,
+ y: position.y,
+ }
+ .to_physical(monitor.scale_factor());
+
+ Some(winit::dpi::Position::Physical(
+ winit::dpi::PhysicalPosition {
+ x: start.x + centered.x,
+ y: start.y + centered.y,
+ },
+ ))
+ } else {
+ None
+ }
+ }
window::Position::Centered => {
if let Some(monitor) = monitor {
let start = monitor.position();
diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs
index 673a6f30..8bd8a64d 100644
--- a/winit/src/multi_window.rs
+++ b/winit/src/multi_window.rs
@@ -16,14 +16,15 @@ use crate::futures::futures::channel::oneshot;
use crate::futures::futures::executor;
use crate::futures::futures::task;
use crate::futures::futures::{Future, StreamExt};
-use crate::futures::{Executor, Runtime, Subscription};
+use crate::futures::subscription::{self, Subscription};
+use crate::futures::{Executor, Runtime};
use crate::graphics;
use crate::graphics::{compositor, Compositor};
use crate::multi_window::window_manager::WindowManager;
-use crate::runtime::command::{self, Command};
use crate::runtime::multi_window::Program;
use crate::runtime::user_interface::{self, UserInterface};
use crate::runtime::Debug;
+use crate::runtime::{Action, Task};
use crate::{Clipboard, Error, Proxy, Settings};
pub use crate::application::{default, Appearance, DefaultStyle};
@@ -40,7 +41,7 @@ use std::time::Instant;
/// its own window.
///
/// An [`Application`] can execute asynchronous actions by returning a
-/// [`Command`] in some of its methods.
+/// [`Task`] in some of its methods.
///
/// When using an [`Application`] with the `debug` feature enabled, a debug view
/// can be toggled by pressing `F12`.
@@ -56,10 +57,10 @@ where
///
/// Here is where you should return the initial state of your app.
///
- /// Additionally, you can return a [`Command`] if you need to perform some
+ /// Additionally, you can return a [`Task`] if you need to perform some
/// async action in the background on startup. This is useful if you want to
/// load state from a file, perform an initial HTTP request, etc.
- fn new(flags: Self::Flags) -> (Self, Command<Self::Message>);
+ fn new(flags: Self::Flags) -> (Self, Task<Self::Message>);
/// Returns the current title of the [`Application`].
///
@@ -126,19 +127,23 @@ where
let (proxy, worker) = Proxy::new(event_loop.create_proxy());
- let runtime = {
+ let mut runtime = {
let executor = E::new().map_err(Error::ExecutorCreationFailed)?;
executor.spawn(worker);
Runtime::new(executor, proxy.clone())
};
- let (application, init_command) = {
+ let (application, task) = {
let flags = settings.flags;
runtime.enter(|| A::new(flags))
};
+ if let Some(stream) = task.into_stream() {
+ runtime.run(stream);
+ }
+
let id = settings.id;
let title = application.title(window::Id::MAIN);
@@ -154,7 +159,6 @@ where
boot_receiver,
event_receiver,
control_sender,
- init_command,
));
let context = task::Context::from_waker(task::noop_waker_ref());
@@ -447,13 +451,12 @@ enum Control {
async fn run_instance<A, E, C>(
mut application: A,
- mut runtime: Runtime<E, Proxy<A::Message>, A::Message>,
+ mut runtime: Runtime<E, Proxy<A::Message>, Action<A::Message>>,
mut proxy: Proxy<A::Message>,
mut debug: Debug,
mut boot: oneshot::Receiver<Boot<C>>,
- mut event_receiver: mpsc::UnboundedReceiver<Event<A::Message>>,
+ mut event_receiver: mpsc::UnboundedReceiver<Event<Action<A::Message>>>,
mut control_sender: mpsc::UnboundedSender<Control>,
- init_command: Command<A::Message>,
) where
A: Application + 'static,
E: Executor + 'static,
@@ -474,7 +477,7 @@ async fn run_instance<A, E, C>(
let _ = window_manager.insert(
window::Id::MAIN,
- main_window.clone(),
+ main_window,
&application,
&mut compositor,
exit_on_close_request,
@@ -491,14 +494,11 @@ async fn run_instance<A, E, C>(
let mut clipboard = Clipboard::connect(&main_window.raw);
let mut events = {
vec![(
- Some(window::Id::MAIN),
- core::Event::Window(
- window::Id::MAIN,
- window::Event::Opened {
- position: main_window.position(),
- size: main_window.size(),
- },
- ),
+ window::Id::MAIN,
+ core::Event::Window(window::Event::Opened {
+ position: main_window.position(),
+ size: main_window.size(),
+ }),
)]
};
@@ -513,21 +513,13 @@ async fn run_instance<A, E, C>(
)]),
));
- run_command(
- &application,
- &mut compositor,
- init_command,
- &mut runtime,
- &mut clipboard,
- &mut control_sender,
- &mut proxy,
- &mut debug,
- &mut window_manager,
- &mut ui_caches,
+ runtime.track(
+ application
+ .subscription()
+ .map(Action::Output)
+ .into_recipes(),
);
- runtime.track(application.subscription().into_recipes());
-
let mut messages = Vec::new();
let mut user_events = 0;
@@ -564,14 +556,11 @@ async fn run_instance<A, E, C>(
let _ = ui_caches.insert(id, user_interface::Cache::default());
events.push((
- Some(id),
- core::Event::Window(
- id,
- window::Event::Opened {
- position: window.position(),
- size: window.size(),
- },
- ),
+ id,
+ core::Event::Window(window::Event::Opened {
+ position: window.position(),
+ size: window.size(),
+ }),
));
}
Event::EventLoopAwakened(event) => {
@@ -591,19 +580,27 @@ async fn run_instance<A, E, C>(
event::MacOS::ReceivedUrl(url),
),
) => {
- use crate::core::event;
-
- events.push((
- None,
- event::Event::PlatformSpecific(
- event::PlatformSpecific::MacOS(
- event::MacOS::ReceivedUrl(url),
+ runtime.broadcast(
+ subscription::Event::PlatformSpecific(
+ subscription::PlatformSpecific::MacOS(
+ subscription::MacOS::ReceivedUrl(url),
),
),
- ));
+ );
}
- event::Event::UserEvent(message) => {
- messages.push(message);
+ event::Event::UserEvent(action) => {
+ run_action(
+ action,
+ &application,
+ &mut compositor,
+ &mut messages,
+ &mut clipboard,
+ &mut control_sender,
+ &mut debug,
+ &mut user_interfaces,
+ &mut window_manager,
+ &mut ui_caches,
+ );
user_events += 1;
}
event::Event::WindowEvent {
@@ -623,7 +620,6 @@ async fn run_instance<A, E, C>(
// Then, we can use the `interface_state` here to decide if a redraw
// is needed right away, or simply wait until a specific time.
let redraw_event = core::Event::Window(
- id,
window::Event::RedrawRequested(Instant::now()),
);
@@ -662,10 +658,11 @@ async fn run_instance<A, E, C>(
window.mouse_interaction = new_mouse_interaction;
}
- runtime.broadcast(
- redraw_event.clone(),
- core::event::Status::Ignored,
- );
+ runtime.broadcast(subscription::Event::Interaction {
+ window: id,
+ event: redraw_event,
+ status: core::event::Status::Ignored,
+ });
let _ = control_sender.start_send(Control::ChangeFlow(
match ui_state {
@@ -801,8 +798,8 @@ async fn run_instance<A, E, C>(
let _ = ui_caches.remove(&id);
events.push((
- None,
- core::Event::Window(id, window::Event::Closed),
+ id,
+ core::Event::Window(window::Event::Closed),
));
if window_manager.is_empty() {
@@ -816,12 +813,11 @@ async fn run_instance<A, E, C>(
);
if let Some(event) = conversion::window_event(
- id,
window_event,
window.state.scale_factor(),
window.state.modifiers(),
) {
- events.push((Some(id), event));
+ events.push((id, event));
}
}
}
@@ -837,8 +833,7 @@ async fn run_instance<A, E, C>(
let mut window_events = vec![];
events.retain(|(window_id, event)| {
- if *window_id == Some(id) || window_id.is_none()
- {
+ if *window_id == id {
window_events.push(event.clone());
false
} else {
@@ -874,15 +869,31 @@ async fn run_instance<A, E, C>(
.into_iter()
.zip(statuses.into_iter())
{
- runtime.broadcast(event, status);
+ runtime.broadcast(
+ subscription::Event::Interaction {
+ window: id,
+ event,
+ status,
+ },
+ );
}
}
+ for (id, event) in events.drain(..) {
+ runtime.broadcast(
+ subscription::Event::Interaction {
+ window: id,
+ event,
+ status: core::event::Status::Ignored,
+ },
+ );
+ }
+
debug.event_processing_finished();
// TODO mw application update returns which window IDs to update
if !messages.is_empty() || uis_stale {
- let mut cached_interfaces: FxHashMap<
+ let cached_interfaces: FxHashMap<
window::Id,
user_interface::Cache,
> = ManuallyDrop::into_inner(user_interfaces)
@@ -893,15 +904,9 @@ async fn run_instance<A, E, C>(
// Update application
update(
&mut application,
- &mut compositor,
&mut runtime,
- &mut clipboard,
- &mut control_sender,
- &mut proxy,
&mut debug,
&mut messages,
- &mut window_manager,
- &mut cached_interfaces,
);
// we must synchronize all window states with application state after an
@@ -965,63 +970,46 @@ where
user_interface
}
-/// Updates a multi-window [`Application`] by feeding it messages, spawning any
-/// resulting [`Command`], and tracking its [`Subscription`].
-fn update<A: Application, C, E: Executor>(
+fn update<A: Application, E: Executor>(
application: &mut A,
- compositor: &mut C,
- runtime: &mut Runtime<E, Proxy<A::Message>, A::Message>,
- clipboard: &mut Clipboard,
- control_sender: &mut mpsc::UnboundedSender<Control>,
- proxy: &mut Proxy<A::Message>,
+ runtime: &mut Runtime<E, Proxy<A::Message>, Action<A::Message>>,
debug: &mut Debug,
messages: &mut Vec<A::Message>,
- window_manager: &mut WindowManager<A, C>,
- ui_caches: &mut FxHashMap<window::Id, user_interface::Cache>,
) where
- C: Compositor<Renderer = A::Renderer> + 'static,
A::Theme: DefaultStyle,
{
for message in messages.drain(..) {
debug.log_message(&message);
debug.update_started();
- let command = runtime.enter(|| application.update(message));
+ let task = runtime.enter(|| application.update(message));
debug.update_finished();
- run_command(
- application,
- compositor,
- command,
- runtime,
- clipboard,
- control_sender,
- proxy,
- debug,
- window_manager,
- ui_caches,
- );
+ if let Some(stream) = task.into_stream() {
+ runtime.run(stream);
+ }
}
let subscription = application.subscription();
- runtime.track(subscription.into_recipes());
+ runtime.track(subscription.map(Action::Output).into_recipes());
}
-/// Runs the actions of a [`Command`].
-fn run_command<A, C, E>(
+fn run_action<A, C>(
+ action: Action<A::Message>,
application: &A,
compositor: &mut C,
- command: Command<A::Message>,
- runtime: &mut Runtime<E, Proxy<A::Message>, A::Message>,
+ messages: &mut Vec<A::Message>,
clipboard: &mut Clipboard,
control_sender: &mut mpsc::UnboundedSender<Control>,
- proxy: &mut Proxy<A::Message>,
debug: &mut Debug,
+ interfaces: &mut FxHashMap<
+ window::Id,
+ UserInterface<'_, A::Message, A::Theme, A::Renderer>,
+ >,
window_manager: &mut WindowManager<A, C>,
ui_caches: &mut FxHashMap<window::Id, user_interface::Cache>,
) where
A: Application,
- E: Executor,
C: Compositor<Renderer = A::Renderer> + 'static,
A::Theme: DefaultStyle,
{
@@ -1029,283 +1017,254 @@ fn run_command<A, C, E>(
use crate::runtime::system;
use crate::runtime::window;
- for action in command.actions() {
- match action {
- command::Action::Future(future) => {
- runtime.spawn(Box::pin(future));
+ match action {
+ Action::Output(message) => {
+ messages.push(message);
+ }
+ Action::Clipboard(action) => match action {
+ clipboard::Action::Read { target, channel } => {
+ let _ = channel.send(clipboard.read(target));
}
- command::Action::Stream(stream) => {
- runtime.run(Box::pin(stream));
+ clipboard::Action::Write { target, contents } => {
+ clipboard.write(target, contents);
}
- command::Action::Clipboard(action) => match action {
- clipboard::Action::Read(tag, kind) => {
- let message = tag(clipboard.read(kind));
+ },
+ Action::Window(action) => match action {
+ window::Action::Open(id, settings, channel) => {
+ let monitor = window_manager.last_monitor();
- proxy.send(message);
- }
- clipboard::Action::Write(contents, kind) => {
- clipboard.write(kind, contents);
- }
- },
- command::Action::Window(action) => match action {
- window::Action::Spawn(id, settings) => {
- let monitor = window_manager.last_monitor();
+ control_sender
+ .start_send(Control::CreateWindow {
+ id,
+ settings,
+ title: application.title(id),
+ monitor,
+ })
+ .expect("Send control action");
+ let _ = channel.send(id);
+ }
+ window::Action::Close(id) => {
+ let _ = window_manager.remove(id);
+ let _ = ui_caches.remove(&id);
+
+ if window_manager.is_empty() {
control_sender
- .start_send(Control::CreateWindow {
- id,
- settings,
- title: application.title(id),
- monitor,
- })
+ .start_send(Control::Exit)
.expect("Send control action");
}
- window::Action::Close(id) => {
- let _ = window_manager.remove(id);
- let _ = ui_caches.remove(&id);
-
- if window_manager.is_empty() {
- control_sender
- .start_send(Control::Exit)
- .expect("Send control action");
- }
- }
- window::Action::Drag(id) => {
- if let Some(window) = window_manager.get_mut(id) {
- let _ = window.raw.drag_window();
- }
+ }
+ window::Action::Drag(id) => {
+ if let Some(window) = window_manager.get_mut(id) {
+ let _ = window.raw.drag_window();
}
- window::Action::Resize(id, size) => {
- if let Some(window) = window_manager.get_mut(id) {
- let _ = window.raw.request_inner_size(
- winit::dpi::LogicalSize {
- width: size.width,
- height: size.height,
- },
- );
- }
+ }
+ window::Action::Resize(id, size) => {
+ if let Some(window) = window_manager.get_mut(id) {
+ let _ = window.raw.request_inner_size(
+ winit::dpi::LogicalSize {
+ width: size.width,
+ height: size.height,
+ },
+ );
}
- window::Action::FetchSize(id, callback) => {
- if let Some(window) = window_manager.get_mut(id) {
- let size = window
- .raw
- .inner_size()
- .to_logical(window.raw.scale_factor());
-
- proxy
- .send(callback(Size::new(size.width, size.height)));
- }
+ }
+ window::Action::FetchSize(id, channel) => {
+ if let Some(window) = window_manager.get_mut(id) {
+ let size = window
+ .raw
+ .inner_size()
+ .to_logical(window.raw.scale_factor());
+
+ let _ = channel.send(Size::new(size.width, size.height));
}
- window::Action::FetchMaximized(id, callback) => {
- if let Some(window) = window_manager.get_mut(id) {
- proxy.send(callback(window.raw.is_maximized()));
- }
+ }
+ window::Action::FetchMaximized(id, channel) => {
+ if let Some(window) = window_manager.get_mut(id) {
+ let _ = channel.send(window.raw.is_maximized());
}
- window::Action::Maximize(id, maximized) => {
- if let Some(window) = window_manager.get_mut(id) {
- window.raw.set_maximized(maximized);
- }
+ }
+ window::Action::Maximize(id, maximized) => {
+ if let Some(window) = window_manager.get_mut(id) {
+ window.raw.set_maximized(maximized);
}
- window::Action::FetchMinimized(id, callback) => {
- if let Some(window) = window_manager.get_mut(id) {
- proxy.send(callback(window.raw.is_minimized()));
- }
+ }
+ window::Action::FetchMinimized(id, channel) => {
+ if let Some(window) = window_manager.get_mut(id) {
+ let _ = channel.send(window.raw.is_minimized());
}
- window::Action::Minimize(id, minimized) => {
- if let Some(window) = window_manager.get_mut(id) {
- window.raw.set_minimized(minimized);
- }
+ }
+ window::Action::Minimize(id, minimized) => {
+ if let Some(window) = window_manager.get_mut(id) {
+ window.raw.set_minimized(minimized);
}
- window::Action::FetchPosition(id, callback) => {
- if let Some(window) = window_manager.get_mut(id) {
- let position = window
- .raw
- .inner_position()
- .map(|position| {
- let position = position.to_logical::<f32>(
- window.raw.scale_factor(),
- );
-
- Point::new(position.x, position.y)
- })
- .ok();
+ }
+ window::Action::FetchPosition(id, channel) => {
+ if let Some(window) = window_manager.get_mut(id) {
+ let position = window
+ .raw
+ .inner_position()
+ .map(|position| {
+ let position = position
+ .to_logical::<f32>(window.raw.scale_factor());
+
+ Point::new(position.x, position.y)
+ })
+ .ok();
- proxy.send(callback(position));
- }
+ let _ = channel.send(position);
}
- window::Action::Move(id, position) => {
- if let Some(window) = window_manager.get_mut(id) {
- window.raw.set_outer_position(
- winit::dpi::LogicalPosition {
- x: position.x,
- y: position.y,
- },
- );
- }
+ }
+ window::Action::Move(id, position) => {
+ if let Some(window) = window_manager.get_mut(id) {
+ window.raw.set_outer_position(
+ winit::dpi::LogicalPosition {
+ x: position.x,
+ y: position.y,
+ },
+ );
}
- window::Action::ChangeMode(id, mode) => {
- if let Some(window) = window_manager.get_mut(id) {
- window.raw.set_visible(conversion::visible(mode));
- window.raw.set_fullscreen(conversion::fullscreen(
- window.raw.current_monitor(),
- mode,
- ));
- }
+ }
+ window::Action::ChangeMode(id, mode) => {
+ if let Some(window) = window_manager.get_mut(id) {
+ window.raw.set_visible(conversion::visible(mode));
+ window.raw.set_fullscreen(conversion::fullscreen(
+ window.raw.current_monitor(),
+ mode,
+ ));
}
- window::Action::ChangeIcon(id, icon) => {
- if let Some(window) = window_manager.get_mut(id) {
- window.raw.set_window_icon(conversion::icon(icon));
- }
+ }
+ window::Action::ChangeIcon(id, icon) => {
+ if let Some(window) = window_manager.get_mut(id) {
+ window.raw.set_window_icon(conversion::icon(icon));
}
- window::Action::FetchMode(id, tag) => {
- if let Some(window) = window_manager.get_mut(id) {
- let mode = if window.raw.is_visible().unwrap_or(true) {
- conversion::mode(window.raw.fullscreen())
- } else {
- core::window::Mode::Hidden
- };
-
- proxy.send(tag(mode));
- }
+ }
+ window::Action::FetchMode(id, channel) => {
+ if let Some(window) = window_manager.get_mut(id) {
+ let mode = if window.raw.is_visible().unwrap_or(true) {
+ conversion::mode(window.raw.fullscreen())
+ } else {
+ core::window::Mode::Hidden
+ };
+
+ let _ = channel.send(mode);
}
- window::Action::ToggleMaximize(id) => {
- if let Some(window) = window_manager.get_mut(id) {
- window.raw.set_maximized(!window.raw.is_maximized());
- }
+ }
+ window::Action::ToggleMaximize(id) => {
+ if let Some(window) = window_manager.get_mut(id) {
+ window.raw.set_maximized(!window.raw.is_maximized());
}
- window::Action::ToggleDecorations(id) => {
- if let Some(window) = window_manager.get_mut(id) {
- window.raw.set_decorations(!window.raw.is_decorated());
- }
+ }
+ window::Action::ToggleDecorations(id) => {
+ if let Some(window) = window_manager.get_mut(id) {
+ window.raw.set_decorations(!window.raw.is_decorated());
}
- window::Action::RequestUserAttention(id, attention_type) => {
- if let Some(window) = window_manager.get_mut(id) {
- window.raw.request_user_attention(
- attention_type.map(conversion::user_attention),
- );
- }
+ }
+ window::Action::RequestUserAttention(id, attention_type) => {
+ if let Some(window) = window_manager.get_mut(id) {
+ window.raw.request_user_attention(
+ attention_type.map(conversion::user_attention),
+ );
}
- window::Action::GainFocus(id) => {
- if let Some(window) = window_manager.get_mut(id) {
- window.raw.focus_window();
- }
+ }
+ window::Action::GainFocus(id) => {
+ if let Some(window) = window_manager.get_mut(id) {
+ window.raw.focus_window();
}
- window::Action::ChangeLevel(id, level) => {
- if let Some(window) = window_manager.get_mut(id) {
- window
- .raw
- .set_window_level(conversion::window_level(level));
- }
+ }
+ window::Action::ChangeLevel(id, level) => {
+ if let Some(window) = window_manager.get_mut(id) {
+ window
+ .raw
+ .set_window_level(conversion::window_level(level));
}
- window::Action::ShowSystemMenu(id) => {
- if let Some(window) = window_manager.get_mut(id) {
- if let mouse::Cursor::Available(point) =
- window.state.cursor()
- {
- window.raw.show_window_menu(
- winit::dpi::LogicalPosition {
- x: point.x,
- y: point.y,
- },
- );
- }
+ }
+ window::Action::ShowSystemMenu(id) => {
+ if let Some(window) = window_manager.get_mut(id) {
+ if let mouse::Cursor::Available(point) =
+ window.state.cursor()
+ {
+ window.raw.show_window_menu(
+ winit::dpi::LogicalPosition {
+ x: point.x,
+ y: point.y,
+ },
+ );
}
}
- window::Action::FetchId(id, tag) => {
- if let Some(window) = window_manager.get_mut(id) {
- proxy.send(tag(window.raw.id().into()));
- }
+ }
+ window::Action::FetchRawId(id, channel) => {
+ if let Some(window) = window_manager.get_mut(id) {
+ let _ = channel.send(window.raw.id().into());
}
- window::Action::RunWithHandle(id, tag) => {
- use window::raw_window_handle::HasWindowHandle;
+ }
+ window::Action::RunWithHandle(id, f) => {
+ use window::raw_window_handle::HasWindowHandle;
- if let Some(handle) = window_manager
- .get_mut(id)
- .and_then(|window| window.raw.window_handle().ok())
- {
- proxy.send(tag(&handle));
- }
+ if let Some(handle) = window_manager
+ .get_mut(id)
+ .and_then(|window| window.raw.window_handle().ok())
+ {
+ f(handle);
}
- window::Action::Screenshot(id, tag) => {
- if let Some(window) = window_manager.get_mut(id) {
- let bytes = compositor.screenshot(
- &mut window.renderer,
- &mut window.surface,
- window.state.viewport(),
- window.state.background_color(),
- &debug.overlay(),
- );
+ }
+ window::Action::Screenshot(id, channel) => {
+ if let Some(window) = window_manager.get_mut(id) {
+ let bytes = compositor.screenshot(
+ &mut window.renderer,
+ &mut window.surface,
+ window.state.viewport(),
+ window.state.background_color(),
+ &debug.overlay(),
+ );
- proxy.send(tag(window::Screenshot::new(
- bytes,
- window.state.physical_size(),
- )));
- }
+ let _ = channel.send(window::Screenshot::new(
+ bytes,
+ window.state.physical_size(),
+ window.state.viewport().scale_factor(),
+ ));
}
- },
- command::Action::System(action) => match action {
- system::Action::QueryInformation(_tag) => {
- #[cfg(feature = "system")]
- {
- let graphics_info = compositor.fetch_information();
- let mut proxy = proxy.clone();
-
- let _ = std::thread::spawn(move || {
- let information =
- crate::system::information(graphics_info);
+ }
+ },
+ Action::System(action) => match action {
+ system::Action::QueryInformation(_channel) => {
+ #[cfg(feature = "system")]
+ {
+ let graphics_info = compositor.fetch_information();
- let message = _tag(information);
+ let _ = std::thread::spawn(move || {
+ let information =
+ crate::system::information(graphics_info);
- proxy.send(message);
- });
+ let _ = _channel.send(information);
+ });
+ }
+ }
+ },
+ Action::Widget(operation) => {
+ let mut current_operation = Some(operation);
+
+ while let Some(mut operation) = current_operation.take() {
+ for (id, ui) in interfaces.iter_mut() {
+ if let Some(window) = window_manager.get_mut(*id) {
+ ui.operate(&window.renderer, operation.as_mut());
}
}
- },
- command::Action::Widget(action) => {
- let mut current_operation = Some(action);
-
- let mut uis = build_user_interfaces(
- application,
- debug,
- window_manager,
- std::mem::take(ui_caches),
- );
-
- 'operate: while let Some(mut operation) =
- current_operation.take()
- {
- for (id, ui) in uis.iter_mut() {
- if let Some(window) = window_manager.get_mut(*id) {
- ui.operate(&window.renderer, operation.as_mut());
- match operation.finish() {
- operation::Outcome::None => {}
- operation::Outcome::Some(message) => {
- proxy.send(message);
-
- // operation completed, don't need to try to operate on rest of UIs
- break 'operate;
- }
- operation::Outcome::Chain(next) => {
- current_operation = Some(next);
- }
- }
- }
+ match operation.finish() {
+ operation::Outcome::None => {}
+ operation::Outcome::Some(()) => {}
+ operation::Outcome::Chain(next) => {
+ current_operation = Some(next);
}
}
-
- *ui_caches =
- uis.drain().map(|(id, ui)| (id, ui.into_cache())).collect();
}
- command::Action::LoadFont { bytes, tagger } => {
- // TODO: Error handling (?)
- compositor.load_font(bytes.clone());
+ }
+ Action::LoadFont { bytes, channel } => {
+ // TODO: Error handling (?)
+ compositor.load_font(bytes.clone());
- proxy.send(tagger(Ok(())));
- }
- command::Action::Custom(_) => {
- log::warn!("Unsupported custom action in `iced_winit` shell");
- }
+ let _ = channel.send(Ok(()));
}
}
}
diff --git a/winit/src/proxy.rs b/winit/src/proxy.rs
index 3edc30ad..0ab61375 100644
--- a/winit/src/proxy.rs
+++ b/winit/src/proxy.rs
@@ -4,17 +4,18 @@ use crate::futures::futures::{
task::{Context, Poll},
Future, Sink, StreamExt,
};
+use crate::runtime::Action;
use std::pin::Pin;
/// An event loop proxy with backpressure that implements `Sink`.
#[derive(Debug)]
-pub struct Proxy<Message: 'static> {
- raw: winit::event_loop::EventLoopProxy<Message>,
- sender: mpsc::Sender<Message>,
+pub struct Proxy<T: 'static> {
+ raw: winit::event_loop::EventLoopProxy<Action<T>>,
+ sender: mpsc::Sender<Action<T>>,
notifier: mpsc::Sender<usize>,
}
-impl<Message: 'static> Clone for Proxy<Message> {
+impl<T: 'static> Clone for Proxy<T> {
fn clone(&self) -> Self {
Self {
raw: self.raw.clone(),
@@ -24,12 +25,12 @@ impl<Message: 'static> Clone for Proxy<Message> {
}
}
-impl<Message: 'static> Proxy<Message> {
+impl<T: 'static> Proxy<T> {
const MAX_SIZE: usize = 100;
/// Creates a new [`Proxy`] from an `EventLoopProxy`.
pub fn new(
- raw: winit::event_loop::EventLoopProxy<Message>,
+ raw: winit::event_loop::EventLoopProxy<Action<T>>,
) -> (Self, impl Future<Output = ()>) {
let (notifier, mut processed) = mpsc::channel(Self::MAX_SIZE);
let (sender, mut receiver) = mpsc::channel(Self::MAX_SIZE);
@@ -72,16 +73,16 @@ impl<Message: 'static> Proxy<Message> {
)
}
- /// Sends a `Message` to the event loop.
+ /// Sends a value to the event loop.
///
/// Note: This skips the backpressure mechanism with an unbounded
/// channel. Use sparingly!
- pub fn send(&mut self, message: Message)
+ pub fn send(&mut self, value: T)
where
- Message: std::fmt::Debug,
+ T: std::fmt::Debug,
{
self.raw
- .send_event(message)
+ .send_event(Action::Output(value))
.expect("Send message to event loop");
}
@@ -92,7 +93,7 @@ impl<Message: 'static> Proxy<Message> {
}
}
-impl<Message: 'static> Sink<Message> for Proxy<Message> {
+impl<T: 'static> Sink<Action<T>> for Proxy<T> {
type Error = mpsc::SendError;
fn poll_ready(
@@ -104,9 +105,9 @@ impl<Message: 'static> Sink<Message> for Proxy<Message> {
fn start_send(
mut self: Pin<&mut Self>,
- message: Message,
+ action: Action<T>,
) -> Result<(), Self::Error> {
- self.sender.start_send(message)
+ self.sender.start_send(action)
}
fn poll_flush(
diff --git a/winit/src/system.rs b/winit/src/system.rs
index c5a5b219..7997f311 100644
--- a/winit/src/system.rs
+++ b/winit/src/system.rs
@@ -1,15 +1,13 @@
//! Access the native system.
use crate::graphics::compositor;
-use crate::runtime::command::{self, Command};
use crate::runtime::system::{Action, Information};
+use crate::runtime::{self, Task};
/// Query for available system information.
-pub fn fetch_information<Message>(
- f: impl Fn(Information) -> Message + Send + 'static,
-) -> Command<Message> {
- Command::single(command::Action::System(Action::QueryInformation(
- Box::new(f),
- )))
+pub fn fetch_information() -> Task<Information> {
+ Task::oneshot(|channel| {
+ runtime::Action::System(Action::QueryInformation(channel))
+ })
}
pub(crate) fn information(