summaryrefslogtreecommitdiffstats
path: root/winit/src/application.rs
diff options
context:
space:
mode:
Diffstat (limited to 'winit/src/application.rs')
-rw-r--r--winit/src/application.rs256
1 files changed, 191 insertions, 65 deletions
diff --git a/winit/src/application.rs b/winit/src/application.rs
index ed077507..0496aea9 100644
--- a/winit/src/application.rs
+++ b/winit/src/application.rs
@@ -6,17 +6,22 @@ pub use state::State;
use crate::clipboard::{self, Clipboard};
use crate::conversion;
use crate::mouse;
+use crate::renderer;
+use crate::widget::operation;
use crate::{
- Color, Command, Debug, Error, Executor, Mode, Proxy, Runtime, Settings,
- Size, Subscription,
+ Command, Debug, Error, Executor, Proxy, Runtime, Settings, Size,
+ Subscription,
};
use iced_futures::futures;
use iced_futures::futures::channel::mpsc;
+use iced_graphics::compositor;
use iced_graphics::window;
use iced_native::program::Program;
use iced_native::user_interface::{self, UserInterface};
+pub use iced_native::application::{Appearance, StyleSheet};
+
use std::mem::ManuallyDrop;
/// An interactive, native cross-platform application.
@@ -30,7 +35,10 @@ use std::mem::ManuallyDrop;
///
/// When using an [`Application`] with the `debug` feature enabled, a debug view
/// can be toggled by pressing `F12`.
-pub trait Application: Program {
+pub trait Application: Program
+where
+ <Self::Renderer as crate::Renderer>::Theme: StyleSheet,
+{
/// The data needed to initialize your [`Application`].
type Flags;
@@ -50,6 +58,16 @@ pub trait Application: Program {
/// title of your application when necessary.
fn title(&self) -> String;
+ /// Returns the current [`Theme`] of the [`Application`].
+ fn theme(&self) -> <Self::Renderer as crate::Renderer>::Theme;
+
+ /// Returns the [`Style`] variation of the [`Theme`].
+ fn style(
+ &self,
+ ) -> <<Self::Renderer as crate::Renderer>::Theme as StyleSheet>::Style {
+ Default::default()
+ }
+
/// Returns the event `Subscription` for the current state of the
/// application.
///
@@ -63,23 +81,6 @@ pub trait Application: Program {
Subscription::none()
}
- /// Returns the current [`Application`] mode.
- ///
- /// The runtime will automatically transition your application if a new mode
- /// is returned.
- ///
- /// By default, an application will run in windowed mode.
- fn mode(&self) -> Mode {
- Mode::Windowed
- }
-
- /// Returns the background [`Color`] of the [`Application`].
- ///
- /// By default, it returns [`Color::WHITE`].
- fn background_color(&self) -> Color {
- Color::WHITE
- }
-
/// Returns the scale factor of the [`Application`].
///
/// It can be used to dynamically control the size of the UI at runtime
@@ -111,18 +112,19 @@ where
A: Application + 'static,
E: Executor + 'static,
C: window::Compositor<Renderer = A::Renderer> + 'static,
+ <A::Renderer as crate::Renderer>::Theme: StyleSheet,
{
use futures::task;
use futures::Future;
- use winit::event_loop::EventLoop;
+ use winit::event_loop::EventLoopBuilder;
let mut debug = Debug::new();
debug.startup_started();
- let event_loop = EventLoop::with_user_event();
- let mut proxy = event_loop.create_proxy();
+ let event_loop = EventLoopBuilder::with_user_event().build();
+ let proxy = event_loop.create_proxy();
- let mut runtime = {
+ let runtime = {
let proxy = Proxy::new(event_loop.create_proxy());
let executor = E::new().map_err(Error::ExecutorCreationFailed)?;
@@ -135,16 +137,15 @@ where
runtime.enter(|| A::new(flags))
};
- let subscription = application.subscription();
+ let builder = settings.window.into_builder(
+ &application.title(),
+ event_loop.primary_monitor(),
+ settings.id,
+ );
- let window = settings
- .window
- .into_builder(
- &application.title(),
- application.mode(),
- event_loop.primary_monitor(),
- settings.id,
- )
+ log::info!("Window builder: {:#?}", builder);
+
+ let window = builder
.build(&event_loop)
.map_err(Error::WindowCreationFailed)?;
@@ -163,17 +164,6 @@ where
.expect("Append canvas to HTML body");
}
- let mut clipboard = Clipboard::connect(&window);
-
- run_command(
- init_command,
- &mut runtime,
- &mut clipboard,
- &mut proxy,
- &window,
- );
- runtime.track(subscription);
-
let (compositor, renderer) = C::new(compositor_settings, Some(&window))?;
let (mut sender, receiver) = mpsc::unbounded();
@@ -183,10 +173,10 @@ where
compositor,
renderer,
runtime,
- clipboard,
proxy,
debug,
receiver,
+ init_command,
window,
settings.exit_on_close_request,
));
@@ -196,7 +186,7 @@ where
platform::run(event_loop, move |event, _, control_flow| {
use winit::event_loop::ControlFlow;
- if let ControlFlow::Exit = control_flow {
+ if let ControlFlow::ExitWithCode(_) = control_flow {
return;
}
@@ -233,20 +223,23 @@ async fn run_instance<A, E, C>(
mut compositor: C,
mut renderer: A::Renderer,
mut runtime: Runtime<E, Proxy<A::Message>, A::Message>,
- mut clipboard: Clipboard,
mut proxy: winit::event_loop::EventLoopProxy<A::Message>,
mut debug: Debug,
mut receiver: mpsc::UnboundedReceiver<winit::event::Event<'_, A::Message>>,
+ init_command: Command<A::Message>,
window: winit::window::Window,
exit_on_close_request: bool,
) where
A: Application + 'static,
E: Executor + 'static,
C: window::Compositor<Renderer = A::Renderer> + 'static,
+ <A::Renderer as crate::Renderer>::Theme: StyleSheet,
{
use iced_futures::futures::stream::StreamExt;
use winit::event;
+ let mut clipboard = Clipboard::connect(&window);
+ let mut cache = user_interface::Cache::default();
let mut surface = compositor.create_surface(&window);
let mut state = State::new(&application, &window);
@@ -260,9 +253,24 @@ async fn run_instance<A, E, C>(
physical_size.height,
);
+ run_command(
+ &application,
+ &mut cache,
+ &state,
+ &mut renderer,
+ init_command,
+ &mut runtime,
+ &mut clipboard,
+ &mut proxy,
+ &mut debug,
+ &window,
+ || compositor.fetch_information(),
+ );
+ runtime.track(application.subscription());
+
let mut user_interface = ManuallyDrop::new(build_user_interface(
- &mut application,
- user_interface::Cache::default(),
+ &application,
+ cache,
&mut renderer,
state.logical_size(),
&mut debug,
@@ -303,18 +311,22 @@ async fn run_instance<A, E, C>(
user_interface::State::Outdated,
)
{
- let cache =
+ let mut cache =
ManuallyDrop::into_inner(user_interface).into_cache();
// Update application
update(
&mut application,
+ &mut cache,
+ &state,
+ &mut renderer,
&mut runtime,
&mut clipboard,
&mut proxy,
&mut debug,
&mut messages,
&window,
+ || compositor.fetch_information(),
);
// Update window
@@ -323,7 +335,7 @@ async fn run_instance<A, E, C>(
let should_exit = application.should_exit();
user_interface = ManuallyDrop::new(build_user_interface(
- &mut application,
+ &application,
cache,
&mut renderer,
state.logical_size(),
@@ -336,8 +348,14 @@ async fn run_instance<A, E, C>(
}
debug.draw_started();
- let new_mouse_interaction =
- user_interface.draw(&mut renderer, state.cursor_position());
+ let new_mouse_interaction = user_interface.draw(
+ &mut renderer,
+ state.theme(),
+ &renderer::Style {
+ text_color: state.text_color(),
+ },
+ state.cursor_position(),
+ );
debug.draw_finished();
if new_mouse_interaction != mouse_interaction {
@@ -354,6 +372,7 @@ async fn run_instance<A, E, C>(
event::MacOS::ReceivedUrl(url),
)) => {
use iced_native::event;
+
events.push(iced_native::Event::PlatformSpecific(
event::PlatformSpecific::MacOS(event::MacOS::ReceivedUrl(
url,
@@ -384,8 +403,14 @@ async fn run_instance<A, E, C>(
debug.layout_finished();
debug.draw_started();
- let new_mouse_interaction = user_interface
- .draw(&mut renderer, state.cursor_position());
+ let new_mouse_interaction = user_interface.draw(
+ &mut renderer,
+ state.theme(),
+ &renderer::Style {
+ text_color: state.text_color(),
+ },
+ state.cursor_position(),
+ );
if new_mouse_interaction != mouse_interaction {
window.set_cursor_icon(conversion::mouse_interaction(
@@ -420,7 +445,7 @@ async fn run_instance<A, E, C>(
}
Err(error) => match error {
// This is an unrecoverable error.
- window::SurfaceError::OutOfMemory => {
+ compositor::SurfaceError::OutOfMemory => {
panic!("{:?}", error);
}
_ => {
@@ -487,12 +512,15 @@ pub fn requests_exit(
/// Builds a [`UserInterface`] for the provided [`Application`], logging
/// [`struct@Debug`] information accordingly.
pub fn build_user_interface<'a, A: Application>(
- application: &'a mut A,
+ application: &'a A,
cache: user_interface::Cache,
renderer: &mut A::Renderer,
size: Size,
debug: &mut Debug,
-) -> UserInterface<'a, A::Message, A::Renderer> {
+) -> UserInterface<'a, A::Message, A::Renderer>
+where
+ <A::Renderer as crate::Renderer>::Theme: StyleSheet,
+{
debug.view_started();
let view = application.view();
debug.view_finished();
@@ -508,13 +536,19 @@ pub fn build_user_interface<'a, A: Application>(
/// resulting [`Command`], and tracking its [`Subscription`].
pub fn update<A: Application, E: Executor>(
application: &mut A,
+ cache: &mut user_interface::Cache,
+ state: &State<A>,
+ renderer: &mut A::Renderer,
runtime: &mut Runtime<E, Proxy<A::Message>, A::Message>,
clipboard: &mut Clipboard,
proxy: &mut winit::event_loop::EventLoopProxy<A::Message>,
debug: &mut Debug,
messages: &mut Vec<A::Message>,
window: &winit::window::Window,
-) {
+ graphics_info: impl FnOnce() -> compositor::Information + Copy,
+) where
+ <A::Renderer as crate::Renderer>::Theme: StyleSheet,
+{
for message in messages.drain(..) {
debug.log_message(&message);
@@ -522,7 +556,19 @@ pub fn update<A: Application, E: Executor>(
let command = runtime.enter(|| application.update(message));
debug.update_finished();
- run_command(command, runtime, clipboard, proxy, window);
+ run_command(
+ application,
+ cache,
+ state,
+ renderer,
+ command,
+ runtime,
+ clipboard,
+ proxy,
+ debug,
+ window,
+ graphics_info,
+ );
}
let subscription = application.subscription();
@@ -530,14 +576,25 @@ pub fn update<A: Application, E: Executor>(
}
/// Runs the actions of a [`Command`].
-pub fn run_command<Message: 'static + std::fmt::Debug + Send, E: Executor>(
- command: Command<Message>,
- runtime: &mut Runtime<E, Proxy<Message>, Message>,
+pub fn run_command<A, E>(
+ application: &A,
+ 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>,
clipboard: &mut Clipboard,
- proxy: &mut winit::event_loop::EventLoopProxy<Message>,
+ proxy: &mut winit::event_loop::EventLoopProxy<A::Message>,
+ debug: &mut Debug,
window: &winit::window::Window,
-) {
+ _graphics_info: impl FnOnce() -> compositor::Information + Copy,
+) where
+ A: Application,
+ E: Executor,
+ <A::Renderer as crate::Renderer>::Theme: StyleSheet,
+{
use iced_native::command;
+ use iced_native::system;
use iced_native::window;
for action in command.actions() {
@@ -570,7 +627,76 @@ pub fn run_command<Message: 'static + std::fmt::Debug + Send, E: Executor>(
y,
});
}
+ window::Action::SetMode(mode) => {
+ window.set_visible(conversion::visible(mode));
+ window.set_fullscreen(conversion::fullscreen(
+ window.primary_monitor(),
+ mode,
+ ));
+ }
+ window::Action::FetchMode(tag) => {
+ let mode = if window.is_visible().unwrap_or(true) {
+ conversion::mode(window.fullscreen())
+ } else {
+ window::Mode::Hidden
+ };
+
+ proxy
+ .send_event(tag(mode))
+ .expect("Send message to event loop");
+ }
+ },
+ command::Action::System(action) => match action {
+ system::Action::QueryInformation(_tag) => {
+ #[cfg(feature = "system")]
+ {
+ let graphics_info = _graphics_info();
+ let proxy = proxy.clone();
+
+ let _ = std::thread::spawn(move || {
+ let information =
+ crate::system::information(graphics_info);
+
+ let message = _tag(information);
+
+ proxy
+ .send_event(message)
+ .expect("Send message to event loop")
+ });
+ }
+ }
},
+ command::Action::Widget(action) => {
+ let mut current_cache = std::mem::take(cache);
+ let mut current_operation = Some(action.into_operation());
+
+ let mut user_interface = build_user_interface(
+ application,
+ current_cache,
+ renderer,
+ state.logical_size(),
+ debug,
+ );
+
+ while let Some(mut operation) = current_operation.take() {
+ user_interface.operate(renderer, operation.as_mut());
+
+ match operation.finish() {
+ operation::Outcome::None => {}
+ operation::Outcome::Some(message) => {
+ proxy
+ .send_event(message)
+ .expect("Send message to event loop");
+ }
+ operation::Outcome::Chain(next) => {
+ current_operation = Some(next);
+ }
+ }
+ }
+
+ current_cache = user_interface.into_cache();
+ *cache = current_cache;
+ }
}
}
}