iced_winit/window/
state.rs1use 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
12pub 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 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 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 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 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 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 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}