Refactor Windows abstraction into WindowManager

This commit is contained in:
Héctor Ramón Jiménez 2023-12-02 22:26:01 +01:00
parent 31cccd8f7b
commit 5c5e7653be
No known key found for this signature in database
GPG key ID: 7CC46565708259A7
4 changed files with 464 additions and 505 deletions

View file

@ -19,7 +19,7 @@ where
title: String,
scale_factor: f64,
viewport: Viewport,
viewport_version: usize,
viewport_version: u64,
cursor_position: Option<winit::dpi::PhysicalPosition<f64>>,
modifiers: winit::event::ModifiersState,
theme: <A::Renderer as core::Renderer>::Theme,
@ -86,7 +86,7 @@ where
/// Returns the version of the [`Viewport`] of the [`State`].
///
/// The version is incremented every time the [`Viewport`] changes.
pub fn viewport_version(&self) -> usize {
pub fn viewport_version(&self) -> u64 {
self.viewport_version
}

View file

@ -0,0 +1,156 @@
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 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: 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,
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: 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)
}
}

View file

@ -1,201 +0,0 @@
use crate::core::{window, Size};
use crate::graphics::Compositor;
use crate::multi_window::{Application, State};
use crate::style::application::StyleSheet;
use winit::monitor::MonitorHandle;
use std::fmt::{Debug, Formatter};
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(winit::window::Window::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.create_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(winit::window::Window::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)
},
)
}
}