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::dpi::LogicalPosition;
use winit::event::{Touch, WindowEvent};
use winit::window::Window;
pub struct State<P: Program>
where
P::Theme: program::DefaultStyle,
{
pub(crate) 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,
{
pub fn new(
application: &P,
window_id: window::Id,
window: &dyn 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.surface_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,
}
}
pub fn viewport(&self) -> &Viewport {
&self.viewport
}
pub fn viewport_version(&self) -> u64 {
self.viewport_version
}
pub fn physical_size(&self) -> Size<u32> {
self.viewport.physical_size()
}
pub fn logical_size(&self) -> Size<f32> {
self.viewport.logical_size()
}
pub fn scale_factor(&self) -> f64 {
self.viewport.scale_factor()
}
pub fn set_logical_cursor_pos(&mut self, pos: LogicalPosition<f64>) {
let physical = pos.to_physical(self.scale_factor());
self.cursor_position = Some(physical);
}
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)
}
pub fn modifiers(&self) -> winit::keyboard::ModifiersState {
self.modifiers
}
pub fn theme(&self) -> &P::Theme {
&self.theme
}
pub fn background_color(&self) -> Color {
self.appearance.background_color
}
pub fn text_color(&self) -> Color {
self.appearance.text_color
}
pub fn icon_color(&self) -> Color {
self.appearance.icon_color
}
pub(crate) fn update_scale_factor(&mut self, new_scale_factor: f64) {
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);
}
pub fn update(
&mut self,
window: &dyn Window,
event: &WindowEvent,
_debug: &mut crate::runtime::Debug,
) {
match event {
WindowEvent::SurfaceResized(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,
..
} => {
self.update_scale_factor(*new_scale_factor);
}
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(),
_ => {}
}
}
pub fn synchronize(
&mut self,
application: &P,
window_id: window::Id,
window: &dyn Window,
) {
let new_title = application.title(window_id);
if self.title != new_title {
window.set_title(&new_title);
self.title = new_title;
}
let new_scale_factor = application.scale_factor(window_id);
let mut new_size = window.surface_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)
&& !(new_size.width == 0 && new_size.height == 0)
{
if new_size.width == 0 {
new_size.width = current_size.width;
}
if new_size.height == 0 {
new_size.height = current_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;
}
self.theme = application.theme(window_id);
self.appearance = application.style(&self.theme);
}
}