summaryrefslogtreecommitdiffstats
path: root/winit
diff options
context:
space:
mode:
Diffstat (limited to 'winit')
-rw-r--r--winit/Cargo.toml1
-rw-r--r--winit/src/application.rs96
-rw-r--r--winit/src/application/profiler.rs101
-rw-r--r--winit/src/conversion.rs165
-rw-r--r--winit/src/lib.rs8
-rw-r--r--winit/src/multi_window.rs1188
-rw-r--r--winit/src/multi_window/state.rs242
-rw-r--r--winit/src/multi_window/windows.rs196
-rw-r--r--winit/src/position.rs22
-rw-r--r--winit/src/settings.rs230
-rw-r--r--winit/src/settings/linux.rs11
-rw-r--r--winit/src/settings/macos.rs12
-rw-r--r--winit/src/settings/other.rs3
-rw-r--r--winit/src/settings/wasm.rs11
-rw-r--r--winit/src/settings/windows.rs21
15 files changed, 1806 insertions, 501 deletions
diff --git a/winit/Cargo.toml b/winit/Cargo.toml
index 674a66d3..bab05b91 100644
--- a/winit/Cargo.toml
+++ b/winit/Cargo.toml
@@ -19,6 +19,7 @@ x11 = ["winit/x11"]
wayland = ["winit/wayland"]
wayland-dlopen = ["winit/wayland-dlopen"]
wayland-csd-adwaita = ["winit/wayland-csd-adwaita"]
+multi-window = ["iced_runtime/multi-window"]
[dependencies]
iced_graphics.workspace = true
diff --git a/winit/src/application.rs b/winit/src/application.rs
index 2c5c864a..8457fd92 100644
--- a/winit/src/application.rs
+++ b/winit/src/application.rs
@@ -1,6 +1,4 @@
//! Create interactive, native cross-platform applications.
-#[cfg(feature = "trace")]
-mod profiler;
mod state;
pub use state::State;
@@ -27,11 +25,6 @@ use futures::channel::mpsc;
use std::mem::ManuallyDrop;
-#[cfg(feature = "trace")]
-pub use profiler::Profiler;
-#[cfg(feature = "trace")]
-use tracing::{info_span, instrument::Instrument};
-
/// An interactive, native cross-platform application.
///
/// This trait is the main entrypoint of Iced. Once implemented, you can run
@@ -119,15 +112,9 @@ where
use futures::Future;
use winit::event_loop::EventLoopBuilder;
- #[cfg(feature = "trace")]
- let _guard = Profiler::init();
-
let mut debug = Debug::new();
debug.startup_started();
- #[cfg(feature = "trace")]
- let _ = info_span!("Application", "RUN").entered();
-
let event_loop = EventLoopBuilder::with_user_event().build();
let proxy = event_loop.create_proxy();
@@ -148,14 +135,15 @@ where
let target = settings.window.platform_specific.target.clone();
let should_be_visible = settings.window.visible;
- let builder = settings
- .window
- .into_builder(
- &application.title(),
- event_loop.primary_monitor(),
- settings.id,
- )
- .with_visible(false);
+ let exit_on_close_request = settings.window.exit_on_close_request;
+
+ let builder = conversion::window_settings(
+ settings.window,
+ &application.title(),
+ event_loop.primary_monitor(),
+ settings.id,
+ )
+ .with_visible(false);
log::debug!("Window builder: {builder:#?}");
@@ -205,28 +193,20 @@ where
let (mut event_sender, event_receiver) = mpsc::unbounded();
let (control_sender, mut control_receiver) = mpsc::unbounded();
- let mut instance = Box::pin({
- let run_instance = run_instance::<A, E, C>(
- application,
- compositor,
- renderer,
- runtime,
- proxy,
- debug,
- event_receiver,
- control_sender,
- init_command,
- window,
- should_be_visible,
- settings.exit_on_close_request,
- );
-
- #[cfg(feature = "trace")]
- let run_instance =
- run_instance.instrument(info_span!("Application", "LOOP"));
-
- run_instance
- });
+ let mut instance = Box::pin(run_instance::<A, E, C>(
+ application,
+ compositor,
+ renderer,
+ runtime,
+ proxy,
+ debug,
+ event_receiver,
+ control_sender,
+ init_command,
+ window,
+ should_be_visible,
+ exit_on_close_request,
+ ));
let mut context = task::Context::from_waker(task::noop_waker_ref());
@@ -426,6 +406,7 @@ 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()),
);
@@ -488,9 +469,6 @@ async fn run_instance<A, E, C>(
messages.push(message);
}
event::Event::RedrawRequested(_) => {
- #[cfg(feature = "trace")]
- let _ = info_span!("Application", "FRAME").entered();
-
let physical_size = state.physical_size();
if physical_size.width == 0 || physical_size.height == 0 {
@@ -578,6 +556,7 @@ 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(),
@@ -629,24 +608,12 @@ pub fn build_user_interface<'a, A: Application>(
where
<A::Renderer as core::Renderer>::Theme: StyleSheet,
{
- #[cfg(feature = "trace")]
- let view_span = info_span!("Application", "VIEW").entered();
-
debug.view_started();
let view = application.view();
-
- #[cfg(feature = "trace")]
- let _ = view_span.exit();
debug.view_finished();
- #[cfg(feature = "trace")]
- let layout_span = info_span!("Application", "LAYOUT").entered();
-
debug.layout_started();
let user_interface = UserInterface::build(view, size, cache, renderer);
-
- #[cfg(feature = "trace")]
- let _ = layout_span.exit();
debug.layout_finished();
user_interface
@@ -673,16 +640,10 @@ pub fn update<A: Application, C, E: Executor>(
<A::Renderer as core::Renderer>::Theme: StyleSheet,
{
for message in messages.drain(..) {
- #[cfg(feature = "trace")]
- let update_span = info_span!("Application", "UPDATE").entered();
-
debug.log_message(&message);
debug.update_started();
let command = runtime.enter(|| application.update(message));
-
- #[cfg(feature = "trace")]
- let _ = update_span.exit();
debug.update_finished();
run_command(
@@ -751,13 +712,18 @@ pub fn run_command<A, C, E>(
clipboard.write(contents);
}
},
- command::Action::Window(action) => match action {
+ command::Action::Window(_, action) => match action {
window::Action::Close => {
*should_exit = true;
}
window::Action::Drag => {
let _res = window.drag_window();
}
+ window::Action::Spawn { .. } => {
+ log::info!(
+ "Spawning a window is only available with multi-window applications."
+ )
+ }
window::Action::Resize(size) => {
window.set_inner_size(winit::dpi::LogicalSize {
width: size.width,
diff --git a/winit/src/application/profiler.rs b/winit/src/application/profiler.rs
deleted file mode 100644
index 7031507a..00000000
--- a/winit/src/application/profiler.rs
+++ /dev/null
@@ -1,101 +0,0 @@
-//! A simple profiler for Iced.
-use std::ffi::OsStr;
-use std::path::Path;
-use std::time::Duration;
-use tracing_subscriber::prelude::*;
-use tracing_subscriber::Registry;
-#[cfg(feature = "chrome-trace")]
-use {
- tracing_chrome::FlushGuard,
- tracing_subscriber::fmt::{format::DefaultFields, FormattedFields},
-};
-
-/// Profiler state. This will likely need to be updated or reworked when adding new tracing backends.
-#[allow(missing_debug_implementations)]
-pub struct Profiler {
- #[cfg(feature = "chrome-trace")]
- /// [`FlushGuard`] must not be dropped until the application scope is dropped for accurate tracing.
- _guard: FlushGuard,
-}
-
-impl Profiler {
- /// Initializes the [`Profiler`].
- pub fn init() -> Self {
- // Registry stores the spans & generates unique span IDs
- let subscriber = Registry::default();
-
- let default_path = Path::new(env!("CARGO_MANIFEST_DIR"));
- let curr_exe = std::env::current_exe()
- .unwrap_or_else(|_| default_path.to_path_buf());
- let out_dir = curr_exe.parent().unwrap_or(default_path).join("traces");
-
- #[cfg(feature = "chrome-trace")]
- let (chrome_layer, guard) = {
- let mut layer = tracing_chrome::ChromeLayerBuilder::new();
-
- // Optional configurable env var: CHROME_TRACE_FILE=/path/to/trace_file/file.json,
- // for uploading to chrome://tracing (old) or ui.perfetto.dev (new).
- if let Ok(path) = std::env::var("CHROME_TRACE_FILE") {
- layer = layer.file(path);
- } else if std::fs::create_dir_all(&out_dir).is_ok() {
- let time = std::time::SystemTime::now()
- .duration_since(std::time::UNIX_EPOCH)
- .unwrap_or(Duration::from_millis(0))
- .as_millis();
-
- let curr_exe_name = curr_exe
- .file_name()
- .unwrap_or_else(|| OsStr::new("trace"))
- .to_str()
- .unwrap_or("trace");
-
- let path =
- out_dir.join(format!("{curr_exe_name}_trace_{time}.json"));
-
- layer = layer.file(path);
- } else {
- layer = layer.file(env!("CARGO_MANIFEST_DIR"))
- }
-
- let (chrome_layer, guard) = layer
- .name_fn(Box::new(|event_or_span| match event_or_span {
- tracing_chrome::EventOrSpan::Event(event) => {
- event.metadata().name().into()
- }
- tracing_chrome::EventOrSpan::Span(span) => {
- if let Some(fields) = span
- .extensions()
- .get::<FormattedFields<DefaultFields>>()
- {
- format!(
- "{}: {}",
- span.metadata().name(),
- fields.fields.as_str()
- )
- } else {
- span.metadata().name().into()
- }
- }
- }))
- .build();
-
- (chrome_layer, guard)
- };
-
- let fmt_layer = tracing_subscriber::fmt::Layer::default();
- let subscriber = subscriber.with(fmt_layer);
-
- #[cfg(feature = "chrome-trace")]
- let subscriber = subscriber.with(chrome_layer);
-
- // create dispatcher which will forward span events to the subscriber
- // this can only be set once or will panic
- tracing::subscriber::set_global_default(subscriber)
- .expect("Tracer could not set the global default subscriber.");
-
- Profiler {
- #[cfg(feature = "chrome-trace")]
- _guard: guard,
- }
- }
-}
diff --git a/winit/src/conversion.rs b/winit/src/conversion.rs
index 3ecd044c..22e6b9be 100644
--- a/winit/src/conversion.rs
+++ b/winit/src/conversion.rs
@@ -7,10 +7,120 @@ use crate::core::mouse;
use crate::core::touch;
use crate::core::window;
use crate::core::{Event, Point};
-use crate::Position;
+
+/// Converts some [`window::Settings`] into a `WindowBuilder` from `winit`.
+pub fn window_settings(
+ settings: window::Settings,
+ title: &str,
+ primary_monitor: Option<winit::monitor::MonitorHandle>,
+ _id: Option<String>,
+) -> winit::window::WindowBuilder {
+ let mut window_builder = winit::window::WindowBuilder::new();
+
+ let (width, height) = settings.size;
+
+ window_builder = window_builder
+ .with_title(title)
+ .with_inner_size(winit::dpi::LogicalSize { width, height })
+ .with_resizable(settings.resizable)
+ .with_enabled_buttons(if settings.resizable {
+ winit::window::WindowButtons::all()
+ } else {
+ winit::window::WindowButtons::CLOSE
+ | winit::window::WindowButtons::MINIMIZE
+ })
+ .with_decorations(settings.decorations)
+ .with_transparent(settings.transparent)
+ .with_window_icon(settings.icon.and_then(icon))
+ .with_window_level(window_level(settings.level))
+ .with_visible(settings.visible);
+
+ if let Some(position) =
+ position(primary_monitor.as_ref(), settings.size, settings.position)
+ {
+ window_builder = window_builder.with_position(position);
+ }
+
+ if let Some((width, height)) = settings.min_size {
+ window_builder = window_builder
+ .with_min_inner_size(winit::dpi::LogicalSize { width, height });
+ }
+
+ if let Some((width, height)) = settings.max_size {
+ window_builder = window_builder
+ .with_max_inner_size(winit::dpi::LogicalSize { width, height });
+ }
+
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ {
+ // `with_name` is available on both `WindowBuilderExtWayland` and `WindowBuilderExtX11` and they do
+ // exactly the same thing. We arbitrarily choose `WindowBuilderExtWayland` here.
+ use ::winit::platform::wayland::WindowBuilderExtWayland;
+
+ if let Some(id) = _id {
+ window_builder = window_builder.with_name(id.clone(), id);
+ }
+ }
+
+ #[cfg(target_os = "windows")]
+ {
+ use winit::platform::windows::WindowBuilderExtWindows;
+ #[allow(unsafe_code)]
+ unsafe {
+ window_builder = window_builder
+ .with_parent_window(settings.platform_specific.parent);
+ }
+ window_builder = window_builder
+ .with_drag_and_drop(settings.platform_specific.drag_and_drop);
+ }
+
+ #[cfg(target_os = "macos")]
+ {
+ use winit::platform::macos::WindowBuilderExtMacOS;
+
+ window_builder = window_builder
+ .with_title_hidden(settings.platform_specific.title_hidden)
+ .with_titlebar_transparent(
+ settings.platform_specific.titlebar_transparent,
+ )
+ .with_fullsize_content_view(
+ settings.platform_specific.fullsize_content_view,
+ );
+ }
+
+ #[cfg(target_os = "linux")]
+ {
+ #[cfg(feature = "x11")]
+ {
+ use winit::platform::x11::WindowBuilderExtX11;
+
+ window_builder = window_builder.with_name(
+ &settings.platform_specific.application_id,
+ &settings.platform_specific.application_id,
+ );
+ }
+ #[cfg(feature = "wayland")]
+ {
+ use winit::platform::wayland::WindowBuilderExtWayland;
+
+ window_builder = window_builder.with_name(
+ &settings.platform_specific.application_id,
+ &settings.platform_specific.application_id,
+ );
+ }
+ }
+
+ window_builder
+}
/// 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::event::ModifiersState,
@@ -21,21 +131,27 @@ pub fn window_event(
WindowEvent::Resized(new_size) => {
let logical_size = new_size.to_logical(scale_factor);
- Some(Event::Window(window::Event::Resized {
- width: logical_size.width,
- height: logical_size.height,
- }))
+ Some(Event::Window(
+ id,
+ window::Event::Resized {
+ width: logical_size.width,
+ height: logical_size.height,
+ },
+ ))
}
WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
let logical_size = new_inner_size.to_logical(scale_factor);
- Some(Event::Window(window::Event::Resized {
- width: logical_size.width,
- height: logical_size.height,
- }))
+ Some(Event::Window(
+ id,
+ window::Event::Resized {
+ width: logical_size.width,
+ height: logical_size.height,
+ },
+ ))
}
WindowEvent::CloseRequested => {
- Some(Event::Window(window::Event::CloseRequested))
+ Some(Event::Window(id, window::Event::CloseRequested))
}
WindowEvent::CursorMoved { position, .. } => {
let position = position.to_logical::<f64>(scale_factor);
@@ -113,19 +229,22 @@ pub fn window_event(
WindowEvent::ModifiersChanged(new_modifiers) => Some(Event::Keyboard(
keyboard::Event::ModifiersChanged(self::modifiers(*new_modifiers)),
)),
- WindowEvent::Focused(focused) => Some(Event::Window(if *focused {
- window::Event::Focused
- } else {
- window::Event::Unfocused
- })),
+ WindowEvent::Focused(focused) => Some(Event::Window(
+ id,
+ if *focused {
+ window::Event::Focused
+ } else {
+ window::Event::Unfocused
+ },
+ )),
WindowEvent::HoveredFile(path) => {
- Some(Event::Window(window::Event::FileHovered(path.clone())))
+ Some(Event::Window(id, window::Event::FileHovered(path.clone())))
}
WindowEvent::DroppedFile(path) => {
- Some(Event::Window(window::Event::FileDropped(path.clone())))
+ Some(Event::Window(id, window::Event::FileDropped(path.clone())))
}
WindowEvent::HoveredFileCancelled => {
- Some(Event::Window(window::Event::FilesHoveredLeft))
+ Some(Event::Window(id, window::Event::FilesHoveredLeft))
}
WindowEvent::Touch(touch) => {
Some(Event::Touch(touch_event(*touch, scale_factor)))
@@ -134,7 +253,7 @@ pub fn window_event(
let winit::dpi::LogicalPosition { x, y } =
position.to_logical(scale_factor);
- Some(Event::Window(window::Event::Moved { x, y }))
+ Some(Event::Window(id, window::Event::Moved { x, y }))
}
_ => None,
}
@@ -159,17 +278,17 @@ pub fn window_level(level: window::Level) -> winit::window::WindowLevel {
pub fn position(
monitor: Option<&winit::monitor::MonitorHandle>,
(width, height): (u32, u32),
- position: Position,
+ position: window::Position,
) -> Option<winit::dpi::Position> {
match position {
- Position::Default => None,
- Position::Specific(x, y) => {
+ window::Position::Default => None,
+ window::Position::Specific(x, y) => {
Some(winit::dpi::Position::Logical(winit::dpi::LogicalPosition {
x: f64::from(x),
y: f64::from(y),
}))
}
- Position::Centered => {
+ window::Position::Centered => {
if let Some(monitor) = monitor {
let start = monitor.position();
diff --git a/winit/src/lib.rs b/winit/src/lib.rs
index 95b55bb9..cc886354 100644
--- a/winit/src/lib.rs
+++ b/winit/src/lib.rs
@@ -33,6 +33,9 @@ pub use iced_runtime::futures;
pub use iced_style as style;
pub use winit;
+#[cfg(feature = "multi-window")]
+pub mod multi_window;
+
#[cfg(feature = "application")]
pub mod application;
pub mod clipboard;
@@ -43,17 +46,14 @@ pub mod settings;
pub mod system;
mod error;
-mod position;
mod proxy;
#[cfg(feature = "application")]
pub use application::Application;
-#[cfg(feature = "trace")]
-pub use application::Profiler;
pub use clipboard::Clipboard;
pub use error::Error;
-pub use position::Position;
pub use proxy::Proxy;
pub use settings::Settings;
+pub use crate::core::window::*;
pub use iced_graphics::Viewport;
diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs
new file mode 100644
index 00000000..f2452eb3
--- /dev/null
+++ b/winit/src/multi_window.rs
@@ -0,0 +1,1188 @@
+//! Create interactive, native cross-platform applications for WGPU.
+mod state;
+mod windows;
+
+pub use state::State;
+
+use crate::conversion;
+use crate::core::widget::operation;
+use crate::core::{self, mouse, renderer, window, Size};
+use crate::futures::futures::channel::mpsc;
+use crate::futures::futures::{task, Future, FutureExt, StreamExt};
+use crate::futures::{Executor, Runtime, Subscription};
+use crate::graphics::{compositor, Compositor};
+use crate::multi_window::windows::Windows;
+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::style::application::StyleSheet;
+use crate::{Clipboard, Error, Proxy, Settings};
+
+use std::mem::ManuallyDrop;
+use std::time::Instant;
+use winit::monitor::MonitorHandle;
+
+/// This is a wrapper around the `Application::Message` associate type
+/// to allows the `shell` to create internal messages, while still having
+/// the current user-specified custom messages.
+#[derive(Debug)]
+pub enum Event<Message> {
+ /// An internal event which contains an [`Application`] generated message.
+ Application(Message),
+ /// An internal event which spawns a new window.
+ NewWindow {
+ /// The [window::Id] of the newly spawned [`Window`].
+ id: window::Id,
+ /// The [settings::Window] of the newly spawned [`Window`].
+ settings: window::Settings,
+ /// The title of the newly spawned [`Window`].
+ title: String,
+ /// The monitor on which to spawn the window. If `None`, will use monitor of the last window
+ /// spawned.
+ monitor: Option<MonitorHandle>,
+ },
+ /// An internal event for closing a window.
+ CloseWindow(window::Id),
+ /// An internal event for when the window has finished being created.
+ WindowCreated {
+ /// The internal ID of the window.
+ id: window::Id,
+ /// The raw window.
+ window: winit::window::Window,
+ /// Whether or not the window should close when a user requests it does.
+ exit_on_close_request: bool,
+ },
+}
+
+#[allow(unsafe_code)]
+unsafe impl<Message> std::marker::Send for Event<Message> {}
+
+/// An interactive, native, cross-platform, multi-windowed application.
+///
+/// This trait is the main entrypoint of multi-window Iced. Once implemented, you can run
+/// your GUI application by simply calling [`run`]. It will run in
+/// its own window.
+///
+/// An [`Application`] can execute asynchronous actions by returning a
+/// [`Command`] in some of its methods.
+///
+/// When using an [`Application`] with the `debug` feature enabled, a debug view
+/// can be toggled by pressing `F12`.
+pub trait Application: Program
+where
+ <Self::Renderer as core::Renderer>::Theme: StyleSheet,
+{
+ /// The data needed to initialize your [`Application`].
+ type Flags;
+
+ /// Initializes the [`Application`] with the flags provided to
+ /// [`run`] as part of the [`Settings`].
+ ///
+ /// Here is where you should return the initial state of your app.
+ ///
+ /// Additionally, you can return a [`Command`] 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>);
+
+ /// Returns the current title of the [`Application`].
+ ///
+ /// This title can be dynamic! The runtime will automatically update the
+ /// title of your application when necessary.
+ fn title(&self, window: window::Id) -> String;
+
+ /// Returns the current `Theme` of the [`Application`].
+ fn theme(
+ &self,
+ window: window::Id,
+ ) -> <Self::Renderer as core::Renderer>::Theme;
+
+ /// Returns the `Style` variation of the `Theme`.
+ fn style(
+ &self,
+ ) -> <<Self::Renderer as core::Renderer>::Theme as StyleSheet>::Style {
+ Default::default()
+ }
+
+ /// Returns the event `Subscription` for the current state of the
+ /// application.
+ ///
+ /// The messages produced by the `Subscription` will be handled by
+ /// [`update`](#tymethod.update).
+ ///
+ /// A `Subscription` will be kept alive as long as you keep returning it!
+ ///
+ /// By default, it returns an empty subscription.
+ fn subscription(&self) -> Subscription<Self::Message> {
+ Subscription::none()
+ }
+
+ /// Returns the scale factor of the window of the [`Application`].
+ ///
+ /// It can be used to dynamically control the size of the UI at runtime
+ /// (i.e. zooming).
+ ///
+ /// For instance, a scale factor of `2.0` will make widgets twice as big,
+ /// while a scale factor of `0.5` will shrink them to half their size.
+ ///
+ /// By default, it returns `1.0`.
+ #[allow(unused_variables)]
+ fn scale_factor(&self, window: window::Id) -> f64 {
+ 1.0
+ }
+}
+
+/// Runs an [`Application`] with an executor, compositor, and the provided
+/// settings.
+pub fn run<A, E, C>(
+ settings: Settings<A::Flags>,
+ compositor_settings: C::Settings,
+) -> Result<(), Error>
+where
+ A: Application + 'static,
+ E: Executor + 'static,
+ C: Compositor<Renderer = A::Renderer> + 'static,
+ <A::Renderer as core::Renderer>::Theme: StyleSheet,
+{
+ use winit::event_loop::EventLoopBuilder;
+
+ let mut debug = Debug::new();
+ debug.startup_started();
+
+ let event_loop = EventLoopBuilder::with_user_event().build();
+ let proxy = event_loop.create_proxy();
+
+ let runtime = {
+ let proxy = Proxy::new(event_loop.create_proxy());
+ let executor = E::new().map_err(Error::ExecutorCreationFailed)?;
+
+ Runtime::new(executor, proxy)
+ };
+
+ let (application, init_command) = {
+ let flags = settings.flags;
+
+ runtime.enter(|| A::new(flags))
+ };
+
+ let should_main_be_visible = settings.window.visible;
+ let exit_on_close_request = settings.window.exit_on_close_request;
+
+ let builder = conversion::window_settings(
+ settings.window,
+ &application.title(window::Id::MAIN),
+ event_loop.primary_monitor(),
+ settings.id,
+ )
+ .with_visible(false);
+
+ log::info!("Window builder: {:#?}", builder);
+
+ let main_window = builder
+ .build(&event_loop)
+ .map_err(Error::WindowCreationFailed)?;
+
+ #[cfg(target_arch = "wasm32")]
+ {
+ use winit::platform::web::WindowExtWebSys;
+
+ let canvas = main_window.canvas();
+
+ let window = web_sys::window().unwrap();
+ let document = window.document().unwrap();
+ let body = document.body().unwrap();
+
+ let target = target.and_then(|target| {
+ body.query_selector(&format!("#{}", target))
+ .ok()
+ .unwrap_or(None)
+ });
+
+ match target {
+ Some(node) => {
+ let _ = node
+ .replace_with_with_node_1(&canvas)
+ .expect(&format!("Could not replace #{}", node.id()));
+ }
+ None => {
+ let _ = body
+ .append_child(&canvas)
+ .expect("Append canvas to HTML body");
+ }
+ };
+ }
+
+ let (mut compositor, renderer) =
+ C::new(compositor_settings, Some(&main_window))?;
+
+ let windows = Windows::new(
+ &application,
+ &mut compositor,
+ renderer,
+ main_window,
+ exit_on_close_request,
+ );
+
+ let (mut event_sender, event_receiver) = mpsc::unbounded();
+ let (control_sender, mut control_receiver) = mpsc::unbounded();
+
+ let mut instance = Box::pin(run_instance::<A, E, C>(
+ application,
+ compositor,
+ runtime,
+ proxy,
+ debug,
+ event_receiver,
+ control_sender,
+ init_command,
+ windows,
+ should_main_be_visible,
+ ));
+
+ let mut context = task::Context::from_waker(task::noop_waker_ref());
+
+ platform::run(event_loop, move |event, window_target, control_flow| {
+ use winit::event_loop::ControlFlow;
+
+ if let ControlFlow::ExitWithCode(_) = control_flow {
+ return;
+ }
+
+ let event = match event {
+ winit::event::Event::WindowEvent {
+ event:
+ winit::event::WindowEvent::ScaleFactorChanged {
+ new_inner_size,
+ ..
+ },
+ window_id,
+ } => Some(winit::event::Event::WindowEvent {
+ event: winit::event::WindowEvent::Resized(*new_inner_size),
+ window_id,
+ }),
+ winit::event::Event::UserEvent(Event::NewWindow {
+ id,
+ settings,
+ title,
+ monitor,
+ }) => {
+ let exit_on_close_request = settings.exit_on_close_request;
+
+ let window = conversion::window_settings(
+ settings, &title, monitor, None,
+ )
+ .build(window_target)
+ .expect("Failed to build window");
+
+ Some(winit::event::Event::UserEvent(Event::WindowCreated {
+ id,
+ window,
+ exit_on_close_request,
+ }))
+ }
+ _ => event.to_static(),
+ };
+
+ if let Some(event) = event {
+ event_sender.start_send(event).expect("Send event");
+
+ let poll = instance.as_mut().poll(&mut context);
+
+ match poll {
+ task::Poll::Pending => {
+ if let Ok(Some(flow)) = control_receiver.try_next() {
+ *control_flow = flow;
+ }
+ }
+ task::Poll::Ready(_) => {
+ *control_flow = ControlFlow::Exit;
+ }
+ };
+ }
+ })
+}
+
+async fn run_instance<A, E, C>(
+ mut application: A,
+ mut compositor: C,
+ mut runtime: Runtime<E, Proxy<Event<A::Message>>, Event<A::Message>>,
+ mut proxy: winit::event_loop::EventLoopProxy<Event<A::Message>>,
+ mut debug: Debug,
+ mut event_receiver: mpsc::UnboundedReceiver<
+ winit::event::Event<'_, Event<A::Message>>,
+ >,
+ mut control_sender: mpsc::UnboundedSender<winit::event_loop::ControlFlow>,
+ init_command: Command<A::Message>,
+ mut windows: Windows<A, C>,
+ should_main_window_be_visible: bool,
+) where
+ A: Application + 'static,
+ E: Executor + 'static,
+ C: Compositor<Renderer = A::Renderer> + 'static,
+ <A::Renderer as core::Renderer>::Theme: StyleSheet,
+{
+ use winit::event;
+ use winit::event_loop::ControlFlow;
+
+ let mut clipboard = Clipboard::connect(windows.main());
+
+ let mut ui_caches = vec![user_interface::Cache::default()];
+ let mut user_interfaces = ManuallyDrop::new(build_user_interfaces(
+ &application,
+ &mut debug,
+ &mut windows,
+ vec![user_interface::Cache::default()],
+ ));
+
+ if should_main_window_be_visible {
+ windows.main().set_visible(true);
+ }
+
+ run_command(
+ &application,
+ &mut compositor,
+ init_command,
+ &mut runtime,
+ &mut clipboard,
+ &mut proxy,
+ &mut debug,
+ &mut windows,
+ &mut ui_caches,
+ );
+
+ runtime.track(
+ application
+ .subscription()
+ .map(Event::Application)
+ .into_recipes(),
+ );
+
+ let mut mouse_interaction = mouse::Interaction::default();
+
+ let mut events =
+ if let Some((position, size)) = logical_bounds_of(windows.main()) {
+ vec![(
+ Some(window::Id::MAIN),
+ core::Event::Window(
+ window::Id::MAIN,
+ window::Event::Created { position, size },
+ ),
+ )]
+ } else {
+ Vec::new()
+ };
+ let mut messages = Vec::new();
+ let mut redraw_pending = false;
+
+ debug.startup_finished();
+
+ 'main: while let Some(event) = event_receiver.next().await {
+ match event {
+ event::Event::NewEvents(start_cause) => {
+ redraw_pending = matches!(
+ start_cause,
+ event::StartCause::Init
+ | event::StartCause::Poll
+ | event::StartCause::ResumeTimeReached { .. }
+ );
+ }
+ event::Event::MainEventsCleared => {
+ debug.event_processing_started();
+ let mut uis_stale = false;
+
+ for (i, id) in windows.ids.iter().enumerate() {
+ let mut window_events = vec![];
+
+ events.retain(|(window_id, event)| {
+ if *window_id == Some(*id) || window_id.is_none() {
+ window_events.push(event.clone());
+ false
+ } else {
+ true
+ }
+ });
+
+ if !redraw_pending
+ && window_events.is_empty()
+ && messages.is_empty()
+ {
+ continue;
+ }
+
+ let (ui_state, statuses) = user_interfaces[i].update(
+ &window_events,
+ windows.states[i].cursor(),
+ &mut windows.renderers[i],
+ &mut clipboard,
+ &mut messages,
+ );
+
+ if !uis_stale {
+ uis_stale =
+ matches!(ui_state, user_interface::State::Outdated);
+ }
+
+ for (event, status) in
+ window_events.into_iter().zip(statuses.into_iter())
+ {
+ runtime.broadcast(event, status);
+ }
+ }
+
+ debug.event_processing_finished();
+
+ // TODO mw application update returns which window IDs to update
+ if !messages.is_empty() || uis_stale {
+ let mut cached_interfaces: Vec<user_interface::Cache> =
+ ManuallyDrop::into_inner(user_interfaces)
+ .drain(..)
+ .map(UserInterface::into_cache)
+ .collect();
+
+ // Update application
+ update(
+ &mut application,
+ &mut compositor,
+ &mut runtime,
+ &mut clipboard,
+ &mut proxy,
+ &mut debug,
+ &mut messages,
+ &mut windows,
+ &mut cached_interfaces,
+ );
+
+ // we must synchronize all window states with application state after an
+ // application update since we don't know what changed
+ for (state, (id, window)) in windows
+ .states
+ .iter_mut()
+ .zip(windows.ids.iter().zip(windows.raw.iter()))
+ {
+ state.synchronize(&application, *id, window);
+ }
+
+ // rebuild UIs with the synchronized states
+ user_interfaces = ManuallyDrop::new(build_user_interfaces(
+ &application,
+ &mut debug,
+ &mut windows,
+ cached_interfaces,
+ ));
+ }
+
+ debug.draw_started();
+
+ for (i, id) in windows.ids.iter().enumerate() {
+ // TODO: Avoid redrawing all the time by forcing widgets to
+ // request redraws on state changes
+ //
+ // 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()),
+ );
+
+ let cursor = windows.states[i].cursor();
+
+ let (ui_state, _) = user_interfaces[i].update(
+ &[redraw_event.clone()],
+ cursor,
+ &mut windows.renderers[i],
+ &mut clipboard,
+ &mut messages,
+ );
+
+ let new_mouse_interaction = {
+ let state = &windows.states[i];
+
+ user_interfaces[i].draw(
+ &mut windows.renderers[i],
+ state.theme(),
+ &renderer::Style {
+ text_color: state.text_color(),
+ },
+ cursor,
+ )
+ };
+
+ if new_mouse_interaction != mouse_interaction {
+ windows.raw[i].set_cursor_icon(
+ conversion::mouse_interaction(
+ new_mouse_interaction,
+ ),
+ );
+
+ mouse_interaction = new_mouse_interaction;
+ }
+
+ // TODO once widgets can request to be redrawn, we can avoid always requesting a
+ // redraw
+ windows.raw[i].request_redraw();
+
+ runtime.broadcast(
+ redraw_event.clone(),
+ core::event::Status::Ignored,
+ );
+
+ let _ = control_sender.start_send(match ui_state {
+ user_interface::State::Updated {
+ redraw_request: Some(redraw_request),
+ } => match redraw_request {
+ window::RedrawRequest::NextFrame => {
+ ControlFlow::Poll
+ }
+ window::RedrawRequest::At(at) => {
+ ControlFlow::WaitUntil(at)
+ }
+ },
+ _ => ControlFlow::Wait,
+ });
+ }
+
+ redraw_pending = false;
+
+ debug.draw_finished();
+ }
+ event::Event::PlatformSpecific(event::PlatformSpecific::MacOS(
+ event::MacOS::ReceivedUrl(url),
+ )) => {
+ use crate::core::event;
+
+ events.push((
+ None,
+ event::Event::PlatformSpecific(
+ event::PlatformSpecific::MacOS(
+ event::MacOS::ReceivedUrl(url),
+ ),
+ ),
+ ));
+ }
+ event::Event::UserEvent(event) => match event {
+ Event::Application(message) => {
+ messages.push(message);
+ }
+ Event::WindowCreated {
+ id,
+ window,
+ exit_on_close_request,
+ } => {
+ let bounds = logical_bounds_of(&window);
+
+ let (inner_size, i) = windows.add(
+ &application,
+ &mut compositor,
+ id,
+ window,
+ exit_on_close_request,
+ );
+
+ user_interfaces.push(build_user_interface(
+ &application,
+ user_interface::Cache::default(),
+ &mut windows.renderers[i],
+ inner_size,
+ &mut debug,
+ id,
+ ));
+ ui_caches.push(user_interface::Cache::default());
+
+ if let Some(bounds) = bounds {
+ events.push((
+ Some(id),
+ core::Event::Window(
+ id,
+ window::Event::Created {
+ position: bounds.0,
+ size: bounds.1,
+ },
+ ),
+ ));
+ }
+ }
+ Event::CloseWindow(id) => {
+ let i = windows.delete(id);
+ let _ = user_interfaces.remove(i);
+ let _ = ui_caches.remove(i);
+
+ if windows.is_empty() {
+ break 'main;
+ }
+ }
+ Event::NewWindow { .. } => unreachable!(),
+ },
+ event::Event::RedrawRequested(id) => {
+ let i = windows.index_from_raw(id);
+ let state = &windows.states[i];
+ let physical_size = state.physical_size();
+
+ if physical_size.width == 0 || physical_size.height == 0 {
+ continue;
+ }
+
+ debug.render_started();
+ let current_viewport_version = state.viewport_version();
+ let window_viewport_version = windows.viewport_versions[i];
+
+ if window_viewport_version != current_viewport_version {
+ let logical_size = state.logical_size();
+
+ debug.layout_started();
+
+ let renderer = &mut windows.renderers[i];
+ let ui = user_interfaces.remove(i);
+
+ user_interfaces
+ .insert(i, ui.relayout(logical_size, renderer));
+
+ debug.layout_finished();
+
+ debug.draw_started();
+ let new_mouse_interaction = user_interfaces[i].draw(
+ renderer,
+ state.theme(),
+ &renderer::Style {
+ text_color: state.text_color(),
+ },
+ state.cursor(),
+ );
+
+ if new_mouse_interaction != mouse_interaction {
+ windows.raw[i].set_cursor_icon(
+ conversion::mouse_interaction(
+ new_mouse_interaction,
+ ),
+ );
+
+ mouse_interaction = new_mouse_interaction;
+ }
+ debug.draw_finished();
+
+ compositor.configure_surface(
+ &mut windows.surfaces[i],
+ physical_size.width,
+ physical_size.height,
+ );
+
+ windows.viewport_versions[i] = current_viewport_version;
+ }
+
+ match compositor.present(
+ &mut windows.renderers[i],
+ &mut windows.surfaces[i],
+ state.viewport(),
+ state.background_color(),
+ &debug.overlay(),
+ ) {
+ Ok(()) => {
+ debug.render_finished();
+
+ // TODO: Handle animations!
+ // Maybe we can use `ControlFlow::WaitUntil` for this.
+ }
+ Err(error) => match error {
+ // This is an unrecoverable error.
+ compositor::SurfaceError::OutOfMemory => {
+ panic!("{:?}", error);
+ }
+ _ => {
+ debug.render_finished();
+ log::error!(
+ "Error {error:?} when presenting surface."
+ );
+
+ // Try rendering all windows again next frame.
+ for window in &windows.raw {
+ window.request_redraw();
+ }
+ }
+ },
+ }
+ }
+ event::Event::WindowEvent {
+ event: window_event,
+ window_id,
+ } => {
+ let window_index =
+ windows.raw.iter().position(|w| w.id() == window_id);
+
+ match window_index {
+ Some(i) => {
+ let id = windows.ids[i];
+ let raw = &windows.raw[i];
+ let exit_on_close_request =
+ windows.exit_on_close_requested[i];
+
+ if matches!(
+ window_event,
+ winit::event::WindowEvent::CloseRequested
+ ) && exit_on_close_request
+ {
+ let i = windows.delete(id);
+ let _ = user_interfaces.remove(i);
+ let _ = ui_caches.remove(i);
+
+ if windows.is_empty() {
+ break 'main;
+ }
+ } else {
+ let state = &mut windows.states[i];
+ state.update(raw, &window_event, &mut debug);
+
+ if let Some(event) = conversion::window_event(
+ id,
+ &window_event,
+ state.scale_factor(),
+ state.modifiers(),
+ ) {
+ events.push((Some(id), event));
+ }
+ }
+ }
+ None => {
+ // This is the only special case, since in order to trigger the Destroyed event the
+ // window reference from winit must be dropped, but we still want to inform the
+ // user that the window was destroyed so they can clean up any specific window
+ // code for this window
+ if matches!(
+ window_event,
+ winit::event::WindowEvent::Destroyed
+ ) {
+ let id = windows.get_pending_destroy(window_id);
+
+ events.push((
+ None,
+ core::Event::Window(
+ id,
+ window::Event::Destroyed,
+ ),
+ ));
+ }
+ }
+ }
+ }
+ _ => {}
+ }
+ }
+
+ let _ = ManuallyDrop::into_inner(user_interfaces);
+}
+
+/// Builds a window's [`UserInterface`] for the [`Application`].
+pub fn build_user_interface<'a, A: Application>(
+ application: &'a A,
+ cache: user_interface::Cache,
+ renderer: &mut A::Renderer,
+ size: Size,
+ debug: &mut Debug,
+ id: window::Id,
+) -> UserInterface<'a, A::Message, A::Renderer>
+where
+ <A::Renderer as core::Renderer>::Theme: StyleSheet,
+{
+ debug.view_started();
+ let view = application.view(id);
+ debug.view_finished();
+
+ debug.layout_started();
+ let user_interface = UserInterface::build(view, size, cache, renderer);
+ debug.layout_finished();
+
+ user_interface
+}
+
+/// Updates a multi-window [`Application`] by feeding it messages, spawning any
+/// resulting [`Command`], and tracking its [`Subscription`].
+pub fn update<A: Application, C, E: Executor>(
+ application: &mut A,
+ compositor: &mut C,
+ runtime: &mut Runtime<E, Proxy<Event<A::Message>>, Event<A::Message>>,
+ clipboard: &mut Clipboard,
+ proxy: &mut winit::event_loop::EventLoopProxy<Event<A::Message>>,
+ debug: &mut Debug,
+ messages: &mut Vec<A::Message>,
+ windows: &mut Windows<A, C>,
+ ui_caches: &mut Vec<user_interface::Cache>,
+) where
+ C: Compositor<Renderer = A::Renderer> + 'static,
+ <A::Renderer as core::Renderer>::Theme: StyleSheet,
+{
+ for message in messages.drain(..) {
+ debug.log_message(&message);
+ debug.update_started();
+
+ let command = runtime.enter(|| application.update(message));
+ debug.update_finished();
+
+ run_command(
+ application,
+ compositor,
+ command,
+ runtime,
+ clipboard,
+ proxy,
+ debug,
+ windows,
+ ui_caches,
+ );
+ }
+
+ let subscription = application.subscription().map(Event::Application);
+ runtime.track(subscription.into_recipes());
+}
+
+/// Runs the actions of a [`Command`].
+pub fn run_command<A, C, E>(
+ application: &A,
+ compositor: &mut C,
+ command: Command<A::Message>,
+ runtime: &mut Runtime<E, Proxy<Event<A::Message>>, Event<A::Message>>,
+ clipboard: &mut Clipboard,
+ proxy: &mut winit::event_loop::EventLoopProxy<Event<A::Message>>,
+ debug: &mut Debug,
+ windows: &mut Windows<A, C>,
+ ui_caches: &mut Vec<user_interface::Cache>,
+) where
+ A: Application,
+ E: Executor,
+ C: Compositor<Renderer = A::Renderer> + 'static,
+ <A::Renderer as core::Renderer>::Theme: StyleSheet,
+{
+ use crate::runtime::clipboard;
+ use crate::runtime::system;
+ use crate::runtime::window;
+
+ for action in command.actions() {
+ match action {
+ command::Action::Future(future) => {
+ runtime.spawn(Box::pin(future.map(Event::Application)));
+ }
+ command::Action::Stream(stream) => {
+ runtime.run(Box::pin(stream.map(Event::Application)));
+ }
+ command::Action::Clipboard(action) => match action {
+ clipboard::Action::Read(tag) => {
+ let message = tag(clipboard.read());
+
+ proxy
+ .send_event(Event::Application(message))
+ .expect("Send message to event loop");
+ }
+ clipboard::Action::Write(contents) => {
+ clipboard.write(contents);
+ }
+ },
+ command::Action::Window(id, action) => match action {
+ window::Action::Spawn { settings } => {
+ let monitor = windows.last_monitor();
+
+ proxy
+ .send_event(Event::NewWindow {
+ id,
+ settings,
+ title: application.title(id),
+ monitor,
+ })
+ .expect("Send message to event loop");
+ }
+ window::Action::Close => {
+ proxy
+ .send_event(Event::CloseWindow(id))
+ .expect("Send message to event loop");
+ }
+ window::Action::Drag => {
+ let _ = windows.with_raw(id).drag_window();
+ }
+ window::Action::Resize(size) => {
+ windows.with_raw(id).set_inner_size(
+ winit::dpi::LogicalSize {
+ width: size.width,
+ height: size.height,
+ },
+ );
+ }
+ window::Action::FetchSize(callback) => {
+ let window = windows.with_raw(id);
+ let size = window.inner_size();
+
+ proxy
+ .send_event(Event::Application(callback(Size::new(
+ size.width,
+ size.height,
+ ))))
+ .expect("Send message to event loop")
+ }
+ window::Action::Maximize(maximized) => {
+ windows.with_raw(id).set_maximized(maximized);
+ }
+ window::Action::Minimize(minimized) => {
+ windows.with_raw(id).set_minimized(minimized);
+ }
+ window::Action::Move { x, y } => {
+ windows.with_raw(id).set_outer_position(
+ winit::dpi::LogicalPosition { x, y },
+ );
+ }
+ window::Action::ChangeMode(mode) => {
+ let window = windows.with_raw(id);
+ window.set_visible(conversion::visible(mode));
+ window.set_fullscreen(conversion::fullscreen(
+ window.current_monitor(),
+ mode,
+ ));
+ }
+ window::Action::ChangeIcon(icon) => {
+ windows.with_raw(id).set_window_icon(conversion::icon(icon))
+ }
+ window::Action::FetchMode(tag) => {
+ let window = windows.with_raw(id);
+ let mode = if window.is_visible().unwrap_or(true) {
+ conversion::mode(window.fullscreen())
+ } else {
+ core::window::Mode::Hidden
+ };
+
+ proxy
+ .send_event(Event::Application(tag(mode)))
+ .expect("Event loop doesn't exist.");
+ }
+ window::Action::ToggleMaximize => {
+ let window = windows.with_raw(id);
+ window.set_maximized(!window.is_maximized());
+ }
+ window::Action::ToggleDecorations => {
+ let window = windows.with_raw(id);
+ window.set_decorations(!window.is_decorated());
+ }
+ window::Action::RequestUserAttention(attention_type) => {
+ windows.with_raw(id).request_user_attention(
+ attention_type.map(conversion::user_attention),
+ );
+ }
+ window::Action::GainFocus => {
+ windows.with_raw(id).focus_window();
+ }
+ window::Action::ChangeLevel(level) => {
+ windows
+ .with_raw(id)
+ .set_window_level(conversion::window_level(level));
+ }
+ window::Action::FetchId(tag) => proxy
+ .send_event(Event::Application(tag(windows
+ .with_raw(id)
+ .id()
+ .into())))
+ .expect("Event loop doesn't exist."),
+ window::Action::Screenshot(tag) => {
+ let i = windows.index_from_id(id);
+ let state = &windows.states[i];
+ let surface = &mut windows.surfaces[i];
+ let renderer = &mut windows.renderers[i];
+
+ let bytes = compositor.screenshot(
+ renderer,
+ surface,
+ state.viewport(),
+ state.background_color(),
+ &debug.overlay(),
+ );
+
+ proxy
+ .send_event(Event::Application(tag(
+ window::Screenshot::new(
+ bytes,
+ state.physical_size(),
+ ),
+ )))
+ .expect("Event loop doesn't exist.")
+ }
+ },
+ command::Action::System(action) => match action {
+ system::Action::QueryInformation(_tag) => {
+ #[cfg(feature = "system")]
+ {
+ let graphics_info = compositor.fetch_information();
+ let proxy = proxy.clone();
+
+ let _ = std::thread::spawn(move || {
+ let information =
+ crate::system::information(graphics_info);
+
+ let message = _tag(information);
+
+ proxy
+ .send_event(Event::Application(message))
+ .expect("Event loop doesn't exist.")
+ });
+ }
+ }
+ },
+ command::Action::Widget(action) => {
+ let mut current_operation = Some(action);
+
+ let mut uis = build_user_interfaces(
+ application,
+ debug,
+ windows,
+ std::mem::take(ui_caches),
+ );
+
+ 'operate: while let Some(mut operation) =
+ current_operation.take()
+ {
+ for (i, ui) in uis.iter_mut().enumerate() {
+ ui.operate(&windows.renderers[i], operation.as_mut());
+
+ match operation.finish() {
+ operation::Outcome::None => {}
+ operation::Outcome::Some(message) => {
+ proxy
+ .send_event(Event::Application(message))
+ .expect("Event loop doesn't exist.");
+
+ // operation completed, don't need to try to operate on rest of UIs
+ break 'operate;
+ }
+ operation::Outcome::Chain(next) => {
+ current_operation = Some(next);
+ }
+ }
+ }
+ }
+
+ *ui_caches =
+ uis.drain(..).map(UserInterface::into_cache).collect();
+ }
+ command::Action::LoadFont { bytes, tagger } => {
+ use crate::core::text::Renderer;
+
+ // TODO change this once we change each renderer to having a single backend reference.. :pain:
+ // TODO: Error handling (?)
+ for renderer in &mut windows.renderers {
+ renderer.load_font(bytes.clone());
+ }
+
+ proxy
+ .send_event(Event::Application(tagger(Ok(()))))
+ .expect("Send message to event loop");
+ }
+ }
+ }
+}
+
+/// Build the user interface for every window.
+pub fn build_user_interfaces<'a, A: Application, C: Compositor>(
+ application: &'a A,
+ debug: &mut Debug,
+ windows: &mut Windows<A, C>,
+ mut cached_user_interfaces: Vec<user_interface::Cache>,
+) -> Vec<UserInterface<'a, A::Message, A::Renderer>>
+where
+ <A::Renderer as core::Renderer>::Theme: StyleSheet,
+ C: Compositor<Renderer = A::Renderer>,
+{
+ cached_user_interfaces
+ .drain(..)
+ .zip(
+ windows
+ .ids
+ .iter()
+ .zip(windows.states.iter().zip(windows.renderers.iter_mut())),
+ )
+ .fold(vec![], |mut uis, (cache, (id, (state, renderer)))| {
+ uis.push(build_user_interface(
+ application,
+ cache,
+ renderer,
+ state.logical_size(),
+ debug,
+ *id,
+ ));
+
+ uis
+ })
+}
+
+/// Returns true if the provided event should cause an [`Application`] to
+/// exit.
+pub fn user_force_quit(
+ event: &winit::event::WindowEvent<'_>,
+ _modifiers: winit::event::ModifiersState,
+) -> bool {
+ match event {
+ #[cfg(target_os = "macos")]
+ winit::event::WindowEvent::KeyboardInput {
+ input:
+ winit::event::KeyboardInput {
+ virtual_keycode: Some(winit::event::VirtualKeyCode::Q),
+ state: winit::event::ElementState::Pressed,
+ ..
+ },
+ ..
+ } if _modifiers.logo() => true,
+ _ => false,
+ }
+}
+
+fn logical_bounds_of(
+ window: &winit::window::Window,
+) -> Option<((i32, i32), Size<u32>)> {
+ let scale = window.scale_factor();
+ let pos = window
+ .inner_position()
+ .map(|pos| {
+ ((pos.x as f64 / scale) as i32, (pos.y as f64 / scale) as i32)
+ })
+ .ok()?;
+ let size = {
+ let size = window.inner_size();
+ Size::new(
+ (size.width as f64 / scale) as u32,
+ (size.height as f64 / scale) as u32,
+ )
+ };
+
+ Some((pos, size))
+}
+
+#[cfg(not(target_arch = "wasm32"))]
+mod platform {
+ pub fn run<T, F>(
+ mut event_loop: winit::event_loop::EventLoop<T>,
+ event_handler: F,
+ ) -> Result<(), super::Error>
+ where
+ F: 'static
+ + FnMut(
+ winit::event::Event<'_, T>,
+ &winit::event_loop::EventLoopWindowTarget<T>,
+ &mut winit::event_loop::ControlFlow,
+ ),
+ {
+ use winit::platform::run_return::EventLoopExtRunReturn;
+
+ let _ = event_loop.run_return(event_handler);
+
+ Ok(())
+ }
+}
+
+#[cfg(target_arch = "wasm32")]
+mod platform {
+ pub fn run<T, F>(
+ event_loop: winit::event_loop::EventLoop<T>,
+ event_handler: F,
+ ) -> !
+ where
+ F: 'static
+ + FnMut(
+ winit::event::Event<'_, T>,
+ &winit::event_loop::EventLoopWindowTarget<T>,
+ &mut winit::event_loop::ControlFlow,
+ ),
+ {
+ event_loop.run(event_handler)
+ }
+}
diff --git a/winit/src/multi_window/state.rs b/winit/src/multi_window/state.rs
new file mode 100644
index 00000000..f2741c3c
--- /dev/null
+++ b/winit/src/multi_window/state.rs
@@ -0,0 +1,242 @@
+use crate::conversion;
+use crate::core;
+use crate::core::{mouse, window};
+use crate::core::{Color, Size};
+use crate::graphics::Viewport;
+use crate::multi_window::Application;
+use crate::style::application;
+use std::fmt::{Debug, Formatter};
+
+use iced_style::application::StyleSheet;
+use winit::event::{Touch, WindowEvent};
+use winit::window::Window;
+
+/// The state of a multi-windowed [`Application`].
+pub struct State<A: Application>
+where
+ <A::Renderer as core::Renderer>::Theme: application::StyleSheet,
+{
+ title: String,
+ scale_factor: f64,
+ viewport: Viewport,
+ viewport_version: usize,
+ cursor_position: Option<winit::dpi::PhysicalPosition<f64>>,
+ modifiers: winit::event::ModifiersState,
+ theme: <A::Renderer as core::Renderer>::Theme,
+ appearance: application::Appearance,
+}
+
+impl<A: Application> Debug for State<A>
+where
+ <A::Renderer as core::Renderer>::Theme: application::StyleSheet,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("multi_window::State")
+ .field("title", &self.title)
+ .field("scale_factor", &self.scale_factor)
+ .field("viewport", &self.viewport)
+ .field("viewport_version", &self.viewport_version)
+ .field("cursor_position", &self.cursor_position)
+ .field("appearance", &self.appearance)
+ .finish()
+ }
+}
+
+impl<A: Application> State<A>
+where
+ <A::Renderer as core::Renderer>::Theme: application::StyleSheet,
+{
+ /// Creates a new [`State`] for the provided [`Application`]'s `window`.
+ pub fn new(
+ application: &A,
+ window_id: window::Id,
+ window: &Window,
+ ) -> Self {
+ let title = application.title(window_id);
+ let scale_factor = application.scale_factor(window_id);
+ let theme = application.theme(window_id);
+ let appearance = theme.appearance(&application.style());
+
+ let viewport = {
+ let physical_size = window.inner_size();
+
+ Viewport::with_physical_size(
+ Size::new(physical_size.width, physical_size.height),
+ window.scale_factor() * scale_factor,
+ )
+ };
+
+ Self {
+ title,
+ scale_factor,
+ viewport,
+ viewport_version: 0,
+ cursor_position: None,
+ modifiers: winit::event::ModifiersState::default(),
+ theme,
+ appearance,
+ }
+ }
+
+ /// Returns the current [`Viewport`] of the [`State`].
+ pub fn viewport(&self) -> &Viewport {
+ &self.viewport
+ }
+
+ /// Returns the version of the [`Viewport`] of the [`State`].
+ ///
+ /// The version is incremented every time the [`Viewport`] changes.
+ pub fn viewport_version(&self) -> usize {
+ self.viewport_version
+ }
+
+ /// Returns the physical [`Size`] of the [`Viewport`] of the [`State`].
+ pub fn physical_size(&self) -> Size<u32> {
+ self.viewport.physical_size()
+ }
+
+ /// Returns the logical [`Size`] of the [`Viewport`] of the [`State`].
+ pub fn logical_size(&self) -> Size<f32> {
+ self.viewport.logical_size()
+ }
+
+ /// Returns the current scale factor of the [`Viewport`] of the [`State`].
+ pub fn scale_factor(&self) -> f64 {
+ self.viewport.scale_factor()
+ }
+
+ /// Returns the current cursor position of the [`State`].
+ pub fn cursor(&self) -> mouse::Cursor {
+ self.cursor_position
+ .map(|cursor_position| {
+ conversion::cursor_position(
+ cursor_position,
+ self.viewport.scale_factor(),
+ )
+ })
+ .map(mouse::Cursor::Available)
+ .unwrap_or(mouse::Cursor::Unavailable)
+ }
+
+ /// Returns the current keyboard modifiers of the [`State`].
+ pub fn modifiers(&self) -> winit::event::ModifiersState {
+ self.modifiers
+ }
+
+ /// Returns the current theme of the [`State`].
+ pub fn theme(&self) -> &<A::Renderer as core::Renderer>::Theme {
+ &self.theme
+ }
+
+ /// Returns the current background [`Color`] of the [`State`].
+ pub fn background_color(&self) -> Color {
+ self.appearance.background_color
+ }
+
+ /// Returns the current text [`Color`] of the [`State`].
+ pub fn text_color(&self) -> Color {
+ self.appearance.text_color
+ }
+
+ /// Processes the provided window event and updates the [`State`] accordingly.
+ pub fn update(
+ &mut self,
+ window: &Window,
+ event: &WindowEvent<'_>,
+ _debug: &mut crate::runtime::Debug,
+ ) {
+ match event {
+ WindowEvent::Resized(new_size) => {
+ let size = Size::new(new_size.width, new_size.height);
+
+ self.viewport = Viewport::with_physical_size(
+ size,
+ window.scale_factor() * self.scale_factor,
+ );
+
+ self.viewport_version = self.viewport_version.wrapping_add(1);
+ }
+ WindowEvent::ScaleFactorChanged {
+ scale_factor: new_scale_factor,
+ new_inner_size,
+ } => {
+ let size =
+ Size::new(new_inner_size.width, new_inner_size.height);
+
+ self.viewport = Viewport::with_physical_size(
+ size,
+ new_scale_factor * self.scale_factor,
+ );
+
+ self.viewport_version = self.viewport_version.wrapping_add(1);
+ }
+ WindowEvent::CursorMoved { position, .. }
+ | WindowEvent::Touch(Touch {
+ location: position, ..
+ }) => {
+ self.cursor_position = Some(*position);
+ }
+ WindowEvent::CursorLeft { .. } => {
+ self.cursor_position = None;
+ }
+ WindowEvent::ModifiersChanged(new_modifiers) => {
+ self.modifiers = *new_modifiers;
+ }
+ #[cfg(feature = "debug")]
+ WindowEvent::KeyboardInput {
+ input:
+ winit::event::KeyboardInput {
+ virtual_keycode: Some(winit::event::VirtualKeyCode::F12),
+ state: winit::event::ElementState::Pressed,
+ ..
+ },
+ ..
+ } => _debug.toggle(),
+ _ => {}
+ }
+ }
+
+ /// Synchronizes the [`State`] with its [`Application`] and its respective
+ /// window.
+ ///
+ /// Normally, an [`Application`] should be synchronized with its [`State`]
+ /// and window after calling [`Application::update`].
+ ///
+ /// [`Application::update`]: crate::Program::update
+ pub fn synchronize(
+ &mut self,
+ application: &A,
+ window_id: window::Id,
+ window: &Window,
+ ) {
+ // Update window title
+ let new_title = application.title(window_id);
+
+ if self.title != new_title {
+ window.set_title(&new_title);
+ self.title = new_title;
+ }
+
+ // Update scale factor and size
+ let new_scale_factor = application.scale_factor(window_id);
+ let new_size = window.inner_size();
+ let current_size = self.viewport.physical_size();
+
+ if self.scale_factor != new_scale_factor
+ || (current_size.width, current_size.height)
+ != (new_size.width, new_size.height)
+ {
+ self.viewport = Viewport::with_physical_size(
+ Size::new(new_size.width, new_size.height),
+ window.scale_factor() * new_scale_factor,
+ );
+ self.viewport_version = self.viewport_version.wrapping_add(1);
+
+ self.scale_factor = new_scale_factor;
+ }
+
+ // Update theme and appearance
+ self.theme = application.theme(window_id);
+ self.appearance = self.theme.appearance(&application.style());
+ }
+}
diff --git a/winit/src/multi_window/windows.rs b/winit/src/multi_window/windows.rs
new file mode 100644
index 00000000..1f606b31
--- /dev/null
+++ b/winit/src/multi_window/windows.rs
@@ -0,0 +1,196 @@
+use crate::core::{window, Size};
+use crate::multi_window::{Application, State};
+use iced_graphics::Compositor;
+use iced_style::application::StyleSheet;
+use std::fmt::{Debug, Formatter};
+use winit::monitor::MonitorHandle;
+
+pub struct Windows<A: Application, C: Compositor>
+where
+ <A::Renderer as crate::core::Renderer>::Theme: StyleSheet,
+ C: Compositor<Renderer = A::Renderer>,
+{
+ pub ids: Vec<window::Id>,
+ pub raw: Vec<winit::window::Window>,
+ pub states: Vec<State<A>>,
+ pub viewport_versions: Vec<usize>,
+ pub exit_on_close_requested: Vec<bool>,
+ pub surfaces: Vec<C::Surface>,
+ pub renderers: Vec<A::Renderer>,
+ pub pending_destroy: Vec<(window::Id, winit::window::WindowId)>,
+}
+
+impl<A: Application, C: Compositor> Debug for Windows<A, C>
+where
+ <A::Renderer as crate::core::Renderer>::Theme: StyleSheet,
+ C: Compositor<Renderer = A::Renderer>,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("Windows")
+ .field("ids", &self.ids)
+ .field(
+ "raw",
+ &self
+ .raw
+ .iter()
+ .map(|raw| raw.id())
+ .collect::<Vec<winit::window::WindowId>>(),
+ )
+ .field("states", &self.states)
+ .field("viewport_versions", &self.viewport_versions)
+ .finish()
+ }
+}
+
+impl<A: Application, C: Compositor> Windows<A, C>
+where
+ <A::Renderer as crate::core::Renderer>::Theme: StyleSheet,
+ C: Compositor<Renderer = A::Renderer>,
+{
+ /// Creates a new [`Windows`] with a single `window::Id::MAIN` window.
+ pub fn new(
+ application: &A,
+ compositor: &mut C,
+ renderer: A::Renderer,
+ main: winit::window::Window,
+ exit_on_close_requested: bool,
+ ) -> Self {
+ let state = State::new(application, window::Id::MAIN, &main);
+ let viewport_version = state.viewport_version();
+ let physical_size = state.physical_size();
+ let surface = compositor.create_surface(
+ &main,
+ physical_size.width,
+ physical_size.height,
+ );
+
+ Self {
+ ids: vec![window::Id::MAIN],
+ raw: vec![main],
+ states: vec![state],
+ viewport_versions: vec![viewport_version],
+ exit_on_close_requested: vec![exit_on_close_requested],
+ surfaces: vec![surface],
+ renderers: vec![renderer],
+ pending_destroy: vec![],
+ }
+ }
+
+ /// Adds a new window to [`Windows`]. Returns the size of the newly created window in logical
+ /// pixels & the index of the window within [`Windows`].
+ pub fn add(
+ &mut self,
+ application: &A,
+ compositor: &mut C,
+ id: window::Id,
+ window: winit::window::Window,
+ exit_on_close_requested: bool,
+ ) -> (Size, usize) {
+ let state = State::new(application, id, &window);
+ let window_size = state.logical_size();
+ let viewport_version = state.viewport_version();
+ let physical_size = state.physical_size();
+ let surface = compositor.create_surface(
+ &window,
+ physical_size.width,
+ physical_size.height,
+ );
+ let renderer = compositor.renderer();
+
+ self.ids.push(id);
+ self.raw.push(window);
+ self.states.push(state);
+ self.exit_on_close_requested.push(exit_on_close_requested);
+ self.viewport_versions.push(viewport_version);
+ self.surfaces.push(surface);
+ self.renderers.push(renderer);
+
+ (window_size, self.ids.len() - 1)
+ }
+
+ pub fn is_empty(&self) -> bool {
+ self.ids.is_empty()
+ }
+
+ pub fn main(&self) -> &winit::window::Window {
+ &self.raw[0]
+ }
+
+ pub fn index_from_raw(&self, id: winit::window::WindowId) -> usize {
+ self.raw
+ .iter()
+ .position(|window| window.id() == id)
+ .expect("No raw window in multi_window::Windows")
+ }
+
+ pub fn index_from_id(&self, id: window::Id) -> usize {
+ self.ids
+ .iter()
+ .position(|window_id| *window_id == id)
+ .expect("No window in multi_window::Windows")
+ }
+
+ pub fn last_monitor(&self) -> Option<MonitorHandle> {
+ self.raw.last().and_then(|w| w.current_monitor())
+ }
+
+ pub fn last(&self) -> usize {
+ self.ids.len() - 1
+ }
+
+ pub fn with_raw(&self, id: window::Id) -> &winit::window::Window {
+ let i = self.index_from_id(id);
+ &self.raw[i]
+ }
+
+ /// Deletes the window with `id` from [`Windows`]. Returns the index that the window had.
+ pub fn delete(&mut self, id: window::Id) -> usize {
+ let i = self.index_from_id(id);
+
+ let id = self.ids.remove(i);
+ let window = self.raw.remove(i);
+ let _ = self.states.remove(i);
+ let _ = self.exit_on_close_requested.remove(i);
+ let _ = self.viewport_versions.remove(i);
+ let _ = self.surfaces.remove(i);
+
+ self.pending_destroy.push((id, window.id()));
+
+ i
+ }
+
+ /// Gets the winit `window` that is pending to be destroyed if it exists.
+ pub fn get_pending_destroy(
+ &mut self,
+ window: winit::window::WindowId,
+ ) -> window::Id {
+ let i = self
+ .pending_destroy
+ .iter()
+ .position(|(_, window_id)| window == *window_id)
+ .unwrap();
+
+ let (id, _) = self.pending_destroy.remove(i);
+ id
+ }
+
+ /// Returns the windows that need to be requested to closed, and also the windows that can be
+ /// closed immediately.
+ pub fn partition_close_requests(&self) -> (Vec<window::Id>, Vec<window::Id>) {
+ self.exit_on_close_requested.iter().enumerate().fold(
+ (vec![], vec![]),
+ |(mut close_immediately, mut needs_request_closed),
+ (i, close)| {
+ let id = self.ids[i];
+
+ if *close {
+ close_immediately.push(id);
+ } else {
+ needs_request_closed.push(id);
+ }
+
+ (close_immediately, needs_request_closed)
+ },
+ )
+ }
+}
diff --git a/winit/src/position.rs b/winit/src/position.rs
deleted file mode 100644
index c260c29e..00000000
--- a/winit/src/position.rs
+++ /dev/null
@@ -1,22 +0,0 @@
-/// The position of a window in a given screen.
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum Position {
- /// The platform-specific default position for a new window.
- Default,
- /// The window is completely centered on the screen.
- Centered,
- /// The window is positioned with specific coordinates: `(X, Y)`.
- ///
- /// When the decorations of the window are enabled, Windows 10 will add some
- /// invisible padding to the window. This padding gets included in the
- /// position. So if you have decorations enabled and want the window to be
- /// at (0, 0) you would have to set the position to
- /// `(PADDING_X, PADDING_Y)`.
- Specific(i32, i32),
-}
-
-impl Default for Position {
- fn default() -> Self {
- Self::Default
- }
-}
diff --git a/winit/src/settings.rs b/winit/src/settings.rs
index 599f33f1..dc0f65a5 100644
--- a/winit/src/settings.rs
+++ b/winit/src/settings.rs
@@ -1,40 +1,7 @@
//! Configure your application.
-#[cfg(target_os = "windows")]
-#[path = "settings/windows.rs"]
-mod platform;
-
-#[cfg(target_os = "macos")]
-#[path = "settings/macos.rs"]
-mod platform;
-
-#[cfg(target_os = "linux")]
-#[path = "settings/linux.rs"]
-mod platform;
-
-#[cfg(target_arch = "wasm32")]
-#[path = "settings/wasm.rs"]
-mod platform;
-
-#[cfg(not(any(
- target_os = "windows",
- target_os = "macos",
- target_os = "linux",
- target_arch = "wasm32"
-)))]
-#[path = "settings/other.rs"]
-mod platform;
-
-pub use platform::PlatformSpecific;
-
-use crate::conversion;
-use crate::core::window::{Icon, Level};
-use crate::Position;
-
-use winit::monitor::MonitorHandle;
-use winit::window::WindowBuilder;
+use crate::core::window;
use std::borrow::Cow;
-use std::fmt;
/// The settings of an application.
#[derive(Debug, Clone, Default)]
@@ -46,7 +13,7 @@ pub struct Settings<Flags> {
pub id: Option<String>,
/// The [`Window`] settings.
- pub window: Window,
+ pub window: window::Settings,
/// The data needed to initialize an [`Application`].
///
@@ -55,197 +22,4 @@ pub struct Settings<Flags> {
/// The fonts to load on boot.
pub fonts: Vec<Cow<'static, [u8]>>,
-
- /// Whether the [`Application`] should exit when the user requests the
- /// window to close (e.g. the user presses the close button).
- ///
- /// [`Application`]: crate::Application
- pub exit_on_close_request: bool,
-}
-
-/// The window settings of an application.
-#[derive(Clone)]
-pub struct Window {
- /// The size of the window.
- pub size: (u32, u32),
-
- /// The position of the window.
- pub position: Position,
-
- /// The minimum size of the window.
- pub min_size: Option<(u32, u32)>,
-
- /// The maximum size of the window.
- pub max_size: Option<(u32, u32)>,
-
- /// Whether the window should be visible or not.
- pub visible: bool,
-
- /// Whether the window should be resizable or not.
- pub resizable: bool,
-
- /// Whether the window should have a border, a title bar, etc.
- pub decorations: bool,
-
- /// Whether the window should be transparent.
- pub transparent: bool,
-
- /// The window [`Level`].
- pub level: Level,
-
- /// The window icon, which is also usually used in the taskbar
- pub icon: Option<Icon>,
-
- /// Platform specific settings.
- pub platform_specific: platform::PlatformSpecific,
-}
-
-impl fmt::Debug for Window {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("Window")
- .field("size", &self.size)
- .field("position", &self.position)
- .field("min_size", &self.min_size)
- .field("max_size", &self.max_size)
- .field("visible", &self.visible)
- .field("resizable", &self.resizable)
- .field("decorations", &self.decorations)
- .field("transparent", &self.transparent)
- .field("level", &self.level)
- .field("icon", &self.icon.is_some())
- .field("platform_specific", &self.platform_specific)
- .finish()
- }
-}
-
-impl Window {
- /// Converts the window settings into a `WindowBuilder` from `winit`.
- pub fn into_builder(
- self,
- title: &str,
- primary_monitor: Option<MonitorHandle>,
- _id: Option<String>,
- ) -> WindowBuilder {
- let mut window_builder = WindowBuilder::new();
-
- let (width, height) = self.size;
-
- window_builder = window_builder
- .with_title(title)
- .with_inner_size(winit::dpi::LogicalSize { width, height })
- .with_resizable(self.resizable)
- .with_enabled_buttons(if self.resizable {
- winit::window::WindowButtons::all()
- } else {
- winit::window::WindowButtons::CLOSE
- | winit::window::WindowButtons::MINIMIZE
- })
- .with_decorations(self.decorations)
- .with_transparent(self.transparent)
- .with_window_icon(self.icon.and_then(conversion::icon))
- .with_window_level(conversion::window_level(self.level))
- .with_visible(self.visible);
-
- if let Some(position) = conversion::position(
- primary_monitor.as_ref(),
- self.size,
- self.position,
- ) {
- window_builder = window_builder.with_position(position);
- }
-
- if let Some((width, height)) = self.min_size {
- window_builder = window_builder
- .with_min_inner_size(winit::dpi::LogicalSize { width, height });
- }
-
- if let Some((width, height)) = self.max_size {
- window_builder = window_builder
- .with_max_inner_size(winit::dpi::LogicalSize { width, height });
- }
-
- #[cfg(any(
- target_os = "dragonfly",
- target_os = "freebsd",
- target_os = "netbsd",
- target_os = "openbsd"
- ))]
- {
- // `with_name` is available on both `WindowBuilderExtWayland` and `WindowBuilderExtX11` and they do
- // exactly the same thing. We arbitrarily choose `WindowBuilderExtWayland` here.
- use ::winit::platform::wayland::WindowBuilderExtWayland;
-
- if let Some(id) = _id {
- window_builder = window_builder.with_name(id.clone(), id);
- }
- }
-
- #[cfg(target_os = "windows")]
- {
- use winit::platform::windows::WindowBuilderExtWindows;
- #[allow(unsafe_code)]
- unsafe {
- window_builder = window_builder
- .with_parent_window(self.platform_specific.parent);
- }
- window_builder = window_builder
- .with_drag_and_drop(self.platform_specific.drag_and_drop);
- }
-
- #[cfg(target_os = "macos")]
- {
- use winit::platform::macos::WindowBuilderExtMacOS;
-
- window_builder = window_builder
- .with_title_hidden(self.platform_specific.title_hidden)
- .with_titlebar_transparent(
- self.platform_specific.titlebar_transparent,
- )
- .with_fullsize_content_view(
- self.platform_specific.fullsize_content_view,
- );
- }
-
- #[cfg(target_os = "linux")]
- {
- #[cfg(feature = "x11")]
- {
- use winit::platform::x11::WindowBuilderExtX11;
-
- window_builder = window_builder.with_name(
- &self.platform_specific.application_id,
- &self.platform_specific.application_id,
- );
- }
- #[cfg(feature = "wayland")]
- {
- use winit::platform::wayland::WindowBuilderExtWayland;
-
- window_builder = window_builder.with_name(
- &self.platform_specific.application_id,
- &self.platform_specific.application_id,
- );
- }
- }
-
- window_builder
- }
-}
-
-impl Default for Window {
- fn default() -> Window {
- Window {
- size: (1024, 768),
- position: Position::default(),
- min_size: None,
- max_size: None,
- visible: true,
- resizable: true,
- decorations: true,
- transparent: false,
- level: Level::default(),
- icon: None,
- platform_specific: PlatformSpecific::default(),
- }
- }
}
diff --git a/winit/src/settings/linux.rs b/winit/src/settings/linux.rs
deleted file mode 100644
index 009b9d9e..00000000
--- a/winit/src/settings/linux.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-//! Platform specific settings for Linux.
-
-/// The platform specific window settings of an application.
-#[derive(Debug, Clone, PartialEq, Eq, Default)]
-pub struct PlatformSpecific {
- /// Sets the application id of the window.
- ///
- /// As a best practice, it is suggested to select an application id that match
- /// the basename of the application’s .desktop file.
- pub application_id: String,
-}
diff --git a/winit/src/settings/macos.rs b/winit/src/settings/macos.rs
deleted file mode 100644
index f86e63ad..00000000
--- a/winit/src/settings/macos.rs
+++ /dev/null
@@ -1,12 +0,0 @@
-//! Platform specific settings for macOS.
-
-/// The platform specific window settings of an application.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
-pub struct PlatformSpecific {
- /// Hides the window title.
- pub title_hidden: bool,
- /// Makes the titlebar transparent and allows the content to appear behind it.
- pub titlebar_transparent: bool,
- /// Makes the window content appear behind the titlebar.
- pub fullsize_content_view: bool,
-}
diff --git a/winit/src/settings/other.rs b/winit/src/settings/other.rs
deleted file mode 100644
index b1103f62..00000000
--- a/winit/src/settings/other.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-/// The platform specific window settings of an application.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
-pub struct PlatformSpecific;
diff --git a/winit/src/settings/wasm.rs b/winit/src/settings/wasm.rs
deleted file mode 100644
index 8e0f1bbc..00000000
--- a/winit/src/settings/wasm.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-//! Platform specific settings for WebAssembly.
-
-/// The platform specific window settings of an application.
-#[derive(Debug, Clone, PartialEq, Eq, Default)]
-pub struct PlatformSpecific {
- /// The identifier of a DOM element that will be replaced with the
- /// application.
- ///
- /// If set to `None`, the application will be appended to the HTML body.
- pub target: Option<String>,
-}
diff --git a/winit/src/settings/windows.rs b/winit/src/settings/windows.rs
deleted file mode 100644
index 45d753bd..00000000
--- a/winit/src/settings/windows.rs
+++ /dev/null
@@ -1,21 +0,0 @@
-//! Platform specific settings for Windows.
-use raw_window_handle::RawWindowHandle;
-
-/// The platform specific window settings of an application.
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub struct PlatformSpecific {
- /// Parent window
- pub parent: Option<RawWindowHandle>,
-
- /// Drag and drop support
- pub drag_and_drop: bool,
-}
-
-impl Default for PlatformSpecific {
- fn default() -> Self {
- Self {
- parent: None,
- drag_and_drop: true,
- }
- }
-}