iced_winit/window/
state.rs

1use crate::conversion;
2use crate::core::{Color, Size};
3use crate::core::{mouse, theme, window};
4use crate::graphics::Viewport;
5use crate::program::{self, Program};
6
7use winit::event::{Touch, WindowEvent};
8use winit::window::Window;
9
10use std::fmt::{Debug, Formatter};
11
12/// The state of a multi-windowed [`Program`].
13pub struct State<P: Program>
14where
15    P::Theme: theme::Base,
16{
17    title: String,
18    scale_factor: f64,
19    viewport: Viewport,
20    viewport_version: u64,
21    cursor_position: Option<winit::dpi::PhysicalPosition<f64>>,
22    modifiers: winit::keyboard::ModifiersState,
23    theme: P::Theme,
24    style: theme::Style,
25}
26
27impl<P: Program> Debug for State<P>
28where
29    P::Theme: theme::Base,
30{
31    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
32        f.debug_struct("multi_window::State")
33            .field("title", &self.title)
34            .field("scale_factor", &self.scale_factor)
35            .field("viewport", &self.viewport)
36            .field("viewport_version", &self.viewport_version)
37            .field("cursor_position", &self.cursor_position)
38            .field("style", &self.style)
39            .finish()
40    }
41}
42
43impl<P: Program> State<P>
44where
45    P::Theme: theme::Base,
46{
47    /// Creates a new [`State`] for the provided [`Program`]'s `window`.
48    pub fn new(
49        program: &program::Instance<P>,
50        window_id: window::Id,
51        window: &Window,
52    ) -> Self {
53        let title = program.title(window_id);
54        let scale_factor = program.scale_factor(window_id);
55        let theme = program.theme(window_id);
56        let style = program.style(&theme);
57
58        let viewport = {
59            let physical_size = window.inner_size();
60
61            Viewport::with_physical_size(
62                Size::new(physical_size.width, physical_size.height),
63                window.scale_factor() * scale_factor,
64            )
65        };
66
67        Self {
68            title,
69            scale_factor,
70            viewport,
71            viewport_version: 0,
72            cursor_position: None,
73            modifiers: winit::keyboard::ModifiersState::default(),
74            theme,
75            style,
76        }
77    }
78
79    /// Returns the current [`Viewport`] of the [`State`].
80    pub fn viewport(&self) -> &Viewport {
81        &self.viewport
82    }
83
84    /// Returns the version of the [`Viewport`] of the [`State`].
85    ///
86    /// The version is incremented every time the [`Viewport`] changes.
87    pub fn viewport_version(&self) -> u64 {
88        self.viewport_version
89    }
90
91    /// Returns the physical [`Size`] of the [`Viewport`] of the [`State`].
92    pub fn physical_size(&self) -> Size<u32> {
93        self.viewport.physical_size()
94    }
95
96    /// Returns the logical [`Size`] of the [`Viewport`] of the [`State`].
97    pub fn logical_size(&self) -> Size<f32> {
98        self.viewport.logical_size()
99    }
100
101    /// Returns the current scale factor of the [`Viewport`] of the [`State`].
102    pub fn scale_factor(&self) -> f64 {
103        self.viewport.scale_factor()
104    }
105
106    /// Returns the current cursor position of the [`State`].
107    pub fn cursor(&self) -> mouse::Cursor {
108        self.cursor_position
109            .map(|cursor_position| {
110                conversion::cursor_position(
111                    cursor_position,
112                    self.viewport.scale_factor(),
113                )
114            })
115            .map(mouse::Cursor::Available)
116            .unwrap_or(mouse::Cursor::Unavailable)
117    }
118
119    /// Returns the current keyboard modifiers of the [`State`].
120    pub fn modifiers(&self) -> winit::keyboard::ModifiersState {
121        self.modifiers
122    }
123
124    /// Returns the current theme of the [`State`].
125    pub fn theme(&self) -> &P::Theme {
126        &self.theme
127    }
128
129    /// Returns the current background [`Color`] of the [`State`].
130    pub fn background_color(&self) -> Color {
131        self.style.background_color
132    }
133
134    /// Returns the current text [`Color`] of the [`State`].
135    pub fn text_color(&self) -> Color {
136        self.style.text_color
137    }
138
139    /// Processes the provided window event and updates the [`State`] accordingly.
140    pub fn update(&mut self, window: &Window, event: &WindowEvent) {
141        match event {
142            WindowEvent::Resized(new_size) => {
143                let size = Size::new(new_size.width, new_size.height);
144
145                self.viewport = Viewport::with_physical_size(
146                    size,
147                    window.scale_factor() * self.scale_factor,
148                );
149
150                self.viewport_version = self.viewport_version.wrapping_add(1);
151            }
152            WindowEvent::ScaleFactorChanged {
153                scale_factor: new_scale_factor,
154                ..
155            } => {
156                let size = self.viewport.physical_size();
157
158                self.viewport = Viewport::with_physical_size(
159                    size,
160                    new_scale_factor * self.scale_factor,
161                );
162
163                self.viewport_version = self.viewport_version.wrapping_add(1);
164            }
165            WindowEvent::CursorMoved { position, .. }
166            | WindowEvent::Touch(Touch {
167                location: position, ..
168            }) => {
169                self.cursor_position = Some(*position);
170            }
171            WindowEvent::CursorLeft { .. } => {
172                self.cursor_position = None;
173            }
174            WindowEvent::ModifiersChanged(new_modifiers) => {
175                self.modifiers = new_modifiers.state();
176            }
177            _ => {}
178        }
179    }
180
181    /// Synchronizes the [`State`] with its [`Program`] and its respective
182    /// window.
183    ///
184    /// Normally, a [`Program`] should be synchronized with its [`State`]
185    /// and window after calling [`State::update`].
186    pub fn synchronize(
187        &mut self,
188        program: &program::Instance<P>,
189        window_id: window::Id,
190        window: &Window,
191    ) {
192        // Update window title
193        let new_title = program.title(window_id);
194
195        if self.title != new_title {
196            window.set_title(&new_title);
197            self.title = new_title;
198        }
199
200        // Update scale factor and size
201        let new_scale_factor = program.scale_factor(window_id);
202        let new_size = window.inner_size();
203        let current_size = self.viewport.physical_size();
204
205        if self.scale_factor != new_scale_factor
206            || (current_size.width, current_size.height)
207                != (new_size.width, new_size.height)
208        {
209            self.viewport = Viewport::with_physical_size(
210                Size::new(new_size.width, new_size.height),
211                window.scale_factor() * new_scale_factor,
212            );
213            self.viewport_version = self.viewport_version.wrapping_add(1);
214
215            self.scale_factor = new_scale_factor;
216        }
217
218        // Update theme and appearance
219        self.theme = program.theme(window_id);
220        self.style = program.style(&self.theme);
221    }
222}