diff options
Diffstat (limited to 'winit/src/multi_window')
| -rw-r--r-- | winit/src/multi_window/state.rs | 242 | ||||
| -rw-r--r-- | winit/src/multi_window/window_manager.rs | 157 | 
2 files changed, 399 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..235771f4 --- /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: u64, +    cursor_position: Option<winit::dpi::PhysicalPosition<f64>>, +    modifiers: winit::keyboard::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::keyboard::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) -> u64 { +        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::keyboard::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, +                .. +            } => { +                let size = self.viewport.physical_size(); + +                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.state(); +            } +            #[cfg(feature = "debug")] +            WindowEvent::KeyboardInput { +                event: +                    winit::event::KeyEvent { +                        logical_key: +                            winit::keyboard::Key::Named( +                                winit::keyboard::NamedKey::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 [`State::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/window_manager.rs b/winit/src/multi_window/window_manager.rs new file mode 100644 index 00000000..9e15f9ea --- /dev/null +++ b/winit/src/multi_window/window_manager.rs @@ -0,0 +1,157 @@ +use crate::core::mouse; +use crate::core::window::Id; +use crate::core::{Point, Size}; +use crate::graphics::Compositor; +use crate::multi_window::{Application, State}; +use crate::style::application::StyleSheet; + +use std::collections::BTreeMap; +use std::sync::Arc; +use winit::monitor::MonitorHandle; + +#[allow(missing_debug_implementations)] +pub struct WindowManager<A: Application, C: Compositor> +where +    <A::Renderer as crate::core::Renderer>::Theme: StyleSheet, +    C: Compositor<Renderer = A::Renderer>, +{ +    aliases: BTreeMap<winit::window::WindowId, Id>, +    entries: BTreeMap<Id, Window<A, C>>, +} + +impl<A, C> WindowManager<A, C> +where +    A: Application, +    C: Compositor<Renderer = A::Renderer>, +    <A::Renderer as crate::core::Renderer>::Theme: StyleSheet, +{ +    pub fn new() -> Self { +        Self { +            aliases: BTreeMap::new(), +            entries: BTreeMap::new(), +        } +    } + +    pub fn insert( +        &mut self, +        id: Id, +        window: Arc<winit::window::Window>, +        application: &A, +        compositor: &mut C, +        exit_on_close_request: bool, +    ) -> &mut Window<A, C> { +        let state = State::new(application, id, &window); +        let viewport_version = state.viewport_version(); +        let physical_size = state.physical_size(); +        let surface = compositor.create_surface( +            window.clone(), +            physical_size.width, +            physical_size.height, +        ); +        let renderer = compositor.create_renderer(); + +        let _ = self.aliases.insert(window.id(), id); + +        let _ = self.entries.insert( +            id, +            Window { +                raw: window, +                state, +                viewport_version, +                exit_on_close_request, +                surface, +                renderer, +                mouse_interaction: mouse::Interaction::Idle, +            }, +        ); + +        self.entries +            .get_mut(&id) +            .expect("Get window that was just inserted") +    } + +    pub fn is_empty(&self) -> bool { +        self.entries.is_empty() +    } + +    pub fn iter_mut( +        &mut self, +    ) -> impl Iterator<Item = (Id, &mut Window<A, C>)> { +        self.entries.iter_mut().map(|(k, v)| (*k, v)) +    } + +    pub fn get_mut(&mut self, id: Id) -> Option<&mut Window<A, C>> { +        self.entries.get_mut(&id) +    } + +    pub fn get_mut_alias( +        &mut self, +        id: winit::window::WindowId, +    ) -> Option<(Id, &mut Window<A, C>)> { +        let id = self.aliases.get(&id).copied()?; + +        Some((id, self.get_mut(id)?)) +    } + +    pub fn last_monitor(&self) -> Option<MonitorHandle> { +        self.entries.values().last()?.raw.current_monitor() +    } + +    pub fn remove(&mut self, id: Id) -> Option<Window<A, C>> { +        let window = self.entries.remove(&id)?; +        let _ = self.aliases.remove(&window.raw.id()); + +        Some(window) +    } +} + +impl<A, C> Default for WindowManager<A, C> +where +    A: Application, +    C: Compositor<Renderer = A::Renderer>, +    <A::Renderer as crate::core::Renderer>::Theme: StyleSheet, +{ +    fn default() -> Self { +        Self::new() +    } +} + +#[allow(missing_debug_implementations)] +pub struct Window<A, C> +where +    A: Application, +    C: Compositor<Renderer = A::Renderer>, +    <A::Renderer as crate::core::Renderer>::Theme: StyleSheet, +{ +    pub raw: Arc<winit::window::Window>, +    pub state: State<A>, +    pub viewport_version: u64, +    pub exit_on_close_request: bool, +    pub mouse_interaction: mouse::Interaction, +    pub surface: C::Surface, +    pub renderer: A::Renderer, +} + +impl<A, C> Window<A, C> +where +    A: Application, +    C: Compositor<Renderer = A::Renderer>, +    <A::Renderer as crate::core::Renderer>::Theme: StyleSheet, +{ +    pub fn position(&self) -> Option<Point> { +        self.raw +            .inner_position() +            .ok() +            .map(|position| position.to_logical(self.raw.scale_factor())) +            .map(|position| Point { +                x: position.x, +                y: position.y, +            }) +    } + +    pub fn size(&self) -> Size { +        let size = self.raw.inner_size().to_logical(self.raw.scale_factor()); + +        Size::new(size.width, size.height) +    } +}  | 
