summaryrefslogtreecommitdiffstats
path: root/winit/src/program
diff options
context:
space:
mode:
Diffstat (limited to 'winit/src/program')
-rw-r--r--winit/src/program/state.rs239
-rw-r--r--winit/src/program/window_manager.rs157
2 files changed, 396 insertions, 0 deletions
diff --git a/winit/src/program/state.rs b/winit/src/program/state.rs
new file mode 100644
index 00000000..a7fa2788
--- /dev/null
+++ b/winit/src/program/state.rs
@@ -0,0 +1,239 @@
+use crate::conversion;
+use crate::core::{mouse, window};
+use crate::core::{Color, Size};
+use crate::graphics::Viewport;
+use crate::program::{self, Program};
+use std::fmt::{Debug, Formatter};
+
+use winit::event::{Touch, WindowEvent};
+use winit::window::Window;
+
+/// The state of a multi-windowed [`Program`].
+pub struct State<P: Program>
+where
+ P::Theme: program::DefaultStyle,
+{
+ title: String,
+ scale_factor: f64,
+ viewport: Viewport,
+ viewport_version: u64,
+ cursor_position: Option<winit::dpi::PhysicalPosition<f64>>,
+ modifiers: winit::keyboard::ModifiersState,
+ theme: P::Theme,
+ appearance: program::Appearance,
+}
+
+impl<P: Program> Debug for State<P>
+where
+ P::Theme: program::DefaultStyle,
+{
+ 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<P: Program> State<P>
+where
+ P::Theme: program::DefaultStyle,
+{
+ /// Creates a new [`State`] for the provided [`Program`]'s `window`.
+ pub fn new(
+ application: &P,
+ 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 = application.style(&theme);
+
+ 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) -> &P::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 [`Program`] and its respective
+ /// window.
+ ///
+ /// Normally, a [`Program`] should be synchronized with its [`State`]
+ /// and window after calling [`State::update`].
+ pub fn synchronize(
+ &mut self,
+ application: &P,
+ 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 = application.style(&self.theme);
+ }
+}
diff --git a/winit/src/program/window_manager.rs b/winit/src/program/window_manager.rs
new file mode 100644
index 00000000..fcbf79f6
--- /dev/null
+++ b/winit/src/program/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::program::{DefaultStyle, Program, State};
+
+use std::collections::BTreeMap;
+use std::sync::Arc;
+use winit::monitor::MonitorHandle;
+
+#[allow(missing_debug_implementations)]
+pub struct WindowManager<P, C>
+where
+ P: Program,
+ C: Compositor<Renderer = P::Renderer>,
+ P::Theme: DefaultStyle,
+{
+ aliases: BTreeMap<winit::window::WindowId, Id>,
+ entries: BTreeMap<Id, Window<P, C>>,
+}
+
+impl<P, C> WindowManager<P, C>
+where
+ P: Program,
+ C: Compositor<Renderer = P::Renderer>,
+ P::Theme: DefaultStyle,
+{
+ pub fn new() -> Self {
+ Self {
+ aliases: BTreeMap::new(),
+ entries: BTreeMap::new(),
+ }
+ }
+
+ pub fn insert(
+ &mut self,
+ id: Id,
+ window: Arc<winit::window::Window>,
+ application: &P,
+ compositor: &mut C,
+ exit_on_close_request: bool,
+ ) -> &mut Window<P, 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::None,
+ },
+ );
+
+ 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<P, C>)> {
+ self.entries.iter_mut().map(|(k, v)| (*k, v))
+ }
+
+ pub fn get_mut(&mut self, id: Id) -> Option<&mut Window<P, C>> {
+ self.entries.get_mut(&id)
+ }
+
+ pub fn get_mut_alias(
+ &mut self,
+ id: winit::window::WindowId,
+ ) -> Option<(Id, &mut Window<P, 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<P, C>> {
+ let window = self.entries.remove(&id)?;
+ let _ = self.aliases.remove(&window.raw.id());
+
+ Some(window)
+ }
+}
+
+impl<P, C> Default for WindowManager<P, C>
+where
+ P: Program,
+ C: Compositor<Renderer = P::Renderer>,
+ P::Theme: DefaultStyle,
+{
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+#[allow(missing_debug_implementations)]
+pub struct Window<P, C>
+where
+ P: Program,
+ C: Compositor<Renderer = P::Renderer>,
+ P::Theme: DefaultStyle,
+{
+ pub raw: Arc<winit::window::Window>,
+ pub state: State<P>,
+ pub viewport_version: u64,
+ pub exit_on_close_request: bool,
+ pub mouse_interaction: mouse::Interaction,
+ pub surface: C::Surface,
+ pub renderer: P::Renderer,
+}
+
+impl<P, C> Window<P, C>
+where
+ P: Program,
+ C: Compositor<Renderer = P::Renderer>,
+ P::Theme: DefaultStyle,
+{
+ 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)
+ }
+}