refactored window storage;

new helper window events (Destroyed, Created);
clippy + fmt;
This commit is contained in:
Bingus 2023-07-12 19:21:05 -07:00
parent 633f405f3f
commit d53ccc857d
No known key found for this signature in database
GPG key ID: 5F84D2AA40A9F170
56 changed files with 1508 additions and 1819 deletions

View file

@ -12,8 +12,6 @@ categories = ["gui"]
[features]
default = ["x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"]
trace = ["tracing", "tracing-core", "tracing-subscriber"]
chrome-trace = ["trace", "tracing-chrome"]
debug = ["iced_runtime/debug"]
system = ["sysinfo"]
application = []
@ -21,7 +19,7 @@ x11 = ["winit/x11"]
wayland = ["winit/wayland"]
wayland-dlopen = ["winit/wayland-dlopen"]
wayland-csd-adwaita = ["winit/wayland-csd-adwaita"]
multi-window = []
multi-window = ["iced_runtime/multi-window"]
[dependencies]
window_clipboard = "0.3"
@ -47,24 +45,6 @@ path = "../graphics"
version = "0.8"
path = "../style"
[dependencies.tracing]
version = "0.1.37"
optional = true
features = ["std"]
[dependencies.tracing-core]
version = "0.1.30"
optional = true
[dependencies.tracing-subscriber]
version = "0.3.16"
optional = true
features = ["registry"]
[dependencies.tracing-chrome]
version = "0.7.0"
optional = true
[target.'cfg(target_os = "windows")'.dependencies.winapi]
version = "0.3.6"

View file

@ -18,6 +18,7 @@ use crate::runtime::clipboard;
use crate::runtime::program::Program;
use crate::runtime::user_interface::{self, UserInterface};
use crate::runtime::{Command, Debug};
use crate::settings;
use crate::style::application::{Appearance, StyleSheet};
use crate::{Clipboard, Error, Proxy, Settings};
@ -25,11 +26,6 @@ use futures::channel::mpsc;
use std::mem::ManuallyDrop;
#[cfg(feature = "trace")]
pub use crate::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
@ -117,15 +113,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();
@ -146,14 +136,13 @@ 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 builder = settings::window_builder(
settings.window,
&application.title(),
event_loop.primary_monitor(),
settings.id,
)
.with_visible(false);
log::debug!("Window builder: {:#?}", builder);
@ -196,28 +185,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,
settings.exit_on_close_request,
));
let mut context = task::Context::from_waker(task::noop_waker_ref());
@ -480,9 +461,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 {
@ -622,24 +600,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
@ -666,16 +632,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(
@ -750,7 +710,7 @@ pub fn run_command<A, C, E>(
}
window::Action::Spawn { .. } => {
log::info!(
"Spawning a window is only available with `multi_window::Application`s."
"Spawning a window is only available with multi-window applications."
)
}
window::Action::Resize(size) => {

View file

@ -7,7 +7,6 @@ use crate::core::mouse;
use crate::core::touch;
use crate::core::window;
use crate::core::{Event, Point};
use crate::Position;
/// Converts a winit window event into an iced event.
pub fn window_event(
@ -169,17 +168,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();

View file

@ -1,63 +0,0 @@
//! Attach an icon to the window of your application.
pub use crate::core::window::icon::*;
use crate::core::window::icon;
use std::io;
#[cfg(feature = "image")]
use std::path::Path;
/// Creates an icon from an image file.
///
/// This will return an error in case the file is missing at run-time. You may prefer [`Self::from_file_data`] instead.
#[cfg(feature = "image")]
pub fn from_file<P: AsRef<Path>>(icon_path: P) -> Result<Icon, Error> {
let icon = image_rs::io::Reader::open(icon_path)?.decode()?.to_rgba8();
Ok(icon::from_rgba(icon.to_vec(), icon.width(), icon.height())?)
}
/// Creates an icon from the content of an image file.
///
/// This content can be included in your application at compile-time, e.g. using the `include_bytes!` macro.
/// You can pass an explicit file format. Otherwise, the file format will be guessed at runtime.
#[cfg(feature = "image")]
pub fn from_file_data(
data: &[u8],
explicit_format: Option<image_rs::ImageFormat>,
) -> Result<Icon, Error> {
let mut icon = image_rs::io::Reader::new(std::io::Cursor::new(data));
let icon_with_format = match explicit_format {
Some(format) => {
icon.set_format(format);
icon
}
None => icon.with_guessed_format()?,
};
let pixels = icon_with_format.decode()?.to_rgba8();
Ok(icon::from_rgba(
pixels.to_vec(),
pixels.width(),
pixels.height(),
)?)
}
/// An error produced when creating an [`Icon`].
#[derive(Debug, thiserror::Error)]
pub enum Error {
/// The [`Icon`] is not valid.
#[error("The icon is invalid: {0}")]
InvalidError(#[from] icon::Error),
/// The underlying OS failed to create the icon.
#[error("The underlying OS failted to create the window icon: {0}")]
OsError(#[from] io::Error),
/// The `image` crate reported an error.
#[cfg(feature = "image")]
#[error("Unable to create icon from a file: {0}")]
ImageError(#[from] image_rs::error::ImageError),
}

View file

@ -51,20 +51,14 @@ pub mod settings;
pub mod system;
mod error;
mod icon;
mod proxy;
#[cfg(feature = "trace")]
mod profiler;
#[cfg(feature = "application")]
pub use application::Application;
#[cfg(feature = "trace")]
pub use profiler::Profiler;
pub use clipboard::Clipboard;
pub use error::Error;
pub use icon::Icon;
pub use proxy::Proxy;
pub use settings::Settings;
pub use crate::core::window::*;
pub use iced_graphics::Viewport;
pub use iced_native::window::Position;

File diff suppressed because it is too large Load diff

View file

@ -1,33 +1,50 @@
use crate::application::{self, StyleSheet as _};
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::window;
use crate::{Color, Debug, Point, Size, Viewport};
use crate::style::application;
use std::fmt::{Debug, Formatter};
use std::marker::PhantomData;
use iced_style::application::StyleSheet;
use winit::event::{Touch, WindowEvent};
use winit::window::Window;
/// The state of a multi-windowed [`Application`].
#[allow(missing_debug_implementations)]
pub struct State<A: Application>
where
<A::Renderer as crate::Renderer>::Theme: application::StyleSheet,
<A::Renderer as core::Renderer>::Theme: application::StyleSheet,
{
title: String,
scale_factor: f64,
viewport: Viewport,
viewport_changed: bool,
cursor_position: winit::dpi::PhysicalPosition<f64>,
viewport_version: usize,
cursor_position: Option<winit::dpi::PhysicalPosition<f64>>,
modifiers: winit::event::ModifiersState,
theme: <A::Renderer as crate::Renderer>::Theme,
theme: <A::Renderer as core::Renderer>::Theme,
appearance: application::Appearance,
application: PhantomData<A>,
}
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 crate::Renderer>::Theme: application::StyleSheet,
<A::Renderer as core::Renderer>::Theme: application::StyleSheet,
{
/// Creates a new [`State`] for the provided [`Application`]'s `window`.
pub fn new(
@ -53,13 +70,11 @@ where
title,
scale_factor,
viewport,
viewport_changed: false,
// TODO: Encode cursor availability in the type-system
cursor_position: winit::dpi::PhysicalPosition::new(-1.0, -1.0),
viewport_version: 0,
cursor_position: None,
modifiers: winit::event::ModifiersState::default(),
theme,
appearance,
application: PhantomData,
}
}
@ -68,9 +83,11 @@ where
&self.viewport
}
/// Returns whether or not the viewport changed.
pub fn viewport_changed(&self) -> bool {
self.viewport_changed
/// 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`].
@ -89,11 +106,16 @@ where
}
/// Returns the current cursor position of the [`State`].
pub fn cursor_position(&self) -> Point {
conversion::cursor_position(
self.cursor_position,
self.viewport.scale_factor(),
)
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`].
@ -102,7 +124,7 @@ where
}
/// Returns the current theme of the [`State`].
pub fn theme(&self) -> &<A::Renderer as crate::Renderer>::Theme {
pub fn theme(&self) -> &<A::Renderer as core::Renderer>::Theme {
&self.theme
}
@ -121,7 +143,7 @@ where
&mut self,
window: &Window,
event: &WindowEvent<'_>,
_debug: &mut Debug,
_debug: &mut crate::runtime::Debug,
) {
match event {
WindowEvent::Resized(new_size) => {
@ -132,7 +154,7 @@ where
window.scale_factor() * self.scale_factor,
);
self.viewport_changed = true;
self.viewport_version = self.viewport_version.wrapping_add(1);
}
WindowEvent::ScaleFactorChanged {
scale_factor: new_scale_factor,
@ -146,18 +168,16 @@ where
new_scale_factor * self.scale_factor,
);
self.viewport_changed = true;
self.viewport_version = self.viewport_version.wrapping_add(1);
}
WindowEvent::CursorMoved { position, .. }
| WindowEvent::Touch(Touch {
location: position, ..
}) => {
self.cursor_position = *position;
self.cursor_position = Some(*position);
}
WindowEvent::CursorLeft { .. } => {
// TODO: Encode cursor availability in the type-system
self.cursor_position =
winit::dpi::PhysicalPosition::new(-1.0, -1.0);
self.cursor_position = None;
}
WindowEvent::ModifiersChanged(new_modifiers) => {
self.modifiers = *new_modifiers;
@ -197,16 +217,20 @@ where
self.title = new_title;
}
// Update scale factor
// 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 {
let size = window.inner_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(size.width, size.height),
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;
}

View file

@ -0,0 +1,170 @@
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 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,
) -> 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],
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,
) -> (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.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.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
}
}

View file

@ -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,
}
}
}

View file

@ -1,35 +1,10 @@
//! 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_arch = "wasm32")]
#[path = "settings/wasm.rs"]
mod platform;
#[cfg(not(any(
target_os = "windows",
target_os = "macos",
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 crate::core::window;
use winit::monitor::MonitorHandle;
use winit::window::WindowBuilder;
use std::fmt;
/// The settings of an application.
#[derive(Debug, Clone, Default)]
pub struct Settings<Flags> {
@ -40,7 +15,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`].
///
@ -50,166 +25,93 @@ pub struct Settings<Flags> {
/// Whether the [`Application`] should exit when the user requests the
/// window to close (e.g. the user presses the close button).
///
/// With a [`multi_window::Application`] this will instead be used to determine whether the
/// application should exit when the "main"" window is closed, i.e. the first window created on
/// app launch.
///
/// [`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),
/// Converts the window settings into a `WindowBuilder` from `winit`.
pub fn window_builder(
settings: window::Settings,
title: &str,
monitor: Option<MonitorHandle>,
_id: Option<String>,
) -> WindowBuilder {
let mut window_builder = WindowBuilder::new();
/// The position of the window.
pub position: Position,
let (width, height) = settings.size;
/// The minimum size of the window.
pub min_size: Option<(u32, u32)>,
window_builder = window_builder
.with_title(title)
.with_inner_size(winit::dpi::LogicalSize { width, height })
.with_resizable(settings.resizable)
.with_decorations(settings.decorations)
.with_transparent(settings.transparent)
.with_window_icon(settings.icon.and_then(conversion::icon))
.with_window_level(conversion::window_level(settings.level))
.with_visible(settings.visible);
/// 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()
if let Some(position) =
conversion::position(monitor.as_ref(), settings.size, settings.position)
{
window_builder = window_builder.with_position(position);
}
}
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();
if let Some((width, height)) = settings.min_size {
window_builder = window_builder
.with_min_inner_size(winit::dpi::LogicalSize { width, height });
}
let (width, height) = self.size;
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 = "linux",
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(title)
.with_inner_size(winit::dpi::LogicalSize { width, height })
.with_resizable(self.resizable)
.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 = "linux",
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,
);
}
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,
);
}
}
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: Default::default(),
}
}
window_builder
}

View file

@ -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,
}

View file

@ -1,3 +0,0 @@
/// The platform specific window settings of an application.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct PlatformSpecific;

View file

@ -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>,
}

View file

@ -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,
}
}
}

View file