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 the window of a [`Program`].
13pub struct State<P: Program>
14where
15    P::Theme: theme::Base,
16{
17    title: String,
18    scale_factor: f32,
19    viewport: Viewport,
20    surface_version: u64,
21    cursor_position: Option<winit::dpi::PhysicalPosition<f64>>,
22    modifiers: winit::keyboard::ModifiersState,
23    theme: Option<P::Theme>,
24    theme_mode: theme::Mode,
25    default_theme: P::Theme,
26    style: theme::Style,
27}
28
29impl<P: Program> Debug for State<P>
30where
31    P::Theme: theme::Base,
32{
33    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
34        f.debug_struct("window::State")
35            .field("title", &self.title)
36            .field("scale_factor", &self.scale_factor)
37            .field("viewport", &self.viewport)
38            .field("cursor_position", &self.cursor_position)
39            .field("style", &self.style)
40            .finish()
41    }
42}
43
44impl<P: Program> State<P>
45where
46    P::Theme: theme::Base,
47{
48    /// Creates a new [`State`] for the provided [`Program`]'s `window`.
49    pub fn new(
50        program: &program::Instance<P>,
51        window_id: window::Id,
52        window: &Window,
53        system_theme: theme::Mode,
54    ) -> Self {
55        let title = program.title(window_id);
56        let scale_factor = program.scale_factor(window_id);
57        let theme = program.theme(window_id);
58        let theme_mode = theme.as_ref().map(theme::Base::mode).unwrap_or_default();
59        let default_theme = <P::Theme as theme::Base>::default(system_theme);
60        let style = program.style(theme.as_ref().unwrap_or(&default_theme));
61
62        let viewport = {
63            let physical_size = window.inner_size();
64
65            Viewport::with_physical_size(
66                Size::new(physical_size.width, physical_size.height),
67                window.scale_factor() as f32 * scale_factor,
68            )
69        };
70
71        Self {
72            title,
73            scale_factor,
74            viewport,
75            surface_version: 0,
76            cursor_position: None,
77            modifiers: winit::keyboard::ModifiersState::default(),
78            theme,
79            theme_mode,
80            default_theme,
81            style,
82        }
83    }
84
85    pub fn viewport(&self) -> &Viewport {
86        &self.viewport
87    }
88
89    pub fn surface_version(&self) -> u64 {
90        self.surface_version
91    }
92
93    pub fn physical_size(&self) -> Size<u32> {
94        self.viewport.physical_size()
95    }
96
97    pub fn logical_size(&self) -> Size<f32> {
98        self.viewport.logical_size()
99    }
100
101    pub fn scale_factor(&self) -> f32 {
102        self.viewport.scale_factor()
103    }
104
105    pub fn cursor(&self) -> mouse::Cursor {
106        self.cursor_position
107            .map(|cursor_position| {
108                conversion::cursor_position(cursor_position, self.viewport.scale_factor())
109            })
110            .map(mouse::Cursor::Available)
111            .unwrap_or(mouse::Cursor::Unavailable)
112    }
113
114    pub fn modifiers(&self) -> winit::keyboard::ModifiersState {
115        self.modifiers
116    }
117
118    pub fn theme(&self) -> &P::Theme {
119        self.theme.as_ref().unwrap_or(&self.default_theme)
120    }
121
122    pub fn theme_mode(&self) -> theme::Mode {
123        self.theme_mode
124    }
125
126    pub fn background_color(&self) -> Color {
127        self.style.background_color
128    }
129
130    pub fn text_color(&self) -> Color {
131        self.style.text_color
132    }
133
134    pub fn update(&mut self, program: &program::Instance<P>, window: &Window, event: &WindowEvent) {
135        match event {
136            WindowEvent::Resized(new_size) => {
137                let size = Size::new(new_size.width, new_size.height);
138
139                self.viewport = Viewport::with_physical_size(
140                    size,
141                    window.scale_factor() as f32 * self.scale_factor,
142                );
143                self.surface_version += 1;
144            }
145            WindowEvent::ScaleFactorChanged {
146                scale_factor: new_scale_factor,
147                ..
148            } => {
149                let size = self.viewport.physical_size();
150
151                self.viewport = Viewport::with_physical_size(
152                    size,
153                    *new_scale_factor as f32 * self.scale_factor,
154                );
155                self.surface_version += 1;
156            }
157            WindowEvent::CursorMoved { position, .. }
158            | WindowEvent::Touch(Touch {
159                location: position, ..
160            }) => {
161                self.cursor_position = Some(*position);
162            }
163            WindowEvent::CursorLeft { .. } => {
164                self.cursor_position = None;
165            }
166            WindowEvent::ModifiersChanged(new_modifiers) => {
167                self.modifiers = new_modifiers.state();
168            }
169            WindowEvent::ThemeChanged(theme) => {
170                self.default_theme =
171                    <P::Theme as theme::Base>::default(conversion::theme_mode(*theme));
172
173                if self.theme.is_none() {
174                    self.style = program.style(&self.default_theme);
175                    window.request_redraw();
176                }
177            }
178            _ => {}
179        }
180    }
181
182    pub fn synchronize(
183        &mut self,
184        program: &program::Instance<P>,
185        window_id: window::Id,
186        window: &Window,
187    ) {
188        // Update window title
189        let new_title = program.title(window_id);
190
191        if self.title != new_title {
192            window.set_title(&new_title);
193            self.title = new_title;
194        }
195
196        // Update scale factor
197        let new_scale_factor = program.scale_factor(window_id);
198
199        if self.scale_factor != new_scale_factor {
200            self.viewport = Viewport::with_physical_size(
201                self.viewport.physical_size(),
202                window.scale_factor() as f32 * new_scale_factor,
203            );
204
205            self.scale_factor = new_scale_factor;
206        }
207
208        // Update theme and appearance
209        self.theme = program.theme(window_id);
210        self.style = program.style(self.theme());
211
212        let new_mode = self
213            .theme
214            .as_ref()
215            .map(theme::Base::mode)
216            .unwrap_or_default();
217
218        if self.theme_mode != new_mode {
219            #[cfg(not(target_os = "linux"))]
220            {
221                window.set_theme(conversion::window_theme(new_mode));
222
223                // Assume the old mode matches the system one
224                // We will be notified otherwise
225                if new_mode == theme::Mode::None {
226                    self.default_theme = <P::Theme as theme::Base>::default(self.theme_mode);
227
228                    if self.theme.is_none() {
229                        self.style = program.style(&self.default_theme);
230                    }
231                }
232            }
233
234            #[cfg(target_os = "linux")]
235            {
236                // mundy always notifies system theme changes, so we
237                // just restore the default theme mode.
238                let new_mode = if new_mode == theme::Mode::None {
239                    theme::Base::mode(&self.default_theme)
240                } else {
241                    new_mode
242                };
243
244                window.set_theme(conversion::window_theme(new_mode));
245            }
246
247            self.theme_mode = new_mode;
248        }
249    }
250}