summaryrefslogtreecommitdiffstats
path: root/winit/src/multi_window
diff options
context:
space:
mode:
Diffstat (limited to 'winit/src/multi_window')
-rw-r--r--winit/src/multi_window/state.rs242
-rw-r--r--winit/src/multi_window/windows.rs196
2 files changed, 438 insertions, 0 deletions
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)
+ },
+ )
+ }
+}