pub struct Window { /* private fields */ }
Expand description
Represents a window.
The window is closed when dropped.
§Threading
This is Send + Sync
, meaning that it can be freely used from other
threads.
However, some platforms (macOS, Web and iOS) only allow user interface interactions on the main thread, so on those platforms, if you use the window from a thread other than the main, the code is scheduled to run on the main thread, and your thread may be blocked until that completes.
§Platform-specific
Web: The Window
, which is represented by a HTMLElementCanvas
, can
not be closed by dropping the Window
.
Implementations§
Source§impl Window
Base Window functions.
impl Window
Base Window functions.
Sourcepub fn default_attributes() -> WindowAttributes
pub fn default_attributes() -> WindowAttributes
Create a new WindowAttributes
which allows modifying the window’s attributes before
creation.
Examples found in repository?
More examples
3fn main() -> Result<(), impl std::error::Error> {
4 use std::collections::HashMap;
5
6 use winit::dpi::{LogicalPosition, LogicalSize, Position};
7 use winit::event::{ElementState, Event, KeyEvent, WindowEvent};
8 use winit::event_loop::{ActiveEventLoop, EventLoop};
9 use winit::raw_window_handle::HasRawWindowHandle;
10 use winit::window::Window;
11
12 #[path = "util/fill.rs"]
13 mod fill;
14
15 fn spawn_child_window(parent: &Window, event_loop: &ActiveEventLoop) -> Window {
16 let parent = parent.raw_window_handle().unwrap();
17 let mut window_attributes = Window::default_attributes()
18 .with_title("child window")
19 .with_inner_size(LogicalSize::new(200.0f32, 200.0f32))
20 .with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
21 .with_visible(true);
22 // `with_parent_window` is unsafe. Parent window must be a valid window.
23 window_attributes = unsafe { window_attributes.with_parent_window(Some(parent)) };
24
25 event_loop.create_window(window_attributes).unwrap()
26 }
27
28 let mut windows = HashMap::new();
29
30 let event_loop: EventLoop<()> = EventLoop::new().unwrap();
31 let mut parent_window_id = None;
32
33 event_loop.run(move |event: Event<()>, event_loop| {
34 match event {
35 Event::Resumed => {
36 let attributes = Window::default_attributes()
37 .with_title("parent window")
38 .with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
39 .with_inner_size(LogicalSize::new(640.0f32, 480.0f32));
40 let window = event_loop.create_window(attributes).unwrap();
41
42 parent_window_id = Some(window.id());
43
44 println!("Parent window id: {parent_window_id:?})");
45 windows.insert(window.id(), window);
46 },
47 Event::WindowEvent { window_id, event } => match event {
48 WindowEvent::CloseRequested => {
49 windows.clear();
50 event_loop.exit();
51 },
52 WindowEvent::CursorEntered { device_id: _ } => {
53 // On x11, println when the cursor entered in a window even if the child window
54 // is created by some key inputs.
55 // the child windows are always placed at (0, 0) with size (200, 200) in the
56 // parent window, so we also can see this log when we move
57 // the cursor around (200, 200) in parent window.
58 println!("cursor entered in the window {window_id:?}");
59 },
60 WindowEvent::KeyboardInput {
61 event: KeyEvent { state: ElementState::Pressed, .. },
62 ..
63 } => {
64 let parent_window = windows.get(&parent_window_id.unwrap()).unwrap();
65 let child_window = spawn_child_window(parent_window, event_loop);
66 let child_id = child_window.id();
67 println!("Child window created with id: {child_id:?}");
68 windows.insert(child_id, child_window);
69 },
70 WindowEvent::RedrawRequested => {
71 if let Some(window) = windows.get(&window_id) {
72 fill::fill_window(window);
73 }
74 },
75 _ => (),
76 },
77 _ => (),
78 }
79 })
80}
125 fn create_window(
126 &mut self,
127 event_loop: &ActiveEventLoop,
128 _tab_id: Option<String>,
129 ) -> Result<WindowId, Box<dyn Error>> {
130 // TODO read-out activation token.
131
132 #[allow(unused_mut)]
133 let mut window_attributes = Window::default_attributes()
134 .with_title("Winit window")
135 .with_transparent(true)
136 .with_window_icon(Some(self.icon.clone()));
137
138 #[cfg(any(x11_platform, wayland_platform))]
139 if let Some(token) = event_loop.read_token_from_env() {
140 startup_notify::reset_activation_token_env();
141 info!("Using token {:?} to activate a window", token);
142 window_attributes = window_attributes.with_activation_token(token);
143 }
144
145 #[cfg(x11_platform)]
146 match std::env::var("X11_VISUAL_ID") {
147 Ok(visual_id_str) => {
148 info!("Using X11 visual id {visual_id_str}");
149 let visual_id = visual_id_str.parse()?;
150 window_attributes = window_attributes.with_x11_visual(visual_id);
151 },
152 Err(_) => info!("Set the X11_VISUAL_ID env variable to request specific X11 visual"),
153 }
154
155 #[cfg(x11_platform)]
156 match std::env::var("X11_SCREEN_ID") {
157 Ok(screen_id_str) => {
158 info!("Placing the window on X11 screen {screen_id_str}");
159 let screen_id = screen_id_str.parse()?;
160 window_attributes = window_attributes.with_x11_screen(screen_id);
161 },
162 Err(_) => info!(
163 "Set the X11_SCREEN_ID env variable to place the window on non-default screen"
164 ),
165 }
166
167 #[cfg(macos_platform)]
168 if let Some(tab_id) = _tab_id {
169 window_attributes = window_attributes.with_tabbing_identifier(&tab_id);
170 }
171
172 #[cfg(web_platform)]
173 {
174 use winit::platform::web::WindowAttributesExtWebSys;
175 window_attributes = window_attributes.with_append(true);
176 }
177
178 let window = event_loop.create_window(window_attributes)?;
179
180 #[cfg(ios_platform)]
181 {
182 use winit::platform::ios::WindowExtIOS;
183 window.recognize_doubletap_gesture(true);
184 window.recognize_pinch_gesture(true);
185 window.recognize_rotation_gesture(true);
186 window.recognize_pan_gesture(true, 2, 2);
187 }
188
189 let window_state = WindowState::new(self, window)?;
190 let window_id = window_state.window.id();
191 info!("Created new window with id={window_id:?}");
192 self.windows.insert(window_id, window_state);
193 Ok(window_id)
194 }
Sourcepub fn id(&self) -> WindowId
pub fn id(&self) -> WindowId
Returns an identifier unique to the window.
Examples found in repository?
More examples
54 fn create_surface(
55 &mut self,
56 window: &Window,
57 ) -> &mut Surface<&'static Window, &'static Window> {
58 self.surfaces.entry(window.id()).or_insert_with(|| {
59 Surface::new(&self.context.borrow(), unsafe {
60 mem::transmute::<&'_ Window, &'static Window>(window)
61 })
62 .expect("Failed to create a softbuffer surface")
63 })
64 }
65
66 fn destroy_surface(&mut self, window: &Window) {
67 self.surfaces.remove(&window.id());
68 }
125 fn create_window(
126 &mut self,
127 event_loop: &ActiveEventLoop,
128 _tab_id: Option<String>,
129 ) -> Result<WindowId, Box<dyn Error>> {
130 // TODO read-out activation token.
131
132 #[allow(unused_mut)]
133 let mut window_attributes = Window::default_attributes()
134 .with_title("Winit window")
135 .with_transparent(true)
136 .with_window_icon(Some(self.icon.clone()));
137
138 #[cfg(any(x11_platform, wayland_platform))]
139 if let Some(token) = event_loop.read_token_from_env() {
140 startup_notify::reset_activation_token_env();
141 info!("Using token {:?} to activate a window", token);
142 window_attributes = window_attributes.with_activation_token(token);
143 }
144
145 #[cfg(x11_platform)]
146 match std::env::var("X11_VISUAL_ID") {
147 Ok(visual_id_str) => {
148 info!("Using X11 visual id {visual_id_str}");
149 let visual_id = visual_id_str.parse()?;
150 window_attributes = window_attributes.with_x11_visual(visual_id);
151 },
152 Err(_) => info!("Set the X11_VISUAL_ID env variable to request specific X11 visual"),
153 }
154
155 #[cfg(x11_platform)]
156 match std::env::var("X11_SCREEN_ID") {
157 Ok(screen_id_str) => {
158 info!("Placing the window on X11 screen {screen_id_str}");
159 let screen_id = screen_id_str.parse()?;
160 window_attributes = window_attributes.with_x11_screen(screen_id);
161 },
162 Err(_) => info!(
163 "Set the X11_SCREEN_ID env variable to place the window on non-default screen"
164 ),
165 }
166
167 #[cfg(macos_platform)]
168 if let Some(tab_id) = _tab_id {
169 window_attributes = window_attributes.with_tabbing_identifier(&tab_id);
170 }
171
172 #[cfg(web_platform)]
173 {
174 use winit::platform::web::WindowAttributesExtWebSys;
175 window_attributes = window_attributes.with_append(true);
176 }
177
178 let window = event_loop.create_window(window_attributes)?;
179
180 #[cfg(ios_platform)]
181 {
182 use winit::platform::ios::WindowExtIOS;
183 window.recognize_doubletap_gesture(true);
184 window.recognize_pinch_gesture(true);
185 window.recognize_rotation_gesture(true);
186 window.recognize_pan_gesture(true, 2, 2);
187 }
188
189 let window_state = WindowState::new(self, window)?;
190 let window_id = window_state.window.id();
191 info!("Created new window with id={window_id:?}");
192 self.windows.insert(window_id, window_state);
193 Ok(window_id)
194 }
195
196 fn handle_action(&mut self, event_loop: &ActiveEventLoop, window_id: WindowId, action: Action) {
197 // let cursor_position = self.cursor_position;
198 let window = self.windows.get_mut(&window_id).unwrap();
199 info!("Executing action: {action:?}");
200 match action {
201 Action::CloseWindow => {
202 let _ = self.windows.remove(&window_id);
203 },
204 Action::CreateNewWindow => {
205 #[cfg(any(x11_platform, wayland_platform))]
206 if let Err(err) = window.window.request_activation_token() {
207 info!("Failed to get activation token: {err}");
208 } else {
209 return;
210 }
211
212 if let Err(err) = self.create_window(event_loop, None) {
213 error!("Error creating new window: {err}");
214 }
215 },
216 Action::ToggleResizeIncrements => window.toggle_resize_increments(),
217 Action::ToggleCursorVisibility => window.toggle_cursor_visibility(),
218 Action::ToggleResizable => window.toggle_resizable(),
219 Action::ToggleDecorations => window.toggle_decorations(),
220 Action::ToggleFullscreen => window.toggle_fullscreen(),
221 Action::ToggleMaximize => window.toggle_maximize(),
222 Action::ToggleImeInput => window.toggle_ime(),
223 Action::Minimize => window.minimize(),
224 Action::NextCursor => window.next_cursor(),
225 Action::NextCustomCursor => window.next_custom_cursor(&self.custom_cursors),
226 #[cfg(web_platform)]
227 Action::UrlCustomCursor => window.url_custom_cursor(event_loop),
228 #[cfg(web_platform)]
229 Action::AnimationCustomCursor => {
230 window.animation_custom_cursor(event_loop, &self.custom_cursors)
231 },
232 Action::CycleCursorGrab => window.cycle_cursor_grab(),
233 Action::DragWindow => window.drag_window(),
234 Action::DragResizeWindow => window.drag_resize_window(),
235 Action::ShowWindowMenu => window.show_menu(),
236 Action::PrintHelp => self.print_help(),
237 #[cfg(macos_platform)]
238 Action::CycleOptionAsAlt => window.cycle_option_as_alt(),
239 Action::SetTheme(theme) => {
240 window.window.set_theme(theme);
241 // Get the resulting current theme to draw with
242 let actual_theme = theme.or_else(|| window.window.theme()).unwrap_or(Theme::Dark);
243 window.set_draw_theme(actual_theme);
244 },
245 #[cfg(macos_platform)]
246 Action::CreateNewTab => {
247 let tab_id = window.window.tabbing_identifier();
248 if let Err(err) = self.create_window(event_loop, Some(tab_id)) {
249 error!("Error creating new window: {err}");
250 }
251 },
252 Action::RequestResize => window.swap_dimensions(),
253 }
254 }
255
256 fn dump_monitors(&self, event_loop: &ActiveEventLoop) {
257 info!("Monitors information");
258 let primary_monitor = event_loop.primary_monitor();
259 for monitor in event_loop.available_monitors() {
260 let intro = if primary_monitor.as_ref() == Some(&monitor) {
261 "Primary monitor"
262 } else {
263 "Monitor"
264 };
265
266 if let Some(name) = monitor.name() {
267 info!("{intro}: {name}");
268 } else {
269 info!("{intro}: [no name]");
270 }
271
272 let PhysicalSize { width, height } = monitor.size();
273 info!(
274 " Current mode: {width}x{height}{}",
275 if let Some(m_hz) = monitor.refresh_rate_millihertz() {
276 format!(" @ {}.{} Hz", m_hz / 1000, m_hz % 1000)
277 } else {
278 String::new()
279 }
280 );
281
282 let PhysicalPosition { x, y } = monitor.position();
283 info!(" Position: {x},{y}");
284
285 info!(" Scale factor: {}", monitor.scale_factor());
286
287 info!(" Available modes (width x height x bit-depth):");
288 for mode in monitor.video_modes() {
289 let PhysicalSize { width, height } = mode.size();
290 let bits = mode.bit_depth();
291 let m_hz = mode.refresh_rate_millihertz();
292 info!(" {width}x{height}x{bits} @ {}.{} Hz", m_hz / 1000, m_hz % 1000);
293 }
294 }
295 }
296
297 /// Process the key binding.
298 fn process_key_binding(key: &str, mods: &ModifiersState) -> Option<Action> {
299 KEY_BINDINGS
300 .iter()
301 .find_map(|binding| binding.is_triggered_by(&key, mods).then_some(binding.action))
302 }
303
304 /// Process mouse binding.
305 fn process_mouse_binding(button: MouseButton, mods: &ModifiersState) -> Option<Action> {
306 MOUSE_BINDINGS
307 .iter()
308 .find_map(|binding| binding.is_triggered_by(&button, mods).then_some(binding.action))
309 }
310
311 fn print_help(&self) {
312 info!("Keyboard bindings:");
313 for binding in KEY_BINDINGS {
314 info!(
315 "{}{:<10} - {} ({})",
316 modifiers_to_string(binding.mods),
317 binding.trigger,
318 binding.action,
319 binding.action.help(),
320 );
321 }
322 info!("Mouse bindings:");
323 for binding in MOUSE_BINDINGS {
324 info!(
325 "{}{:<10} - {} ({})",
326 modifiers_to_string(binding.mods),
327 mouse_button_to_string(binding.trigger),
328 binding.action,
329 binding.action.help(),
330 );
331 }
332 }
333}
334
335impl ApplicationHandler<UserEvent> for Application {
336 fn user_event(&mut self, _event_loop: &ActiveEventLoop, event: UserEvent) {
337 info!("User event: {event:?}");
338 }
339
340 fn window_event(
341 &mut self,
342 event_loop: &ActiveEventLoop,
343 window_id: WindowId,
344 event: WindowEvent,
345 ) {
346 let window = match self.windows.get_mut(&window_id) {
347 Some(window) => window,
348 None => return,
349 };
350
351 match event {
352 WindowEvent::Resized(size) => {
353 window.resize(size);
354 },
355 WindowEvent::Focused(focused) => {
356 if focused {
357 info!("Window={window_id:?} focused");
358 } else {
359 info!("Window={window_id:?} unfocused");
360 }
361 },
362 WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
363 info!("Window={window_id:?} changed scale to {scale_factor}");
364 },
365 WindowEvent::ThemeChanged(theme) => {
366 info!("Theme changed to {theme:?}");
367 window.set_draw_theme(theme);
368 },
369 WindowEvent::RedrawRequested => {
370 if let Err(err) = window.draw() {
371 error!("Error drawing window: {err}");
372 }
373 },
374 WindowEvent::Occluded(occluded) => {
375 window.set_occluded(occluded);
376 },
377 WindowEvent::CloseRequested => {
378 info!("Closing Window={window_id:?}");
379 self.windows.remove(&window_id);
380 },
381 WindowEvent::ModifiersChanged(modifiers) => {
382 window.modifiers = modifiers.state();
383 info!("Modifiers changed to {:?}", window.modifiers);
384 },
385 WindowEvent::MouseWheel { delta, .. } => match delta {
386 MouseScrollDelta::LineDelta(x, y) => {
387 info!("Mouse wheel Line Delta: ({x},{y})");
388 },
389 MouseScrollDelta::PixelDelta(px) => {
390 info!("Mouse wheel Pixel Delta: ({},{})", px.x, px.y);
391 },
392 },
393 WindowEvent::KeyboardInput { event, is_synthetic: false, .. } => {
394 let mods = window.modifiers;
395
396 // Dispatch actions only on press.
397 if event.state.is_pressed() {
398 let action = if let Key::Character(ch) = event.logical_key.as_ref() {
399 Self::process_key_binding(&ch.to_uppercase(), &mods)
400 } else {
401 None
402 };
403
404 if let Some(action) = action {
405 self.handle_action(event_loop, window_id, action);
406 }
407 }
408 },
409 WindowEvent::MouseInput { button, state, .. } => {
410 let mods = window.modifiers;
411 if let Some(action) =
412 state.is_pressed().then(|| Self::process_mouse_binding(button, &mods)).flatten()
413 {
414 self.handle_action(event_loop, window_id, action);
415 }
416 },
417 WindowEvent::CursorLeft { .. } => {
418 info!("Cursor left Window={window_id:?}");
419 window.cursor_left();
420 },
421 WindowEvent::CursorMoved { position, .. } => {
422 info!("Moved cursor to {position:?}");
423 window.cursor_moved(position);
424 },
425 WindowEvent::ActivationTokenDone { token: _token, .. } => {
426 #[cfg(any(x11_platform, wayland_platform))]
427 {
428 startup_notify::set_activation_token_env(_token);
429 if let Err(err) = self.create_window(event_loop, None) {
430 error!("Error creating new window: {err}");
431 }
432 }
433 },
434 WindowEvent::Ime(event) => match event {
435 Ime::Enabled => info!("IME enabled for Window={window_id:?}"),
436 Ime::Preedit(text, caret_pos) => {
437 info!("Preedit: {}, with caret at {:?}", text, caret_pos);
438 },
439 Ime::Commit(text) => {
440 info!("Committed: {}", text);
441 },
442 Ime::Disabled => info!("IME disabled for Window={window_id:?}"),
443 },
444 WindowEvent::PinchGesture { delta, .. } => {
445 window.zoom += delta;
446 let zoom = window.zoom;
447 if delta > 0.0 {
448 info!("Zoomed in {delta:.5} (now: {zoom:.5})");
449 } else {
450 info!("Zoomed out {delta:.5} (now: {zoom:.5})");
451 }
452 },
453 WindowEvent::RotationGesture { delta, .. } => {
454 window.rotated += delta;
455 let rotated = window.rotated;
456 if delta > 0.0 {
457 info!("Rotated counterclockwise {delta:.5} (now: {rotated:.5})");
458 } else {
459 info!("Rotated clockwise {delta:.5} (now: {rotated:.5})");
460 }
461 },
462 WindowEvent::PanGesture { delta, phase, .. } => {
463 window.panned.x += delta.x;
464 window.panned.y += delta.y;
465 info!("Panned ({delta:?})) (now: {:?}), {phase:?}", window.panned);
466 },
467 WindowEvent::DoubleTapGesture { .. } => {
468 info!("Smart zoom");
469 },
470 WindowEvent::TouchpadPressure { .. }
471 | WindowEvent::HoveredFileCancelled
472 | WindowEvent::KeyboardInput { .. }
473 | WindowEvent::CursorEntered { .. }
474 | WindowEvent::AxisMotion { .. }
475 | WindowEvent::DroppedFile(_)
476 | WindowEvent::HoveredFile(_)
477 | WindowEvent::Destroyed
478 | WindowEvent::Touch(_)
479 | WindowEvent::Moved(_) => (),
480 }
481 }
482
483 fn device_event(
484 &mut self,
485 _event_loop: &ActiveEventLoop,
486 device_id: DeviceId,
487 event: DeviceEvent,
488 ) {
489 info!("Device {device_id:?} event: {event:?}");
490 }
491
492 fn resumed(&mut self, event_loop: &ActiveEventLoop) {
493 info!("Resumed the event loop");
494 self.dump_monitors(event_loop);
495
496 // Create initial window.
497 self.create_window(event_loop, None).expect("failed to create initial window");
498
499 self.print_help();
500 }
501
502 fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
503 if self.windows.is_empty() {
504 info!("No windows left, exiting...");
505 event_loop.exit();
506 }
507 }
508
509 #[cfg(not(any(android_platform, ios_platform)))]
510 fn exiting(&mut self, _event_loop: &ActiveEventLoop) {
511 // We must drop the context here.
512 self.context = None;
513 }
514}
515
516/// State of the window.
517struct WindowState {
518 /// IME input.
519 ime: bool,
520 /// Render surface.
521 ///
522 /// NOTE: This surface must be dropped before the `Window`.
523 #[cfg(not(any(android_platform, ios_platform)))]
524 surface: Surface<DisplayHandle<'static>, Arc<Window>>,
525 /// The actual winit Window.
526 window: Arc<Window>,
527 /// The window theme we're drawing with.
528 theme: Theme,
529 /// Cursor position over the window.
530 cursor_position: Option<PhysicalPosition<f64>>,
531 /// Window modifiers state.
532 modifiers: ModifiersState,
533 /// Occlusion state of the window.
534 occluded: bool,
535 /// Current cursor grab mode.
536 cursor_grab: CursorGrabMode,
537 /// The amount of zoom into window.
538 zoom: f64,
539 /// The amount of rotation of the window.
540 rotated: f32,
541 /// The amount of pan of the window.
542 panned: PhysicalPosition<f32>,
543
544 #[cfg(macos_platform)]
545 option_as_alt: OptionAsAlt,
546
547 // Cursor states.
548 named_idx: usize,
549 custom_idx: usize,
550 cursor_hidden: bool,
551}
552
553impl WindowState {
554 fn new(app: &Application, window: Window) -> Result<Self, Box<dyn Error>> {
555 let window = Arc::new(window);
556
557 // SAFETY: the surface is dropped before the `window` which provided it with handle, thus
558 // it doesn't outlive it.
559 #[cfg(not(any(android_platform, ios_platform)))]
560 let surface = Surface::new(app.context.as_ref().unwrap(), Arc::clone(&window))?;
561
562 let theme = window.theme().unwrap_or(Theme::Dark);
563 info!("Theme: {theme:?}");
564 let named_idx = 0;
565 window.set_cursor(CURSORS[named_idx]);
566
567 // Allow IME out of the box.
568 let ime = true;
569 window.set_ime_allowed(ime);
570
571 let size = window.inner_size();
572 let mut state = Self {
573 #[cfg(macos_platform)]
574 option_as_alt: window.option_as_alt(),
575 custom_idx: app.custom_cursors.len() - 1,
576 cursor_grab: CursorGrabMode::None,
577 named_idx,
578 #[cfg(not(any(android_platform, ios_platform)))]
579 surface,
580 window,
581 theme,
582 ime,
583 cursor_position: Default::default(),
584 cursor_hidden: Default::default(),
585 modifiers: Default::default(),
586 occluded: Default::default(),
587 rotated: Default::default(),
588 panned: Default::default(),
589 zoom: Default::default(),
590 };
591
592 state.resize(size);
593 Ok(state)
594 }
595
596 pub fn toggle_ime(&mut self) {
597 self.ime = !self.ime;
598 self.window.set_ime_allowed(self.ime);
599 if let Some(position) = self.ime.then_some(self.cursor_position).flatten() {
600 self.window.set_ime_cursor_area(position, PhysicalSize::new(20, 20));
601 }
602 }
603
604 pub fn minimize(&mut self) {
605 self.window.set_minimized(true);
606 }
607
608 pub fn cursor_moved(&mut self, position: PhysicalPosition<f64>) {
609 self.cursor_position = Some(position);
610 if self.ime {
611 self.window.set_ime_cursor_area(position, PhysicalSize::new(20, 20));
612 }
613 }
614
615 pub fn cursor_left(&mut self) {
616 self.cursor_position = None;
617 }
618
619 /// Toggle maximized.
620 fn toggle_maximize(&self) {
621 let maximized = self.window.is_maximized();
622 self.window.set_maximized(!maximized);
623 }
624
625 /// Toggle window decorations.
626 fn toggle_decorations(&self) {
627 let decorated = self.window.is_decorated();
628 self.window.set_decorations(!decorated);
629 }
630
631 /// Toggle window resizable state.
632 fn toggle_resizable(&self) {
633 let resizable = self.window.is_resizable();
634 self.window.set_resizable(!resizable);
635 }
636
637 /// Toggle cursor visibility
638 fn toggle_cursor_visibility(&mut self) {
639 self.cursor_hidden = !self.cursor_hidden;
640 self.window.set_cursor_visible(!self.cursor_hidden);
641 }
642
643 /// Toggle resize increments on a window.
644 fn toggle_resize_increments(&mut self) {
645 let new_increments = match self.window.resize_increments() {
646 Some(_) => None,
647 None => Some(LogicalSize::new(25.0, 25.0)),
648 };
649 info!("Had increments: {}", new_increments.is_none());
650 self.window.set_resize_increments(new_increments);
651 }
652
653 /// Toggle fullscreen.
654 fn toggle_fullscreen(&self) {
655 let fullscreen = if self.window.fullscreen().is_some() {
656 None
657 } else {
658 Some(Fullscreen::Borderless(None))
659 };
660
661 self.window.set_fullscreen(fullscreen);
662 }
663
664 /// Cycle through the grab modes ignoring errors.
665 fn cycle_cursor_grab(&mut self) {
666 self.cursor_grab = match self.cursor_grab {
667 CursorGrabMode::None => CursorGrabMode::Confined,
668 CursorGrabMode::Confined => CursorGrabMode::Locked,
669 CursorGrabMode::Locked => CursorGrabMode::None,
670 };
671 info!("Changing cursor grab mode to {:?}", self.cursor_grab);
672 if let Err(err) = self.window.set_cursor_grab(self.cursor_grab) {
673 error!("Error setting cursor grab: {err}");
674 }
675 }
676
677 #[cfg(macos_platform)]
678 fn cycle_option_as_alt(&mut self) {
679 self.option_as_alt = match self.option_as_alt {
680 OptionAsAlt::None => OptionAsAlt::OnlyLeft,
681 OptionAsAlt::OnlyLeft => OptionAsAlt::OnlyRight,
682 OptionAsAlt::OnlyRight => OptionAsAlt::Both,
683 OptionAsAlt::Both => OptionAsAlt::None,
684 };
685 info!("Setting option as alt {:?}", self.option_as_alt);
686 self.window.set_option_as_alt(self.option_as_alt);
687 }
688
689 /// Swap the window dimensions with `request_inner_size`.
690 fn swap_dimensions(&mut self) {
691 let old_inner_size = self.window.inner_size();
692 let mut inner_size = old_inner_size;
693
694 mem::swap(&mut inner_size.width, &mut inner_size.height);
695 info!("Requesting resize from {old_inner_size:?} to {inner_size:?}");
696
697 if let Some(new_inner_size) = self.window.request_inner_size(inner_size) {
698 if old_inner_size == new_inner_size {
699 info!("Inner size change got ignored");
700 } else {
701 self.resize(new_inner_size);
702 }
703 } else {
704 info!("Request inner size is asynchronous");
705 }
706 }
707
708 /// Pick the next cursor.
709 fn next_cursor(&mut self) {
710 self.named_idx = (self.named_idx + 1) % CURSORS.len();
711 info!("Setting cursor to \"{:?}\"", CURSORS[self.named_idx]);
712 self.window.set_cursor(Cursor::Icon(CURSORS[self.named_idx]));
713 }
714
715 /// Pick the next custom cursor.
716 fn next_custom_cursor(&mut self, custom_cursors: &[CustomCursor]) {
717 self.custom_idx = (self.custom_idx + 1) % custom_cursors.len();
718 let cursor = Cursor::Custom(custom_cursors[self.custom_idx].clone());
719 self.window.set_cursor(cursor);
720 }
721
722 /// Custom cursor from an URL.
723 #[cfg(web_platform)]
724 fn url_custom_cursor(&mut self, event_loop: &ActiveEventLoop) {
725 let cursor = event_loop.create_custom_cursor(url_custom_cursor());
726
727 self.window.set_cursor(cursor);
728 }
729
730 /// Custom cursor from a URL.
731 #[cfg(web_platform)]
732 fn animation_custom_cursor(
733 &mut self,
734 event_loop: &ActiveEventLoop,
735 custom_cursors: &[CustomCursor],
736 ) {
737 use std::time::Duration;
738 use winit::platform::web::CustomCursorExtWebSys;
739
740 let cursors = vec![
741 custom_cursors[0].clone(),
742 custom_cursors[1].clone(),
743 event_loop.create_custom_cursor(url_custom_cursor()),
744 ];
745 let cursor = CustomCursor::from_animation(Duration::from_secs(3), cursors).unwrap();
746 let cursor = event_loop.create_custom_cursor(cursor);
747
748 self.window.set_cursor(cursor);
749 }
750
751 /// Resize the window to the new size.
752 fn resize(&mut self, size: PhysicalSize<u32>) {
753 info!("Resized to {size:?}");
754 #[cfg(not(any(android_platform, ios_platform)))]
755 {
756 let (width, height) = match (NonZeroU32::new(size.width), NonZeroU32::new(size.height))
757 {
758 (Some(width), Some(height)) => (width, height),
759 _ => return,
760 };
761 self.surface.resize(width, height).expect("failed to resize inner buffer");
762 }
763 self.window.request_redraw();
764 }
765
766 /// Change the theme that things are drawn in.
767 fn set_draw_theme(&mut self, theme: Theme) {
768 self.theme = theme;
769 self.window.request_redraw();
770 }
771
772 /// Show window menu.
773 fn show_menu(&self) {
774 if let Some(position) = self.cursor_position {
775 self.window.show_window_menu(position);
776 }
777 }
778
779 /// Drag the window.
780 fn drag_window(&self) {
781 if let Err(err) = self.window.drag_window() {
782 info!("Error starting window drag: {err}");
783 } else {
784 info!("Dragging window Window={:?}", self.window.id());
785 }
786 }
787
788 /// Drag-resize the window.
789 fn drag_resize_window(&self) {
790 let position = match self.cursor_position {
791 Some(position) => position,
792 None => {
793 info!("Drag-resize requires cursor to be inside the window");
794 return;
795 },
796 };
797
798 let win_size = self.window.inner_size();
799 let border_size = BORDER_SIZE * self.window.scale_factor();
800
801 let x_direction = if position.x < border_size {
802 ResizeDirection::West
803 } else if position.x > (win_size.width as f64 - border_size) {
804 ResizeDirection::East
805 } else {
806 // Use arbitrary direction instead of None for simplicity.
807 ResizeDirection::SouthEast
808 };
809
810 let y_direction = if position.y < border_size {
811 ResizeDirection::North
812 } else if position.y > (win_size.height as f64 - border_size) {
813 ResizeDirection::South
814 } else {
815 // Use arbitrary direction instead of None for simplicity.
816 ResizeDirection::SouthEast
817 };
818
819 let direction = match (x_direction, y_direction) {
820 (ResizeDirection::West, ResizeDirection::North) => ResizeDirection::NorthWest,
821 (ResizeDirection::West, ResizeDirection::South) => ResizeDirection::SouthWest,
822 (ResizeDirection::West, _) => ResizeDirection::West,
823 (ResizeDirection::East, ResizeDirection::North) => ResizeDirection::NorthEast,
824 (ResizeDirection::East, ResizeDirection::South) => ResizeDirection::SouthEast,
825 (ResizeDirection::East, _) => ResizeDirection::East,
826 (_, ResizeDirection::South) => ResizeDirection::South,
827 (_, ResizeDirection::North) => ResizeDirection::North,
828 _ => return,
829 };
830
831 if let Err(err) = self.window.drag_resize_window(direction) {
832 info!("Error starting window drag-resize: {err}");
833 } else {
834 info!("Drag-resizing window Window={:?}", self.window.id());
835 }
836 }
837
838 /// Change window occlusion state.
839 fn set_occluded(&mut self, occluded: bool) {
840 self.occluded = occluded;
841 if !occluded {
842 self.window.request_redraw();
843 }
844 }
845
846 /// Draw the window contents.
847 #[cfg(not(any(android_platform, ios_platform)))]
848 fn draw(&mut self) -> Result<(), Box<dyn Error>> {
849 if self.occluded {
850 info!("Skipping drawing occluded window={:?}", self.window.id());
851 return Ok(());
852 }
853
854 const WHITE: u32 = 0xffffffff;
855 const DARK_GRAY: u32 = 0xff181818;
856
857 let color = match self.theme {
858 Theme::Light => WHITE,
859 Theme::Dark => DARK_GRAY,
860 };
861
862 let mut buffer = self.surface.buffer_mut()?;
863 buffer.fill(color);
864 self.window.pre_present_notify();
865 buffer.present()?;
866 Ok(())
867 }
3fn main() -> Result<(), impl std::error::Error> {
4 use std::collections::HashMap;
5
6 use winit::dpi::{LogicalPosition, LogicalSize, Position};
7 use winit::event::{ElementState, Event, KeyEvent, WindowEvent};
8 use winit::event_loop::{ActiveEventLoop, EventLoop};
9 use winit::raw_window_handle::HasRawWindowHandle;
10 use winit::window::Window;
11
12 #[path = "util/fill.rs"]
13 mod fill;
14
15 fn spawn_child_window(parent: &Window, event_loop: &ActiveEventLoop) -> Window {
16 let parent = parent.raw_window_handle().unwrap();
17 let mut window_attributes = Window::default_attributes()
18 .with_title("child window")
19 .with_inner_size(LogicalSize::new(200.0f32, 200.0f32))
20 .with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
21 .with_visible(true);
22 // `with_parent_window` is unsafe. Parent window must be a valid window.
23 window_attributes = unsafe { window_attributes.with_parent_window(Some(parent)) };
24
25 event_loop.create_window(window_attributes).unwrap()
26 }
27
28 let mut windows = HashMap::new();
29
30 let event_loop: EventLoop<()> = EventLoop::new().unwrap();
31 let mut parent_window_id = None;
32
33 event_loop.run(move |event: Event<()>, event_loop| {
34 match event {
35 Event::Resumed => {
36 let attributes = Window::default_attributes()
37 .with_title("parent window")
38 .with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
39 .with_inner_size(LogicalSize::new(640.0f32, 480.0f32));
40 let window = event_loop.create_window(attributes).unwrap();
41
42 parent_window_id = Some(window.id());
43
44 println!("Parent window id: {parent_window_id:?})");
45 windows.insert(window.id(), window);
46 },
47 Event::WindowEvent { window_id, event } => match event {
48 WindowEvent::CloseRequested => {
49 windows.clear();
50 event_loop.exit();
51 },
52 WindowEvent::CursorEntered { device_id: _ } => {
53 // On x11, println when the cursor entered in a window even if the child window
54 // is created by some key inputs.
55 // the child windows are always placed at (0, 0) with size (200, 200) in the
56 // parent window, so we also can see this log when we move
57 // the cursor around (200, 200) in parent window.
58 println!("cursor entered in the window {window_id:?}");
59 },
60 WindowEvent::KeyboardInput {
61 event: KeyEvent { state: ElementState::Pressed, .. },
62 ..
63 } => {
64 let parent_window = windows.get(&parent_window_id.unwrap()).unwrap();
65 let child_window = spawn_child_window(parent_window, event_loop);
66 let child_id = child_window.id();
67 println!("Child window created with id: {child_id:?}");
68 windows.insert(child_id, child_window);
69 },
70 WindowEvent::RedrawRequested => {
71 if let Some(window) = windows.get(&window_id) {
72 fill::fill_window(window);
73 }
74 },
75 _ => (),
76 },
77 _ => (),
78 }
79 })
80}
Sourcepub fn scale_factor(&self) -> f64
pub fn scale_factor(&self) -> f64
Returns the scale factor that can be used to map logical pixels to physical pixels, and vice versa.
Note that this value can change depending on user action (for example if the window is
moved to another screen); as such, tracking WindowEvent::ScaleFactorChanged
events is
the most robust way to track the DPI you need to use to draw.
This value may differ from MonitorHandle::scale_factor
.
See the dpi
crate for more information.
§Platform-specific
The scale factor is calculated differently on different platforms:
-
Windows: On Windows 8 and 10, per-monitor scaling is readily configured by users from the display settings. While users are free to select any option they want, they’re only given a selection of “nice” scale factors, i.e. 1.0, 1.25, 1.5… on Windows 7. The scale factor is global and changing it requires logging out. See this article for technical details.
-
macOS: Recent macOS versions allow the user to change the scaling factor for specific displays. When available, the user may pick a per-monitor scaling factor from a set of pre-defined settings. All “retina displays” have a scaling factor above 1.0 by default, but the specific value varies across devices.
-
X11: Many man-hours have been spent trying to figure out how to handle DPI in X11. Winit currently uses a three-pronged approach:
- Use the value in the
WINIT_X11_SCALE_FACTOR
environment variable if present. - If not present, use the value set in
Xft.dpi
in Xresources. - Otherwise, calculate the scale factor based on the millimeter monitor dimensions provided by XRandR.
If
WINIT_X11_SCALE_FACTOR
is set torandr
, it’ll ignore theXft.dpi
field and use the XRandR scaling method. Generally speaking, you should try to configure the standard system variables to do what you want before resorting toWINIT_X11_SCALE_FACTOR
. - Use the value in the
-
Wayland: The scale factor is suggested by the compositor for each window individually by using the wp-fractional-scale protocol if available. Falls back to integer-scale factors otherwise.
The monitor scale factor may differ from the window scale factor.
-
iOS: Scale factors are set by Apple to the value that best suits the device, and range from
1.0
to3.0
. See this article and this article for more information.This uses the underlying
UIView
’scontentScaleFactor
. -
Android: Scale factors are set by the manufacturer to the value that best suits the device, and range from
1.0
to4.0
. See this article for more information.This is currently unimplemented, and this function always returns 1.0.
-
Web: The scale factor is the ratio between CSS pixels and the physical device pixels. In other words, it is the value of
window.devicePixelRatio
. It is affected by both the screen scaling and the browser zoom level and can go below1.0
. -
Orbital: This is currently unimplemented, and this function always returns 1.0.
Examples found in repository?
789 fn drag_resize_window(&self) {
790 let position = match self.cursor_position {
791 Some(position) => position,
792 None => {
793 info!("Drag-resize requires cursor to be inside the window");
794 return;
795 },
796 };
797
798 let win_size = self.window.inner_size();
799 let border_size = BORDER_SIZE * self.window.scale_factor();
800
801 let x_direction = if position.x < border_size {
802 ResizeDirection::West
803 } else if position.x > (win_size.width as f64 - border_size) {
804 ResizeDirection::East
805 } else {
806 // Use arbitrary direction instead of None for simplicity.
807 ResizeDirection::SouthEast
808 };
809
810 let y_direction = if position.y < border_size {
811 ResizeDirection::North
812 } else if position.y > (win_size.height as f64 - border_size) {
813 ResizeDirection::South
814 } else {
815 // Use arbitrary direction instead of None for simplicity.
816 ResizeDirection::SouthEast
817 };
818
819 let direction = match (x_direction, y_direction) {
820 (ResizeDirection::West, ResizeDirection::North) => ResizeDirection::NorthWest,
821 (ResizeDirection::West, ResizeDirection::South) => ResizeDirection::SouthWest,
822 (ResizeDirection::West, _) => ResizeDirection::West,
823 (ResizeDirection::East, ResizeDirection::North) => ResizeDirection::NorthEast,
824 (ResizeDirection::East, ResizeDirection::South) => ResizeDirection::SouthEast,
825 (ResizeDirection::East, _) => ResizeDirection::East,
826 (_, ResizeDirection::South) => ResizeDirection::South,
827 (_, ResizeDirection::North) => ResizeDirection::North,
828 _ => return,
829 };
830
831 if let Err(err) = self.window.drag_resize_window(direction) {
832 info!("Error starting window drag-resize: {err}");
833 } else {
834 info!("Drag-resizing window Window={:?}", self.window.id());
835 }
836 }
Sourcepub fn request_redraw(&self)
pub fn request_redraw(&self)
Queues a WindowEvent::RedrawRequested
event to be emitted that aligns with the windowing
system drawing loop.
This is the strongly encouraged method of redrawing windows, as it can integrate with
OS-requested redraws (e.g. when a window gets resized). To improve the event delivery
consider using Window::pre_present_notify
as described in docs.
Applications should always aim to redraw whenever they receive a RedrawRequested
event.
There are no strong guarantees about when exactly a RedrawRequest
event will be emitted
with respect to other events, since the requirements can vary significantly between
windowing systems.
However as the event aligns with the windowing system drawing loop, it may not arrive in same or even next event loop iteration.
§Platform-specific
- Windows This API uses
RedrawWindow
to request aWM_PAINT
message andRedrawRequested
is emitted in sync with anyWM_PAINT
messages. - iOS: Can only be called on the main thread.
- Wayland: The events are aligned with the frame callbacks when
Window::pre_present_notify
is used. - Web:
WindowEvent::RedrawRequested
will be aligned with therequestAnimationFrame
.
Examples found in repository?
More examples
752 fn resize(&mut self, size: PhysicalSize<u32>) {
753 info!("Resized to {size:?}");
754 #[cfg(not(any(android_platform, ios_platform)))]
755 {
756 let (width, height) = match (NonZeroU32::new(size.width), NonZeroU32::new(size.height))
757 {
758 (Some(width), Some(height)) => (width, height),
759 _ => return,
760 };
761 self.surface.resize(width, height).expect("failed to resize inner buffer");
762 }
763 self.window.request_redraw();
764 }
765
766 /// Change the theme that things are drawn in.
767 fn set_draw_theme(&mut self, theme: Theme) {
768 self.theme = theme;
769 self.window.request_redraw();
770 }
771
772 /// Show window menu.
773 fn show_menu(&self) {
774 if let Some(position) = self.cursor_position {
775 self.window.show_window_menu(position);
776 }
777 }
778
779 /// Drag the window.
780 fn drag_window(&self) {
781 if let Err(err) = self.window.drag_window() {
782 info!("Error starting window drag: {err}");
783 } else {
784 info!("Dragging window Window={:?}", self.window.id());
785 }
786 }
787
788 /// Drag-resize the window.
789 fn drag_resize_window(&self) {
790 let position = match self.cursor_position {
791 Some(position) => position,
792 None => {
793 info!("Drag-resize requires cursor to be inside the window");
794 return;
795 },
796 };
797
798 let win_size = self.window.inner_size();
799 let border_size = BORDER_SIZE * self.window.scale_factor();
800
801 let x_direction = if position.x < border_size {
802 ResizeDirection::West
803 } else if position.x > (win_size.width as f64 - border_size) {
804 ResizeDirection::East
805 } else {
806 // Use arbitrary direction instead of None for simplicity.
807 ResizeDirection::SouthEast
808 };
809
810 let y_direction = if position.y < border_size {
811 ResizeDirection::North
812 } else if position.y > (win_size.height as f64 - border_size) {
813 ResizeDirection::South
814 } else {
815 // Use arbitrary direction instead of None for simplicity.
816 ResizeDirection::SouthEast
817 };
818
819 let direction = match (x_direction, y_direction) {
820 (ResizeDirection::West, ResizeDirection::North) => ResizeDirection::NorthWest,
821 (ResizeDirection::West, ResizeDirection::South) => ResizeDirection::SouthWest,
822 (ResizeDirection::West, _) => ResizeDirection::West,
823 (ResizeDirection::East, ResizeDirection::North) => ResizeDirection::NorthEast,
824 (ResizeDirection::East, ResizeDirection::South) => ResizeDirection::SouthEast,
825 (ResizeDirection::East, _) => ResizeDirection::East,
826 (_, ResizeDirection::South) => ResizeDirection::South,
827 (_, ResizeDirection::North) => ResizeDirection::North,
828 _ => return,
829 };
830
831 if let Err(err) = self.window.drag_resize_window(direction) {
832 info!("Error starting window drag-resize: {err}");
833 } else {
834 info!("Drag-resizing window Window={:?}", self.window.id());
835 }
836 }
837
838 /// Change window occlusion state.
839 fn set_occluded(&mut self, occluded: bool) {
840 self.occluded = occluded;
841 if !occluded {
842 self.window.request_redraw();
843 }
844 }
30 fn window_event(
31 &mut self,
32 event_loop: &ActiveEventLoop,
33 _window_id: WindowId,
34 event: WindowEvent,
35 ) {
36 println!("{event:?}");
37
38 let window = match self.window.as_ref() {
39 Some(window) => window,
40 None => return,
41 };
42
43 match event {
44 WindowEvent::CloseRequested => event_loop.exit(),
45 WindowEvent::RedrawRequested => {
46 fill::fill_window(window);
47 window.request_redraw();
48 },
49 _ => (),
50 }
51 }
125 fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
126 if self.request_redraw && !self.wait_cancelled && !self.close_requested {
127 self.window.as_ref().unwrap().request_redraw();
128 }
129
130 match self.mode {
131 Mode::Wait => event_loop.set_control_flow(ControlFlow::Wait),
132 Mode::WaitUntil => {
133 if !self.wait_cancelled {
134 event_loop
135 .set_control_flow(ControlFlow::WaitUntil(time::Instant::now() + WAIT_TIME));
136 }
137 },
138 Mode::Poll => {
139 thread::sleep(POLL_SLEEP_TIME);
140 event_loop.set_control_flow(ControlFlow::Poll);
141 },
142 };
143
144 if self.close_requested {
145 event_loop.exit();
146 }
147 }
Sourcepub fn pre_present_notify(&self)
pub fn pre_present_notify(&self)
Notify the windowing system before presenting to the window.
You should call this event after your drawing operations, but before you submit
the buffer to the display or commit your drawings. Doing so will help winit to properly
schedule and make assumptions about its internal state. For example, it could properly
throttle WindowEvent::RedrawRequested
.
§Example
This example illustrates how it looks with OpenGL, but it applies to other graphics APIs and software rendering.
// Do the actual drawing with OpenGL.
// Notify winit that we're about to submit buffer to the windowing system.
window.pre_present_notify();
// Submit buffer to the windowing system.
swap_buffers();
§Platform-specific
- Android / iOS / X11 / Web / Windows / macOS / Orbital: Unsupported.
- Wayland: Schedules a frame callback to throttle
WindowEvent::RedrawRequested
.
Examples found in repository?
30 fn window_event(
31 &mut self,
32 event_loop: &ActiveEventLoop,
33 _window_id: WindowId,
34 event: WindowEvent,
35 ) {
36 let window = self.window.as_ref().unwrap();
37 match event {
38 WindowEvent::CloseRequested => event_loop.exit(),
39 WindowEvent::RedrawRequested => {
40 window.pre_present_notify();
41 fill::fill_window(window);
42 },
43 _ => (),
44 }
45 }
More examples
848 fn draw(&mut self) -> Result<(), Box<dyn Error>> {
849 if self.occluded {
850 info!("Skipping drawing occluded window={:?}", self.window.id());
851 return Ok(());
852 }
853
854 const WHITE: u32 = 0xffffffff;
855 const DARK_GRAY: u32 = 0xff181818;
856
857 let color = match self.theme {
858 Theme::Light => WHITE,
859 Theme::Dark => DARK_GRAY,
860 };
861
862 let mut buffer = self.surface.buffer_mut()?;
863 buffer.fill(color);
864 self.window.pre_present_notify();
865 buffer.present()?;
866 Ok(())
867 }
77 fn window_event(
78 &mut self,
79 _event_loop: &ActiveEventLoop,
80 _window_id: WindowId,
81 event: WindowEvent,
82 ) {
83 info!("{event:?}");
84
85 match event {
86 WindowEvent::CloseRequested => {
87 self.close_requested = true;
88 },
89 WindowEvent::KeyboardInput {
90 event: KeyEvent { logical_key: key, state: ElementState::Pressed, .. },
91 ..
92 } => match key.as_ref() {
93 // WARNING: Consider using `key_without_modifiers()` if available on your platform.
94 // See the `key_binding` example
95 Key::Character("1") => {
96 self.mode = Mode::Wait;
97 warn!("mode: {:?}", self.mode);
98 },
99 Key::Character("2") => {
100 self.mode = Mode::WaitUntil;
101 warn!("mode: {:?}", self.mode);
102 },
103 Key::Character("3") => {
104 self.mode = Mode::Poll;
105 warn!("mode: {:?}", self.mode);
106 },
107 Key::Character("r") => {
108 self.request_redraw = !self.request_redraw;
109 warn!("request_redraw: {}", self.request_redraw);
110 },
111 Key::Named(NamedKey::Escape) => {
112 self.close_requested = true;
113 },
114 _ => (),
115 },
116 WindowEvent::RedrawRequested => {
117 let window = self.window.as_ref().unwrap();
118 window.pre_present_notify();
119 fill::fill_window(window);
120 },
121 _ => (),
122 }
123 }
Sourcepub fn reset_dead_keys(&self)
pub fn reset_dead_keys(&self)
Reset the dead key state of the keyboard.
This is useful when a dead key is bound to trigger an action. Then this function can be called to reset the dead key state so that follow-up text input won’t be affected by the dead key.
§Platform-specific
- Web, macOS: Does nothing
Source§impl Window
Position and size functions.
impl Window
Position and size functions.
Sourcepub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError>
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError>
Returns the position of the top-left hand corner of the window’s client area relative to the top-left hand corner of the desktop.
The same conditions that apply to Window::outer_position
apply to this method.
§Platform-specific
- iOS: Can only be called on the main thread. Returns the top left coordinates of the window’s safe area in the screen space coordinate system.
- Web: Returns the top-left coordinates relative to the viewport. Note: this returns
the same value as
Window::outer_position
. - Android / Wayland: Always returns
NotSupportedError
.
Sourcepub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError>
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, NotSupportedError>
Returns the position of the top-left hand corner of the window relative to the top-left hand corner of the desktop.
Note that the top-left hand corner of the desktop is not necessarily the same as the screen. If the user uses a desktop with multiple monitors, the top-left hand corner of the desktop is the top-left hand corner of the monitor at the top-left of the desktop.
The coordinates can be negative if the top-left hand corner of the window is outside of the visible screen region.
§Platform-specific
- iOS: Can only be called on the main thread. Returns the top left coordinates of the window in the screen space coordinate system.
- Web: Returns the top-left coordinates relative to the viewport.
- Android / Wayland: Always returns
NotSupportedError
.
Sourcepub fn set_outer_position<P: Into<Position>>(&self, position: P)
pub fn set_outer_position<P: Into<Position>>(&self, position: P)
Modifies the position of the window.
See Window::outer_position
for more information about the coordinates.
This automatically un-maximizes the window if it’s maximized.
// Specify the position in logical dimensions like this:
window.set_outer_position(LogicalPosition::new(400.0, 200.0));
// Or specify the position in physical dimensions like this:
window.set_outer_position(PhysicalPosition::new(400, 200));
§Platform-specific
- iOS: Can only be called on the main thread. Sets the top left coordinates of the window in the screen space coordinate system.
- Web: Sets the top-left coordinates relative to the viewport. Doesn’t account for CSS
transform
. - Android / Wayland: Unsupported.
Sourcepub fn inner_size(&self) -> PhysicalSize<u32>
pub fn inner_size(&self) -> PhysicalSize<u32>
Returns the physical size of the window’s client area.
The client area is the content of the window, excluding the title bar and borders.
§Platform-specific
Examples found in repository?
71 pub fn fill_window(window: &Window) {
72 GC.with(|gc| {
73 let size = window.inner_size();
74 let (Some(width), Some(height)) =
75 (NonZeroU32::new(size.width), NonZeroU32::new(size.height))
76 else {
77 return;
78 };
79
80 // Either get the last context used or create a new one.
81 let mut gc = gc.borrow_mut();
82 let surface =
83 gc.get_or_insert_with(|| GraphicsContext::new(window)).create_surface(window);
84
85 // Fill a buffer with a solid color.
86 const DARK_GRAY: u32 = 0xff181818;
87
88 surface.resize(width, height).expect("Failed to resize the softbuffer surface");
89
90 let mut buffer = surface.buffer_mut().expect("Failed to get the softbuffer buffer");
91 buffer.fill(DARK_GRAY);
92 buffer.present().expect("Failed to present the softbuffer buffer");
93 })
94 }
More examples
554 fn new(app: &Application, window: Window) -> Result<Self, Box<dyn Error>> {
555 let window = Arc::new(window);
556
557 // SAFETY: the surface is dropped before the `window` which provided it with handle, thus
558 // it doesn't outlive it.
559 #[cfg(not(any(android_platform, ios_platform)))]
560 let surface = Surface::new(app.context.as_ref().unwrap(), Arc::clone(&window))?;
561
562 let theme = window.theme().unwrap_or(Theme::Dark);
563 info!("Theme: {theme:?}");
564 let named_idx = 0;
565 window.set_cursor(CURSORS[named_idx]);
566
567 // Allow IME out of the box.
568 let ime = true;
569 window.set_ime_allowed(ime);
570
571 let size = window.inner_size();
572 let mut state = Self {
573 #[cfg(macos_platform)]
574 option_as_alt: window.option_as_alt(),
575 custom_idx: app.custom_cursors.len() - 1,
576 cursor_grab: CursorGrabMode::None,
577 named_idx,
578 #[cfg(not(any(android_platform, ios_platform)))]
579 surface,
580 window,
581 theme,
582 ime,
583 cursor_position: Default::default(),
584 cursor_hidden: Default::default(),
585 modifiers: Default::default(),
586 occluded: Default::default(),
587 rotated: Default::default(),
588 panned: Default::default(),
589 zoom: Default::default(),
590 };
591
592 state.resize(size);
593 Ok(state)
594 }
595
596 pub fn toggle_ime(&mut self) {
597 self.ime = !self.ime;
598 self.window.set_ime_allowed(self.ime);
599 if let Some(position) = self.ime.then_some(self.cursor_position).flatten() {
600 self.window.set_ime_cursor_area(position, PhysicalSize::new(20, 20));
601 }
602 }
603
604 pub fn minimize(&mut self) {
605 self.window.set_minimized(true);
606 }
607
608 pub fn cursor_moved(&mut self, position: PhysicalPosition<f64>) {
609 self.cursor_position = Some(position);
610 if self.ime {
611 self.window.set_ime_cursor_area(position, PhysicalSize::new(20, 20));
612 }
613 }
614
615 pub fn cursor_left(&mut self) {
616 self.cursor_position = None;
617 }
618
619 /// Toggle maximized.
620 fn toggle_maximize(&self) {
621 let maximized = self.window.is_maximized();
622 self.window.set_maximized(!maximized);
623 }
624
625 /// Toggle window decorations.
626 fn toggle_decorations(&self) {
627 let decorated = self.window.is_decorated();
628 self.window.set_decorations(!decorated);
629 }
630
631 /// Toggle window resizable state.
632 fn toggle_resizable(&self) {
633 let resizable = self.window.is_resizable();
634 self.window.set_resizable(!resizable);
635 }
636
637 /// Toggle cursor visibility
638 fn toggle_cursor_visibility(&mut self) {
639 self.cursor_hidden = !self.cursor_hidden;
640 self.window.set_cursor_visible(!self.cursor_hidden);
641 }
642
643 /// Toggle resize increments on a window.
644 fn toggle_resize_increments(&mut self) {
645 let new_increments = match self.window.resize_increments() {
646 Some(_) => None,
647 None => Some(LogicalSize::new(25.0, 25.0)),
648 };
649 info!("Had increments: {}", new_increments.is_none());
650 self.window.set_resize_increments(new_increments);
651 }
652
653 /// Toggle fullscreen.
654 fn toggle_fullscreen(&self) {
655 let fullscreen = if self.window.fullscreen().is_some() {
656 None
657 } else {
658 Some(Fullscreen::Borderless(None))
659 };
660
661 self.window.set_fullscreen(fullscreen);
662 }
663
664 /// Cycle through the grab modes ignoring errors.
665 fn cycle_cursor_grab(&mut self) {
666 self.cursor_grab = match self.cursor_grab {
667 CursorGrabMode::None => CursorGrabMode::Confined,
668 CursorGrabMode::Confined => CursorGrabMode::Locked,
669 CursorGrabMode::Locked => CursorGrabMode::None,
670 };
671 info!("Changing cursor grab mode to {:?}", self.cursor_grab);
672 if let Err(err) = self.window.set_cursor_grab(self.cursor_grab) {
673 error!("Error setting cursor grab: {err}");
674 }
675 }
676
677 #[cfg(macos_platform)]
678 fn cycle_option_as_alt(&mut self) {
679 self.option_as_alt = match self.option_as_alt {
680 OptionAsAlt::None => OptionAsAlt::OnlyLeft,
681 OptionAsAlt::OnlyLeft => OptionAsAlt::OnlyRight,
682 OptionAsAlt::OnlyRight => OptionAsAlt::Both,
683 OptionAsAlt::Both => OptionAsAlt::None,
684 };
685 info!("Setting option as alt {:?}", self.option_as_alt);
686 self.window.set_option_as_alt(self.option_as_alt);
687 }
688
689 /// Swap the window dimensions with `request_inner_size`.
690 fn swap_dimensions(&mut self) {
691 let old_inner_size = self.window.inner_size();
692 let mut inner_size = old_inner_size;
693
694 mem::swap(&mut inner_size.width, &mut inner_size.height);
695 info!("Requesting resize from {old_inner_size:?} to {inner_size:?}");
696
697 if let Some(new_inner_size) = self.window.request_inner_size(inner_size) {
698 if old_inner_size == new_inner_size {
699 info!("Inner size change got ignored");
700 } else {
701 self.resize(new_inner_size);
702 }
703 } else {
704 info!("Request inner size is asynchronous");
705 }
706 }
707
708 /// Pick the next cursor.
709 fn next_cursor(&mut self) {
710 self.named_idx = (self.named_idx + 1) % CURSORS.len();
711 info!("Setting cursor to \"{:?}\"", CURSORS[self.named_idx]);
712 self.window.set_cursor(Cursor::Icon(CURSORS[self.named_idx]));
713 }
714
715 /// Pick the next custom cursor.
716 fn next_custom_cursor(&mut self, custom_cursors: &[CustomCursor]) {
717 self.custom_idx = (self.custom_idx + 1) % custom_cursors.len();
718 let cursor = Cursor::Custom(custom_cursors[self.custom_idx].clone());
719 self.window.set_cursor(cursor);
720 }
721
722 /// Custom cursor from an URL.
723 #[cfg(web_platform)]
724 fn url_custom_cursor(&mut self, event_loop: &ActiveEventLoop) {
725 let cursor = event_loop.create_custom_cursor(url_custom_cursor());
726
727 self.window.set_cursor(cursor);
728 }
729
730 /// Custom cursor from a URL.
731 #[cfg(web_platform)]
732 fn animation_custom_cursor(
733 &mut self,
734 event_loop: &ActiveEventLoop,
735 custom_cursors: &[CustomCursor],
736 ) {
737 use std::time::Duration;
738 use winit::platform::web::CustomCursorExtWebSys;
739
740 let cursors = vec![
741 custom_cursors[0].clone(),
742 custom_cursors[1].clone(),
743 event_loop.create_custom_cursor(url_custom_cursor()),
744 ];
745 let cursor = CustomCursor::from_animation(Duration::from_secs(3), cursors).unwrap();
746 let cursor = event_loop.create_custom_cursor(cursor);
747
748 self.window.set_cursor(cursor);
749 }
750
751 /// Resize the window to the new size.
752 fn resize(&mut self, size: PhysicalSize<u32>) {
753 info!("Resized to {size:?}");
754 #[cfg(not(any(android_platform, ios_platform)))]
755 {
756 let (width, height) = match (NonZeroU32::new(size.width), NonZeroU32::new(size.height))
757 {
758 (Some(width), Some(height)) => (width, height),
759 _ => return,
760 };
761 self.surface.resize(width, height).expect("failed to resize inner buffer");
762 }
763 self.window.request_redraw();
764 }
765
766 /// Change the theme that things are drawn in.
767 fn set_draw_theme(&mut self, theme: Theme) {
768 self.theme = theme;
769 self.window.request_redraw();
770 }
771
772 /// Show window menu.
773 fn show_menu(&self) {
774 if let Some(position) = self.cursor_position {
775 self.window.show_window_menu(position);
776 }
777 }
778
779 /// Drag the window.
780 fn drag_window(&self) {
781 if let Err(err) = self.window.drag_window() {
782 info!("Error starting window drag: {err}");
783 } else {
784 info!("Dragging window Window={:?}", self.window.id());
785 }
786 }
787
788 /// Drag-resize the window.
789 fn drag_resize_window(&self) {
790 let position = match self.cursor_position {
791 Some(position) => position,
792 None => {
793 info!("Drag-resize requires cursor to be inside the window");
794 return;
795 },
796 };
797
798 let win_size = self.window.inner_size();
799 let border_size = BORDER_SIZE * self.window.scale_factor();
800
801 let x_direction = if position.x < border_size {
802 ResizeDirection::West
803 } else if position.x > (win_size.width as f64 - border_size) {
804 ResizeDirection::East
805 } else {
806 // Use arbitrary direction instead of None for simplicity.
807 ResizeDirection::SouthEast
808 };
809
810 let y_direction = if position.y < border_size {
811 ResizeDirection::North
812 } else if position.y > (win_size.height as f64 - border_size) {
813 ResizeDirection::South
814 } else {
815 // Use arbitrary direction instead of None for simplicity.
816 ResizeDirection::SouthEast
817 };
818
819 let direction = match (x_direction, y_direction) {
820 (ResizeDirection::West, ResizeDirection::North) => ResizeDirection::NorthWest,
821 (ResizeDirection::West, ResizeDirection::South) => ResizeDirection::SouthWest,
822 (ResizeDirection::West, _) => ResizeDirection::West,
823 (ResizeDirection::East, ResizeDirection::North) => ResizeDirection::NorthEast,
824 (ResizeDirection::East, ResizeDirection::South) => ResizeDirection::SouthEast,
825 (ResizeDirection::East, _) => ResizeDirection::East,
826 (_, ResizeDirection::South) => ResizeDirection::South,
827 (_, ResizeDirection::North) => ResizeDirection::North,
828 _ => return,
829 };
830
831 if let Err(err) = self.window.drag_resize_window(direction) {
832 info!("Error starting window drag-resize: {err}");
833 } else {
834 info!("Drag-resizing window Window={:?}", self.window.id());
835 }
836 }
Sourcepub fn request_inner_size<S: Into<Size>>(
&self,
size: S,
) -> Option<PhysicalSize<u32>>
pub fn request_inner_size<S: Into<Size>>( &self, size: S, ) -> Option<PhysicalSize<u32>>
Request the new size for the window.
On platforms where the size is entirely controlled by the user the applied size will be returned immediately, resize event in such case may not be generated.
On platforms where resizing is disallowed by the windowing system, the current inner size is returned immediately, and the user one is ignored.
When None
is returned, it means that the request went to the display system,
and the actual size will be delivered later with the WindowEvent::Resized
.
See Window::inner_size
for more information about the values.
The request could automatically un-maximize the window if it’s maximized.
// Specify the size in logical dimensions like this:
let _ = window.request_inner_size(LogicalSize::new(400.0, 200.0));
// Or specify the size in physical dimensions like this:
let _ = window.request_inner_size(PhysicalSize::new(400, 200));
§Platform-specific
- Web: Sets the size of the canvas element. Doesn’t account for CSS
transform
.
Examples found in repository?
690 fn swap_dimensions(&mut self) {
691 let old_inner_size = self.window.inner_size();
692 let mut inner_size = old_inner_size;
693
694 mem::swap(&mut inner_size.width, &mut inner_size.height);
695 info!("Requesting resize from {old_inner_size:?} to {inner_size:?}");
696
697 if let Some(new_inner_size) = self.window.request_inner_size(inner_size) {
698 if old_inner_size == new_inner_size {
699 info!("Inner size change got ignored");
700 } else {
701 self.resize(new_inner_size);
702 }
703 } else {
704 info!("Request inner size is asynchronous");
705 }
706 }
Sourcepub fn outer_size(&self) -> PhysicalSize<u32>
pub fn outer_size(&self) -> PhysicalSize<u32>
Returns the physical size of the entire window.
These dimensions include the title bar and borders. If you don’t want that (and you usually
don’t), use Window::inner_size
instead.
§Platform-specific
- iOS: Can only be called on the main thread. Returns the
PhysicalSize
of the window in screen space coordinates. - Web: Returns the size of the canvas element. Note: this returns the same value as
Window::inner_size
.
Sourcepub fn set_min_inner_size<S: Into<Size>>(&self, min_size: Option<S>)
pub fn set_min_inner_size<S: Into<Size>>(&self, min_size: Option<S>)
Sets a minimum dimension size for the window.
// Specify the size in logical dimensions like this:
window.set_min_inner_size(Some(LogicalSize::new(400.0, 200.0)));
// Or specify the size in physical dimensions like this:
window.set_min_inner_size(Some(PhysicalSize::new(400, 200)));
§Platform-specific
- iOS / Android / Orbital: Unsupported.
Sourcepub fn set_max_inner_size<S: Into<Size>>(&self, max_size: Option<S>)
pub fn set_max_inner_size<S: Into<Size>>(&self, max_size: Option<S>)
Sets a maximum dimension size for the window.
// Specify the size in logical dimensions like this:
window.set_max_inner_size(Some(LogicalSize::new(400.0, 200.0)));
// Or specify the size in physical dimensions like this:
window.set_max_inner_size(Some(PhysicalSize::new(400, 200)));
§Platform-specific
- iOS / Android / Orbital: Unsupported.
Sourcepub fn resize_increments(&self) -> Option<PhysicalSize<u32>>
pub fn resize_increments(&self) -> Option<PhysicalSize<u32>>
Sourcepub fn set_resize_increments<S: Into<Size>>(&self, increments: Option<S>)
pub fn set_resize_increments<S: Into<Size>>(&self, increments: Option<S>)
Sets window resize increments.
This is a niche constraint hint usually employed by terminal emulators and other apps that need “blocky” resizes.
§Platform-specific
- macOS: Increments are converted to logical size and then macOS rounds them to whole numbers.
- Wayland: Not implemented.
- iOS / Android / Web / Orbital: Unsupported.
Source§impl Window
Misc. attribute functions.
impl Window
Misc. attribute functions.
Sourcepub fn set_transparent(&self, transparent: bool)
pub fn set_transparent(&self, transparent: bool)
Change the window transparency state.
This is just a hint that may not change anything about the window transparency, however doing a mismatch between the content of your window and this hint may result in visual artifacts.
The default value follows the WindowAttributes::with_transparent
.
§Platform-specific
- macOS: This will reset the window’s background color.
- Web / iOS / Android: Unsupported.
- X11: Can only be set while building the window, with
WindowAttributes::with_transparent
.
Sourcepub fn set_blur(&self, blur: bool)
pub fn set_blur(&self, blur: bool)
Change the window blur state.
If true
, this will make the transparent window background blurry.
§Platform-specific
- Android / iOS / X11 / Web / Windows: Unsupported.
- Wayland: Only works with org_kde_kwin_blur_manager protocol.
Sourcepub fn set_visible(&self, visible: bool)
pub fn set_visible(&self, visible: bool)
Modifies the window’s visibility.
If false
, this will hide the window. If true
, this will show the window.
§Platform-specific
- Android / Wayland / Web: Unsupported.
- iOS: Can only be called on the main thread.
Sourcepub fn is_visible(&self) -> Option<bool>
pub fn is_visible(&self) -> Option<bool>
Gets the window’s current visibility state.
None
means it couldn’t be determined, so it is not recommended to use this to drive your
rendering backend.
§Platform-specific
- X11: Not implemented.
- Wayland / iOS / Android / Web: Unsupported.
Sourcepub fn set_resizable(&self, resizable: bool)
pub fn set_resizable(&self, resizable: bool)
Sets whether the window is resizable or not.
Note that making the window unresizable doesn’t exempt you from handling
WindowEvent::Resized
, as that event can still be triggered by DPI scaling, entering
fullscreen mode, etc. Also, the window could still be resized by calling
Window::request_inner_size
.
§Platform-specific
This only has an effect on desktop platforms.
- X11: Due to a bug in XFCE, this has no effect on Xfwm.
- iOS / Android / Web: Unsupported.
Sourcepub fn is_resizable(&self) -> bool
pub fn is_resizable(&self) -> bool
Gets the window’s current resizable state.
§Platform-specific
- X11: Not implemented.
- iOS / Android / Web: Unsupported.
Sets the enabled window buttons.
§Platform-specific
- Wayland / X11 / Orbital: Not implemented.
- Web / iOS / Android: Unsupported.
Gets the enabled window buttons.
§Platform-specific
- Wayland / X11 / Orbital: Not implemented. Always returns
WindowButtons::all
. - Web / iOS / Android: Unsupported. Always returns
WindowButtons::all
.
Sourcepub fn set_minimized(&self, minimized: bool)
pub fn set_minimized(&self, minimized: bool)
Sets the window to minimized or back
§Platform-specific
- iOS / Android / Web / Orbital: Unsupported.
- Wayland: Un-minimize is unsupported.
Sourcepub fn is_minimized(&self) -> Option<bool>
pub fn is_minimized(&self) -> Option<bool>
Sourcepub fn set_maximized(&self, maximized: bool)
pub fn set_maximized(&self, maximized: bool)
Sourcepub fn is_maximized(&self) -> bool
pub fn is_maximized(&self) -> bool
Sourcepub fn set_fullscreen(&self, fullscreen: Option<Fullscreen>)
pub fn set_fullscreen(&self, fullscreen: Option<Fullscreen>)
Sets the window to fullscreen or back.
§Platform-specific
-
macOS:
Fullscreen::Exclusive
provides true exclusive mode with a video mode change. Caveat! macOS doesn’t provide task switching (or spaces!) while in exclusive fullscreen mode. This mode should be used when a video mode change is desired, but for a better user experience, borderless fullscreen might be preferred.Fullscreen::Borderless
provides a borderless fullscreen window on a separate space. This is the idiomatic way for fullscreen games to work on macOS. SeeWindowExtMacOs::set_simple_fullscreen
if separate spaces are not preferred.The dock and the menu bar are disabled in exclusive fullscreen mode.
-
iOS: Can only be called on the main thread.
-
Wayland: Does not support exclusive fullscreen mode and will no-op a request.
-
Windows: Screen saver is disabled in fullscreen mode.
-
Android / Orbital: Unsupported.
-
Web: Does nothing without a transient activation.
Sourcepub fn fullscreen(&self) -> Option<Fullscreen>
pub fn fullscreen(&self) -> Option<Fullscreen>
Gets the window’s current fullscreen state.
§Platform-specific
- iOS: Can only be called on the main thread.
- Android / Orbital: Will always return
None
. - Wayland: Can return
Borderless(None)
when there are no monitors. - Web: Can only return
None
orBorderless(None)
.
Sourcepub fn set_decorations(&self, decorations: bool)
pub fn set_decorations(&self, decorations: bool)
Turn window decorations on or off.
Enable/disable window decorations provided by the server or Winit. By default this is enabled. Note that fullscreen windows and windows on mobile and web platforms naturally do not have decorations.
§Platform-specific
- iOS / Android / Web: No effect.
Sourcepub fn is_decorated(&self) -> bool
pub fn is_decorated(&self) -> bool
Gets the window’s current decorations state.
Returns true
when windows are decorated (server-side or by Winit).
Also returns true
when no decorations are required (mobile, web).
§Platform-specific
- iOS / Android / Web: Always returns
true
.
Sourcepub fn set_window_level(&self, level: WindowLevel)
pub fn set_window_level(&self, level: WindowLevel)
Change the window level.
This is just a hint to the OS, and the system could ignore it.
See WindowLevel
for details.
Sourcepub fn set_window_icon(&self, window_icon: Option<Icon>)
pub fn set_window_icon(&self, window_icon: Option<Icon>)
Sets the window icon.
On Windows and X11, this is typically the small icon in the top-left corner of the titlebar.
§Platform-specific
-
iOS / Android / Web / Wayland / macOS / Orbital: Unsupported.
-
Windows: Sets
ICON_SMALL
. The base size for a window icon is 16x16, but it’s recommended to account for screen scaling and pick a multiple of that, i.e. 32x32. -
X11: Has no universal guidelines for icon sizes, so you’re at the whims of the WM. That said, it’s usually in the same ballpark as on Windows.
Sourcepub fn set_ime_cursor_area<P: Into<Position>, S: Into<Size>>(
&self,
position: P,
size: S,
)
pub fn set_ime_cursor_area<P: Into<Position>, S: Into<Size>>( &self, position: P, size: S, )
Set the IME cursor editing area, where the position
is the top left corner of that area
and size
is the size of this area starting from the position. An example of such area
could be a input field in the UI or line in the editor.
The windowing system could place a candidate box close to that area, but try to not obscure the specified area, so the user input to it stays visible.
The candidate box is the window / popup / overlay that allows you to select the desired characters. The look of this box may differ between input devices, even on the same platform.
(Apple’s official term is “candidate window”, see their chinese and japanese guides).
§Example
// Specify the position in logical dimensions like this:
window.set_ime_cursor_area(LogicalPosition::new(400.0, 200.0), LogicalSize::new(100, 100));
// Or specify the position in physical dimensions like this:
window.set_ime_cursor_area(PhysicalPosition::new(400, 200), PhysicalSize::new(100, 100));
§Platform-specific
- X11: - area is not supported, only position.
- iOS / Android / Web / Orbital: Unsupported.
Examples found in repository?
596 pub fn toggle_ime(&mut self) {
597 self.ime = !self.ime;
598 self.window.set_ime_allowed(self.ime);
599 if let Some(position) = self.ime.then_some(self.cursor_position).flatten() {
600 self.window.set_ime_cursor_area(position, PhysicalSize::new(20, 20));
601 }
602 }
603
604 pub fn minimize(&mut self) {
605 self.window.set_minimized(true);
606 }
607
608 pub fn cursor_moved(&mut self, position: PhysicalPosition<f64>) {
609 self.cursor_position = Some(position);
610 if self.ime {
611 self.window.set_ime_cursor_area(position, PhysicalSize::new(20, 20));
612 }
613 }
Sourcepub fn set_ime_allowed(&self, allowed: bool)
pub fn set_ime_allowed(&self, allowed: bool)
Sets whether the window should get IME events
When IME is allowed, the window will receive Ime
events, and during the
preedit phase the window will NOT get KeyboardInput
events. The window
should allow IME while it is expecting text input.
When IME is not allowed, the window won’t receive Ime
events, and will
receive KeyboardInput
events for every keypress instead. Not allowing
IME is useful for games for example.
IME is not allowed by default.
§Platform-specific
- macOS: IME must be enabled to receive text-input where dead-key sequences are combined.
- iOS / Android: This will show / hide the soft keyboard.
- Web / Orbital: Unsupported.
- X11: Enabling IME will disable dead keys reporting during compose.
Examples found in repository?
554 fn new(app: &Application, window: Window) -> Result<Self, Box<dyn Error>> {
555 let window = Arc::new(window);
556
557 // SAFETY: the surface is dropped before the `window` which provided it with handle, thus
558 // it doesn't outlive it.
559 #[cfg(not(any(android_platform, ios_platform)))]
560 let surface = Surface::new(app.context.as_ref().unwrap(), Arc::clone(&window))?;
561
562 let theme = window.theme().unwrap_or(Theme::Dark);
563 info!("Theme: {theme:?}");
564 let named_idx = 0;
565 window.set_cursor(CURSORS[named_idx]);
566
567 // Allow IME out of the box.
568 let ime = true;
569 window.set_ime_allowed(ime);
570
571 let size = window.inner_size();
572 let mut state = Self {
573 #[cfg(macos_platform)]
574 option_as_alt: window.option_as_alt(),
575 custom_idx: app.custom_cursors.len() - 1,
576 cursor_grab: CursorGrabMode::None,
577 named_idx,
578 #[cfg(not(any(android_platform, ios_platform)))]
579 surface,
580 window,
581 theme,
582 ime,
583 cursor_position: Default::default(),
584 cursor_hidden: Default::default(),
585 modifiers: Default::default(),
586 occluded: Default::default(),
587 rotated: Default::default(),
588 panned: Default::default(),
589 zoom: Default::default(),
590 };
591
592 state.resize(size);
593 Ok(state)
594 }
595
596 pub fn toggle_ime(&mut self) {
597 self.ime = !self.ime;
598 self.window.set_ime_allowed(self.ime);
599 if let Some(position) = self.ime.then_some(self.cursor_position).flatten() {
600 self.window.set_ime_cursor_area(position, PhysicalSize::new(20, 20));
601 }
602 }
Sourcepub fn set_ime_purpose(&self, purpose: ImePurpose)
pub fn set_ime_purpose(&self, purpose: ImePurpose)
Sets the IME purpose for the window using ImePurpose
.
§Platform-specific
- iOS / Android / Web / Windows / X11 / macOS / Orbital: Unsupported.
Sourcepub fn focus_window(&self)
pub fn focus_window(&self)
Brings the window to the front and sets input focus. Has no effect if the window is already in focus, minimized, or not visible.
This method steals input focus from other applications. Do not use this method unless you are certain that’s what the user wants. Focus stealing can cause an extremely disruptive user experience.
§Platform-specific
- iOS / Android / Wayland / Orbital: Unsupported.
Sourcepub fn has_focus(&self) -> bool
pub fn has_focus(&self) -> bool
Gets whether the window has keyboard focus.
This queries the same state information as WindowEvent::Focused
.
Sourcepub fn request_user_attention(&self, request_type: Option<UserAttentionType>)
pub fn request_user_attention(&self, request_type: Option<UserAttentionType>)
Requests user attention to the window, this has no effect if the application
is already focused. How requesting for user attention manifests is platform dependent,
see UserAttentionType
for details.
Providing None
will unset the request for user attention. Unsetting the request for
user attention might not be done automatically by the WM when the window receives input.
§Platform-specific
- iOS / Android / Web / Orbital: Unsupported.
- macOS:
None
has no effect. - X11: Requests for user attention must be manually cleared.
- Wayland: Requires
xdg_activation_v1
protocol,None
has no effect.
Sourcepub fn set_theme(&self, theme: Option<Theme>)
pub fn set_theme(&self, theme: Option<Theme>)
Set or override the window theme.
Specify None
to reset the theme to the system default.
§Platform-specific
- Wayland: Sets the theme for the client side decorations. Using
None
will use dbus to get the system preference. - X11: Sets
_GTK_THEME_VARIANT
hint todark
orlight
and ifNone
is used, it will default toTheme::Dark
. - iOS / Android / Web / Orbital: Unsupported.
Examples found in repository?
196 fn handle_action(&mut self, event_loop: &ActiveEventLoop, window_id: WindowId, action: Action) {
197 // let cursor_position = self.cursor_position;
198 let window = self.windows.get_mut(&window_id).unwrap();
199 info!("Executing action: {action:?}");
200 match action {
201 Action::CloseWindow => {
202 let _ = self.windows.remove(&window_id);
203 },
204 Action::CreateNewWindow => {
205 #[cfg(any(x11_platform, wayland_platform))]
206 if let Err(err) = window.window.request_activation_token() {
207 info!("Failed to get activation token: {err}");
208 } else {
209 return;
210 }
211
212 if let Err(err) = self.create_window(event_loop, None) {
213 error!("Error creating new window: {err}");
214 }
215 },
216 Action::ToggleResizeIncrements => window.toggle_resize_increments(),
217 Action::ToggleCursorVisibility => window.toggle_cursor_visibility(),
218 Action::ToggleResizable => window.toggle_resizable(),
219 Action::ToggleDecorations => window.toggle_decorations(),
220 Action::ToggleFullscreen => window.toggle_fullscreen(),
221 Action::ToggleMaximize => window.toggle_maximize(),
222 Action::ToggleImeInput => window.toggle_ime(),
223 Action::Minimize => window.minimize(),
224 Action::NextCursor => window.next_cursor(),
225 Action::NextCustomCursor => window.next_custom_cursor(&self.custom_cursors),
226 #[cfg(web_platform)]
227 Action::UrlCustomCursor => window.url_custom_cursor(event_loop),
228 #[cfg(web_platform)]
229 Action::AnimationCustomCursor => {
230 window.animation_custom_cursor(event_loop, &self.custom_cursors)
231 },
232 Action::CycleCursorGrab => window.cycle_cursor_grab(),
233 Action::DragWindow => window.drag_window(),
234 Action::DragResizeWindow => window.drag_resize_window(),
235 Action::ShowWindowMenu => window.show_menu(),
236 Action::PrintHelp => self.print_help(),
237 #[cfg(macos_platform)]
238 Action::CycleOptionAsAlt => window.cycle_option_as_alt(),
239 Action::SetTheme(theme) => {
240 window.window.set_theme(theme);
241 // Get the resulting current theme to draw with
242 let actual_theme = theme.or_else(|| window.window.theme()).unwrap_or(Theme::Dark);
243 window.set_draw_theme(actual_theme);
244 },
245 #[cfg(macos_platform)]
246 Action::CreateNewTab => {
247 let tab_id = window.window.tabbing_identifier();
248 if let Err(err) = self.create_window(event_loop, Some(tab_id)) {
249 error!("Error creating new window: {err}");
250 }
251 },
252 Action::RequestResize => window.swap_dimensions(),
253 }
254 }
Sourcepub fn theme(&self) -> Option<Theme>
pub fn theme(&self) -> Option<Theme>
Returns the current window theme.
Returns None
if it cannot be determined on the current platform.
§Platform-specific
- iOS / Android / x11 / Orbital: Unsupported.
- Wayland: Only returns theme overrides.
Examples found in repository?
196 fn handle_action(&mut self, event_loop: &ActiveEventLoop, window_id: WindowId, action: Action) {
197 // let cursor_position = self.cursor_position;
198 let window = self.windows.get_mut(&window_id).unwrap();
199 info!("Executing action: {action:?}");
200 match action {
201 Action::CloseWindow => {
202 let _ = self.windows.remove(&window_id);
203 },
204 Action::CreateNewWindow => {
205 #[cfg(any(x11_platform, wayland_platform))]
206 if let Err(err) = window.window.request_activation_token() {
207 info!("Failed to get activation token: {err}");
208 } else {
209 return;
210 }
211
212 if let Err(err) = self.create_window(event_loop, None) {
213 error!("Error creating new window: {err}");
214 }
215 },
216 Action::ToggleResizeIncrements => window.toggle_resize_increments(),
217 Action::ToggleCursorVisibility => window.toggle_cursor_visibility(),
218 Action::ToggleResizable => window.toggle_resizable(),
219 Action::ToggleDecorations => window.toggle_decorations(),
220 Action::ToggleFullscreen => window.toggle_fullscreen(),
221 Action::ToggleMaximize => window.toggle_maximize(),
222 Action::ToggleImeInput => window.toggle_ime(),
223 Action::Minimize => window.minimize(),
224 Action::NextCursor => window.next_cursor(),
225 Action::NextCustomCursor => window.next_custom_cursor(&self.custom_cursors),
226 #[cfg(web_platform)]
227 Action::UrlCustomCursor => window.url_custom_cursor(event_loop),
228 #[cfg(web_platform)]
229 Action::AnimationCustomCursor => {
230 window.animation_custom_cursor(event_loop, &self.custom_cursors)
231 },
232 Action::CycleCursorGrab => window.cycle_cursor_grab(),
233 Action::DragWindow => window.drag_window(),
234 Action::DragResizeWindow => window.drag_resize_window(),
235 Action::ShowWindowMenu => window.show_menu(),
236 Action::PrintHelp => self.print_help(),
237 #[cfg(macos_platform)]
238 Action::CycleOptionAsAlt => window.cycle_option_as_alt(),
239 Action::SetTheme(theme) => {
240 window.window.set_theme(theme);
241 // Get the resulting current theme to draw with
242 let actual_theme = theme.or_else(|| window.window.theme()).unwrap_or(Theme::Dark);
243 window.set_draw_theme(actual_theme);
244 },
245 #[cfg(macos_platform)]
246 Action::CreateNewTab => {
247 let tab_id = window.window.tabbing_identifier();
248 if let Err(err) = self.create_window(event_loop, Some(tab_id)) {
249 error!("Error creating new window: {err}");
250 }
251 },
252 Action::RequestResize => window.swap_dimensions(),
253 }
254 }
255
256 fn dump_monitors(&self, event_loop: &ActiveEventLoop) {
257 info!("Monitors information");
258 let primary_monitor = event_loop.primary_monitor();
259 for monitor in event_loop.available_monitors() {
260 let intro = if primary_monitor.as_ref() == Some(&monitor) {
261 "Primary monitor"
262 } else {
263 "Monitor"
264 };
265
266 if let Some(name) = monitor.name() {
267 info!("{intro}: {name}");
268 } else {
269 info!("{intro}: [no name]");
270 }
271
272 let PhysicalSize { width, height } = monitor.size();
273 info!(
274 " Current mode: {width}x{height}{}",
275 if let Some(m_hz) = monitor.refresh_rate_millihertz() {
276 format!(" @ {}.{} Hz", m_hz / 1000, m_hz % 1000)
277 } else {
278 String::new()
279 }
280 );
281
282 let PhysicalPosition { x, y } = monitor.position();
283 info!(" Position: {x},{y}");
284
285 info!(" Scale factor: {}", monitor.scale_factor());
286
287 info!(" Available modes (width x height x bit-depth):");
288 for mode in monitor.video_modes() {
289 let PhysicalSize { width, height } = mode.size();
290 let bits = mode.bit_depth();
291 let m_hz = mode.refresh_rate_millihertz();
292 info!(" {width}x{height}x{bits} @ {}.{} Hz", m_hz / 1000, m_hz % 1000);
293 }
294 }
295 }
296
297 /// Process the key binding.
298 fn process_key_binding(key: &str, mods: &ModifiersState) -> Option<Action> {
299 KEY_BINDINGS
300 .iter()
301 .find_map(|binding| binding.is_triggered_by(&key, mods).then_some(binding.action))
302 }
303
304 /// Process mouse binding.
305 fn process_mouse_binding(button: MouseButton, mods: &ModifiersState) -> Option<Action> {
306 MOUSE_BINDINGS
307 .iter()
308 .find_map(|binding| binding.is_triggered_by(&button, mods).then_some(binding.action))
309 }
310
311 fn print_help(&self) {
312 info!("Keyboard bindings:");
313 for binding in KEY_BINDINGS {
314 info!(
315 "{}{:<10} - {} ({})",
316 modifiers_to_string(binding.mods),
317 binding.trigger,
318 binding.action,
319 binding.action.help(),
320 );
321 }
322 info!("Mouse bindings:");
323 for binding in MOUSE_BINDINGS {
324 info!(
325 "{}{:<10} - {} ({})",
326 modifiers_to_string(binding.mods),
327 mouse_button_to_string(binding.trigger),
328 binding.action,
329 binding.action.help(),
330 );
331 }
332 }
333}
334
335impl ApplicationHandler<UserEvent> for Application {
336 fn user_event(&mut self, _event_loop: &ActiveEventLoop, event: UserEvent) {
337 info!("User event: {event:?}");
338 }
339
340 fn window_event(
341 &mut self,
342 event_loop: &ActiveEventLoop,
343 window_id: WindowId,
344 event: WindowEvent,
345 ) {
346 let window = match self.windows.get_mut(&window_id) {
347 Some(window) => window,
348 None => return,
349 };
350
351 match event {
352 WindowEvent::Resized(size) => {
353 window.resize(size);
354 },
355 WindowEvent::Focused(focused) => {
356 if focused {
357 info!("Window={window_id:?} focused");
358 } else {
359 info!("Window={window_id:?} unfocused");
360 }
361 },
362 WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
363 info!("Window={window_id:?} changed scale to {scale_factor}");
364 },
365 WindowEvent::ThemeChanged(theme) => {
366 info!("Theme changed to {theme:?}");
367 window.set_draw_theme(theme);
368 },
369 WindowEvent::RedrawRequested => {
370 if let Err(err) = window.draw() {
371 error!("Error drawing window: {err}");
372 }
373 },
374 WindowEvent::Occluded(occluded) => {
375 window.set_occluded(occluded);
376 },
377 WindowEvent::CloseRequested => {
378 info!("Closing Window={window_id:?}");
379 self.windows.remove(&window_id);
380 },
381 WindowEvent::ModifiersChanged(modifiers) => {
382 window.modifiers = modifiers.state();
383 info!("Modifiers changed to {:?}", window.modifiers);
384 },
385 WindowEvent::MouseWheel { delta, .. } => match delta {
386 MouseScrollDelta::LineDelta(x, y) => {
387 info!("Mouse wheel Line Delta: ({x},{y})");
388 },
389 MouseScrollDelta::PixelDelta(px) => {
390 info!("Mouse wheel Pixel Delta: ({},{})", px.x, px.y);
391 },
392 },
393 WindowEvent::KeyboardInput { event, is_synthetic: false, .. } => {
394 let mods = window.modifiers;
395
396 // Dispatch actions only on press.
397 if event.state.is_pressed() {
398 let action = if let Key::Character(ch) = event.logical_key.as_ref() {
399 Self::process_key_binding(&ch.to_uppercase(), &mods)
400 } else {
401 None
402 };
403
404 if let Some(action) = action {
405 self.handle_action(event_loop, window_id, action);
406 }
407 }
408 },
409 WindowEvent::MouseInput { button, state, .. } => {
410 let mods = window.modifiers;
411 if let Some(action) =
412 state.is_pressed().then(|| Self::process_mouse_binding(button, &mods)).flatten()
413 {
414 self.handle_action(event_loop, window_id, action);
415 }
416 },
417 WindowEvent::CursorLeft { .. } => {
418 info!("Cursor left Window={window_id:?}");
419 window.cursor_left();
420 },
421 WindowEvent::CursorMoved { position, .. } => {
422 info!("Moved cursor to {position:?}");
423 window.cursor_moved(position);
424 },
425 WindowEvent::ActivationTokenDone { token: _token, .. } => {
426 #[cfg(any(x11_platform, wayland_platform))]
427 {
428 startup_notify::set_activation_token_env(_token);
429 if let Err(err) = self.create_window(event_loop, None) {
430 error!("Error creating new window: {err}");
431 }
432 }
433 },
434 WindowEvent::Ime(event) => match event {
435 Ime::Enabled => info!("IME enabled for Window={window_id:?}"),
436 Ime::Preedit(text, caret_pos) => {
437 info!("Preedit: {}, with caret at {:?}", text, caret_pos);
438 },
439 Ime::Commit(text) => {
440 info!("Committed: {}", text);
441 },
442 Ime::Disabled => info!("IME disabled for Window={window_id:?}"),
443 },
444 WindowEvent::PinchGesture { delta, .. } => {
445 window.zoom += delta;
446 let zoom = window.zoom;
447 if delta > 0.0 {
448 info!("Zoomed in {delta:.5} (now: {zoom:.5})");
449 } else {
450 info!("Zoomed out {delta:.5} (now: {zoom:.5})");
451 }
452 },
453 WindowEvent::RotationGesture { delta, .. } => {
454 window.rotated += delta;
455 let rotated = window.rotated;
456 if delta > 0.0 {
457 info!("Rotated counterclockwise {delta:.5} (now: {rotated:.5})");
458 } else {
459 info!("Rotated clockwise {delta:.5} (now: {rotated:.5})");
460 }
461 },
462 WindowEvent::PanGesture { delta, phase, .. } => {
463 window.panned.x += delta.x;
464 window.panned.y += delta.y;
465 info!("Panned ({delta:?})) (now: {:?}), {phase:?}", window.panned);
466 },
467 WindowEvent::DoubleTapGesture { .. } => {
468 info!("Smart zoom");
469 },
470 WindowEvent::TouchpadPressure { .. }
471 | WindowEvent::HoveredFileCancelled
472 | WindowEvent::KeyboardInput { .. }
473 | WindowEvent::CursorEntered { .. }
474 | WindowEvent::AxisMotion { .. }
475 | WindowEvent::DroppedFile(_)
476 | WindowEvent::HoveredFile(_)
477 | WindowEvent::Destroyed
478 | WindowEvent::Touch(_)
479 | WindowEvent::Moved(_) => (),
480 }
481 }
482
483 fn device_event(
484 &mut self,
485 _event_loop: &ActiveEventLoop,
486 device_id: DeviceId,
487 event: DeviceEvent,
488 ) {
489 info!("Device {device_id:?} event: {event:?}");
490 }
491
492 fn resumed(&mut self, event_loop: &ActiveEventLoop) {
493 info!("Resumed the event loop");
494 self.dump_monitors(event_loop);
495
496 // Create initial window.
497 self.create_window(event_loop, None).expect("failed to create initial window");
498
499 self.print_help();
500 }
501
502 fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
503 if self.windows.is_empty() {
504 info!("No windows left, exiting...");
505 event_loop.exit();
506 }
507 }
508
509 #[cfg(not(any(android_platform, ios_platform)))]
510 fn exiting(&mut self, _event_loop: &ActiveEventLoop) {
511 // We must drop the context here.
512 self.context = None;
513 }
514}
515
516/// State of the window.
517struct WindowState {
518 /// IME input.
519 ime: bool,
520 /// Render surface.
521 ///
522 /// NOTE: This surface must be dropped before the `Window`.
523 #[cfg(not(any(android_platform, ios_platform)))]
524 surface: Surface<DisplayHandle<'static>, Arc<Window>>,
525 /// The actual winit Window.
526 window: Arc<Window>,
527 /// The window theme we're drawing with.
528 theme: Theme,
529 /// Cursor position over the window.
530 cursor_position: Option<PhysicalPosition<f64>>,
531 /// Window modifiers state.
532 modifiers: ModifiersState,
533 /// Occlusion state of the window.
534 occluded: bool,
535 /// Current cursor grab mode.
536 cursor_grab: CursorGrabMode,
537 /// The amount of zoom into window.
538 zoom: f64,
539 /// The amount of rotation of the window.
540 rotated: f32,
541 /// The amount of pan of the window.
542 panned: PhysicalPosition<f32>,
543
544 #[cfg(macos_platform)]
545 option_as_alt: OptionAsAlt,
546
547 // Cursor states.
548 named_idx: usize,
549 custom_idx: usize,
550 cursor_hidden: bool,
551}
552
553impl WindowState {
554 fn new(app: &Application, window: Window) -> Result<Self, Box<dyn Error>> {
555 let window = Arc::new(window);
556
557 // SAFETY: the surface is dropped before the `window` which provided it with handle, thus
558 // it doesn't outlive it.
559 #[cfg(not(any(android_platform, ios_platform)))]
560 let surface = Surface::new(app.context.as_ref().unwrap(), Arc::clone(&window))?;
561
562 let theme = window.theme().unwrap_or(Theme::Dark);
563 info!("Theme: {theme:?}");
564 let named_idx = 0;
565 window.set_cursor(CURSORS[named_idx]);
566
567 // Allow IME out of the box.
568 let ime = true;
569 window.set_ime_allowed(ime);
570
571 let size = window.inner_size();
572 let mut state = Self {
573 #[cfg(macos_platform)]
574 option_as_alt: window.option_as_alt(),
575 custom_idx: app.custom_cursors.len() - 1,
576 cursor_grab: CursorGrabMode::None,
577 named_idx,
578 #[cfg(not(any(android_platform, ios_platform)))]
579 surface,
580 window,
581 theme,
582 ime,
583 cursor_position: Default::default(),
584 cursor_hidden: Default::default(),
585 modifiers: Default::default(),
586 occluded: Default::default(),
587 rotated: Default::default(),
588 panned: Default::default(),
589 zoom: Default::default(),
590 };
591
592 state.resize(size);
593 Ok(state)
594 }
Sourcepub fn set_content_protected(&self, protected: bool)
pub fn set_content_protected(&self, protected: bool)
Prevents the window contents from being captured by other apps.
§Platform-specific
- macOS: if
false
,NSWindowSharingNone
is used but doesn’t completely prevent all apps from reading the window content, for instance, QuickTime. - iOS / Android / x11 / Wayland / Web / Orbital: Unsupported.
Source§impl Window
Cursor functions.
impl Window
Cursor functions.
Sourcepub fn set_cursor(&self, cursor: impl Into<Cursor>)
pub fn set_cursor(&self, cursor: impl Into<Cursor>)
Modifies the cursor icon of the window.
§Platform-specific
- iOS / Android / Orbital: Unsupported.
- Web: Custom cursors have to be loaded and decoded first, until then the previous cursor is shown.
Examples found in repository?
554 fn new(app: &Application, window: Window) -> Result<Self, Box<dyn Error>> {
555 let window = Arc::new(window);
556
557 // SAFETY: the surface is dropped before the `window` which provided it with handle, thus
558 // it doesn't outlive it.
559 #[cfg(not(any(android_platform, ios_platform)))]
560 let surface = Surface::new(app.context.as_ref().unwrap(), Arc::clone(&window))?;
561
562 let theme = window.theme().unwrap_or(Theme::Dark);
563 info!("Theme: {theme:?}");
564 let named_idx = 0;
565 window.set_cursor(CURSORS[named_idx]);
566
567 // Allow IME out of the box.
568 let ime = true;
569 window.set_ime_allowed(ime);
570
571 let size = window.inner_size();
572 let mut state = Self {
573 #[cfg(macos_platform)]
574 option_as_alt: window.option_as_alt(),
575 custom_idx: app.custom_cursors.len() - 1,
576 cursor_grab: CursorGrabMode::None,
577 named_idx,
578 #[cfg(not(any(android_platform, ios_platform)))]
579 surface,
580 window,
581 theme,
582 ime,
583 cursor_position: Default::default(),
584 cursor_hidden: Default::default(),
585 modifiers: Default::default(),
586 occluded: Default::default(),
587 rotated: Default::default(),
588 panned: Default::default(),
589 zoom: Default::default(),
590 };
591
592 state.resize(size);
593 Ok(state)
594 }
595
596 pub fn toggle_ime(&mut self) {
597 self.ime = !self.ime;
598 self.window.set_ime_allowed(self.ime);
599 if let Some(position) = self.ime.then_some(self.cursor_position).flatten() {
600 self.window.set_ime_cursor_area(position, PhysicalSize::new(20, 20));
601 }
602 }
603
604 pub fn minimize(&mut self) {
605 self.window.set_minimized(true);
606 }
607
608 pub fn cursor_moved(&mut self, position: PhysicalPosition<f64>) {
609 self.cursor_position = Some(position);
610 if self.ime {
611 self.window.set_ime_cursor_area(position, PhysicalSize::new(20, 20));
612 }
613 }
614
615 pub fn cursor_left(&mut self) {
616 self.cursor_position = None;
617 }
618
619 /// Toggle maximized.
620 fn toggle_maximize(&self) {
621 let maximized = self.window.is_maximized();
622 self.window.set_maximized(!maximized);
623 }
624
625 /// Toggle window decorations.
626 fn toggle_decorations(&self) {
627 let decorated = self.window.is_decorated();
628 self.window.set_decorations(!decorated);
629 }
630
631 /// Toggle window resizable state.
632 fn toggle_resizable(&self) {
633 let resizable = self.window.is_resizable();
634 self.window.set_resizable(!resizable);
635 }
636
637 /// Toggle cursor visibility
638 fn toggle_cursor_visibility(&mut self) {
639 self.cursor_hidden = !self.cursor_hidden;
640 self.window.set_cursor_visible(!self.cursor_hidden);
641 }
642
643 /// Toggle resize increments on a window.
644 fn toggle_resize_increments(&mut self) {
645 let new_increments = match self.window.resize_increments() {
646 Some(_) => None,
647 None => Some(LogicalSize::new(25.0, 25.0)),
648 };
649 info!("Had increments: {}", new_increments.is_none());
650 self.window.set_resize_increments(new_increments);
651 }
652
653 /// Toggle fullscreen.
654 fn toggle_fullscreen(&self) {
655 let fullscreen = if self.window.fullscreen().is_some() {
656 None
657 } else {
658 Some(Fullscreen::Borderless(None))
659 };
660
661 self.window.set_fullscreen(fullscreen);
662 }
663
664 /// Cycle through the grab modes ignoring errors.
665 fn cycle_cursor_grab(&mut self) {
666 self.cursor_grab = match self.cursor_grab {
667 CursorGrabMode::None => CursorGrabMode::Confined,
668 CursorGrabMode::Confined => CursorGrabMode::Locked,
669 CursorGrabMode::Locked => CursorGrabMode::None,
670 };
671 info!("Changing cursor grab mode to {:?}", self.cursor_grab);
672 if let Err(err) = self.window.set_cursor_grab(self.cursor_grab) {
673 error!("Error setting cursor grab: {err}");
674 }
675 }
676
677 #[cfg(macos_platform)]
678 fn cycle_option_as_alt(&mut self) {
679 self.option_as_alt = match self.option_as_alt {
680 OptionAsAlt::None => OptionAsAlt::OnlyLeft,
681 OptionAsAlt::OnlyLeft => OptionAsAlt::OnlyRight,
682 OptionAsAlt::OnlyRight => OptionAsAlt::Both,
683 OptionAsAlt::Both => OptionAsAlt::None,
684 };
685 info!("Setting option as alt {:?}", self.option_as_alt);
686 self.window.set_option_as_alt(self.option_as_alt);
687 }
688
689 /// Swap the window dimensions with `request_inner_size`.
690 fn swap_dimensions(&mut self) {
691 let old_inner_size = self.window.inner_size();
692 let mut inner_size = old_inner_size;
693
694 mem::swap(&mut inner_size.width, &mut inner_size.height);
695 info!("Requesting resize from {old_inner_size:?} to {inner_size:?}");
696
697 if let Some(new_inner_size) = self.window.request_inner_size(inner_size) {
698 if old_inner_size == new_inner_size {
699 info!("Inner size change got ignored");
700 } else {
701 self.resize(new_inner_size);
702 }
703 } else {
704 info!("Request inner size is asynchronous");
705 }
706 }
707
708 /// Pick the next cursor.
709 fn next_cursor(&mut self) {
710 self.named_idx = (self.named_idx + 1) % CURSORS.len();
711 info!("Setting cursor to \"{:?}\"", CURSORS[self.named_idx]);
712 self.window.set_cursor(Cursor::Icon(CURSORS[self.named_idx]));
713 }
714
715 /// Pick the next custom cursor.
716 fn next_custom_cursor(&mut self, custom_cursors: &[CustomCursor]) {
717 self.custom_idx = (self.custom_idx + 1) % custom_cursors.len();
718 let cursor = Cursor::Custom(custom_cursors[self.custom_idx].clone());
719 self.window.set_cursor(cursor);
720 }
Sourcepub fn set_cursor_icon(&self, icon: CursorIcon)
👎Deprecated: Renamed to set_cursor
pub fn set_cursor_icon(&self, icon: CursorIcon)
set_cursor
Deprecated! Use Window::set_cursor()
instead.
Sourcepub fn set_cursor_position<P: Into<Position>>(
&self,
position: P,
) -> Result<(), ExternalError>
pub fn set_cursor_position<P: Into<Position>>( &self, position: P, ) -> Result<(), ExternalError>
Changes the position of the cursor in window coordinates.
// Specify the position in logical dimensions like this:
window.set_cursor_position(LogicalPosition::new(400.0, 200.0));
// Or specify the position in physical dimensions like this:
window.set_cursor_position(PhysicalPosition::new(400, 200));
§Platform-specific
- Wayland: Cursor must be in
CursorGrabMode::Locked
. - iOS / Android / Web / Orbital: Always returns an
ExternalError::NotSupported
.
Sourcepub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError>
pub fn set_cursor_grab(&self, mode: CursorGrabMode) -> Result<(), ExternalError>
Sourcepub fn set_cursor_visible(&self, visible: bool)
pub fn set_cursor_visible(&self, visible: bool)
Modifies the cursor’s visibility.
If false
, this will hide the cursor. If true
, this will show the cursor.
§Platform-specific
- Windows: The cursor is only hidden within the confines of the window.
- X11: The cursor is only hidden within the confines of the window.
- Wayland: The cursor is only hidden within the confines of the window.
- macOS: The cursor is hidden as long as the window has input focus, even if the cursor is outside of the window.
- iOS / Android: Unsupported.
Sourcepub fn drag_window(&self) -> Result<(), ExternalError>
pub fn drag_window(&self) -> Result<(), ExternalError>
Moves the window with the left mouse button until the button is released.
There’s no guarantee that this will work unless the left mouse button was pressed immediately before this function is called.
§Platform-specific
- X11: Un-grabs the cursor.
- Wayland: Requires the cursor to be inside the window to be dragged.
- macOS: May prevent the button release event to be triggered.
- iOS / Android / Web: Always returns an
ExternalError::NotSupported
.
Sourcepub fn drag_resize_window(
&self,
direction: ResizeDirection,
) -> Result<(), ExternalError>
pub fn drag_resize_window( &self, direction: ResizeDirection, ) -> Result<(), ExternalError>
Resizes the window with the left mouse button until the button is released.
There’s no guarantee that this will work unless the left mouse button was pressed immediately before this function is called.
§Platform-specific
- macOS: Always returns an
ExternalError::NotSupported
- iOS / Android / Web: Always returns an
ExternalError::NotSupported
.
Examples found in repository?
789 fn drag_resize_window(&self) {
790 let position = match self.cursor_position {
791 Some(position) => position,
792 None => {
793 info!("Drag-resize requires cursor to be inside the window");
794 return;
795 },
796 };
797
798 let win_size = self.window.inner_size();
799 let border_size = BORDER_SIZE * self.window.scale_factor();
800
801 let x_direction = if position.x < border_size {
802 ResizeDirection::West
803 } else if position.x > (win_size.width as f64 - border_size) {
804 ResizeDirection::East
805 } else {
806 // Use arbitrary direction instead of None for simplicity.
807 ResizeDirection::SouthEast
808 };
809
810 let y_direction = if position.y < border_size {
811 ResizeDirection::North
812 } else if position.y > (win_size.height as f64 - border_size) {
813 ResizeDirection::South
814 } else {
815 // Use arbitrary direction instead of None for simplicity.
816 ResizeDirection::SouthEast
817 };
818
819 let direction = match (x_direction, y_direction) {
820 (ResizeDirection::West, ResizeDirection::North) => ResizeDirection::NorthWest,
821 (ResizeDirection::West, ResizeDirection::South) => ResizeDirection::SouthWest,
822 (ResizeDirection::West, _) => ResizeDirection::West,
823 (ResizeDirection::East, ResizeDirection::North) => ResizeDirection::NorthEast,
824 (ResizeDirection::East, ResizeDirection::South) => ResizeDirection::SouthEast,
825 (ResizeDirection::East, _) => ResizeDirection::East,
826 (_, ResizeDirection::South) => ResizeDirection::South,
827 (_, ResizeDirection::North) => ResizeDirection::North,
828 _ => return,
829 };
830
831 if let Err(err) = self.window.drag_resize_window(direction) {
832 info!("Error starting window drag-resize: {err}");
833 } else {
834 info!("Drag-resizing window Window={:?}", self.window.id());
835 }
836 }
Show window menu at a specified position .
This is the context menu that is normally shown when interacting with the title bar. This is useful when implementing custom decorations.
§Platform-specific
Android / iOS / macOS / Orbital / Wayland / Web / X11: Unsupported.
Sourcepub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), ExternalError>
pub fn set_cursor_hittest(&self, hittest: bool) -> Result<(), ExternalError>
Modifies whether the window catches cursor events.
If true
, the window will catch the cursor events. If false
, events are passed through
the window such that any other window behind it receives them. By default hittest is
enabled.
§Platform-specific
- iOS / Android / Web / Orbital: Always returns an
ExternalError::NotSupported
.
Source§impl Window
Monitor info functions.
impl Window
Monitor info functions.
Sourcepub fn current_monitor(&self) -> Option<MonitorHandle>
pub fn current_monitor(&self) -> Option<MonitorHandle>
Returns the monitor on which the window currently resides.
Returns None
if current monitor can’t be detected.
Sourcepub fn available_monitors(&self) -> impl Iterator<Item = MonitorHandle>
pub fn available_monitors(&self) -> impl Iterator<Item = MonitorHandle>
Returns the list of all the monitors available on the system.
This is the same as ActiveEventLoop::available_monitors
, and is provided for
convenience.
Sourcepub fn primary_monitor(&self) -> Option<MonitorHandle>
pub fn primary_monitor(&self) -> Option<MonitorHandle>
Returns the primary monitor of the system.
Returns None
if it can’t identify any monitor as a primary one.
This is the same as ActiveEventLoop::primary_monitor
, and is provided for convenience.
§Platform-specific
Wayland / Web: Always returns None
.
Trait Implementations§
Source§impl HasDisplayHandle for Window
Available on crate feature rwh_06
only.
impl HasDisplayHandle for Window
rwh_06
only.Source§fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError>
fn display_handle(&self) -> Result<DisplayHandle<'_>, HandleError>
Source§impl HasRawDisplayHandle for Window
Available on crate feature rwh_05
only.
impl HasRawDisplayHandle for Window
rwh_05
only.Source§fn raw_display_handle(&self) -> RawDisplayHandle
fn raw_display_handle(&self) -> RawDisplayHandle
Returns a rwh_05::RawDisplayHandle
used by the EventLoop
that
created a window.
Source§impl HasRawWindowHandle for Window
Available on crate feature rwh_05
only.
impl HasRawWindowHandle for Window
rwh_05
only.fn raw_window_handle(&self) -> RawWindowHandle
Source§impl HasRawWindowHandle for Window
Available on crate feature rwh_04
only.
impl HasRawWindowHandle for Window
rwh_04
only.fn raw_window_handle(&self) -> RawWindowHandle
Source§impl HasWindowHandle for Window
Available on crate feature rwh_06
only.
impl HasWindowHandle for Window
rwh_06
only.Source§fn window_handle(&self) -> Result<WindowHandle<'_>, HandleError>
fn window_handle(&self) -> Result<WindowHandle<'_>, HandleError>
Source§impl WindowExtAndroid for Window
Available on android_platform
only.
impl WindowExtAndroid for Window
android_platform
only.Source§impl WindowExtIOS for Window
Available on ios_platform
only.
impl WindowExtIOS for Window
ios_platform
only.Source§fn set_scale_factor(&self, scale_factor: f64)
fn set_scale_factor(&self, scale_factor: f64)
Source§fn set_valid_orientations(&self, valid_orientations: ValidOrientations)
fn set_valid_orientations(&self, valid_orientations: ValidOrientations)
Source§fn set_preferred_screen_edges_deferring_system_gestures(
&self,
edges: ScreenEdge,
)
fn set_preferred_screen_edges_deferring_system_gestures( &self, edges: ScreenEdge, )
Source§fn set_preferred_status_bar_style(&self, status_bar_style: StatusBarStyle)
fn set_preferred_status_bar_style(&self, status_bar_style: StatusBarStyle)
Source§fn recognize_pinch_gesture(&self, should_recognize: bool)
fn recognize_pinch_gesture(&self, should_recognize: bool)
Source§fn recognize_pan_gesture(
&self,
should_recognize: bool,
minimum_number_of_touches: u8,
maximum_number_of_touches: u8,
)
fn recognize_pan_gesture( &self, should_recognize: bool, minimum_number_of_touches: u8, maximum_number_of_touches: u8, )
Source§fn recognize_doubletap_gesture(&self, should_recognize: bool)
fn recognize_doubletap_gesture(&self, should_recognize: bool)
Source§impl WindowExtMacOS for Window
Available on macos_platform
only.
impl WindowExtMacOS for Window
macos_platform
only.Source§fn simple_fullscreen(&self) -> bool
fn simple_fullscreen(&self) -> bool
Source§fn set_simple_fullscreen(&self, fullscreen: bool) -> bool
fn set_simple_fullscreen(&self, fullscreen: bool) -> bool
Source§fn has_shadow(&self) -> bool
fn has_shadow(&self) -> bool
Source§fn set_has_shadow(&self, has_shadow: bool)
fn set_has_shadow(&self, has_shadow: bool)
Source§fn set_tabbing_identifier(&self, identifier: &str)
fn set_tabbing_identifier(&self, identifier: &str)
Source§fn tabbing_identifier(&self) -> String
fn tabbing_identifier(&self) -> String
Source§fn select_next_tab(&self)
fn select_next_tab(&self)
Source§fn select_previous_tab(&self)
fn select_previous_tab(&self)
Source§fn select_tab_at_index(&self, index: usize)
fn select_tab_at_index(&self, index: usize)
Source§fn is_document_edited(&self) -> bool
fn is_document_edited(&self) -> bool
Source§fn set_document_edited(&self, edited: bool)
fn set_document_edited(&self, edited: bool)
Source§fn set_option_as_alt(&self, option_as_alt: OptionAsAlt)
fn set_option_as_alt(&self, option_as_alt: OptionAsAlt)
OptionAsAlt
. Read moreSource§fn option_as_alt(&self) -> OptionAsAlt
fn option_as_alt(&self) -> OptionAsAlt
WindowExtMacOS::set_option_as_alt
.Source§fn set_borderless_game(&self, borderless_game: bool)
fn set_borderless_game(&self, borderless_game: bool)
Source§fn is_borderless_game(&self) -> bool
fn is_borderless_game(&self) -> bool
WindowExtMacOS::set_borderless_game
.Source§impl WindowExtStartupNotify for Window
Available on x11_platform
or wayland_platform
only.
impl WindowExtStartupNotify for Window
x11_platform
or wayland_platform
only.Source§fn request_activation_token(
&self,
) -> Result<AsyncRequestSerial, NotSupportedError>
fn request_activation_token( &self, ) -> Result<AsyncRequestSerial, NotSupportedError>
Source§impl WindowExtWebSys for Window
Available on web_platform
only.
impl WindowExtWebSys for Window
web_platform
only.Source§fn canvas(&self) -> Option<HtmlCanvasElement>
fn canvas(&self) -> Option<HtmlCanvasElement>
Source§fn prevent_default(&self) -> bool
fn prevent_default(&self) -> bool
Source§fn set_prevent_default(&self, prevent_default: bool)
fn set_prevent_default(&self, prevent_default: bool)
event.preventDefault()
should be called on events on the
canvas that have side effects. Read moreSource§impl WindowExtWindows for Window
Available on windows_platform
only.
impl WindowExtWindows for Window
windows_platform
only.Source§fn set_enable(&self, enabled: bool)
fn set_enable(&self, enabled: bool)
Source§fn set_taskbar_icon(&self, taskbar_icon: Option<Icon>)
fn set_taskbar_icon(&self, taskbar_icon: Option<Icon>)
ICON_BIG
. A good ceiling here is 256x256.Source§fn set_skip_taskbar(&self, skip: bool)
fn set_skip_taskbar(&self, skip: bool)
Source§fn set_undecorated_shadow(&self, shadow: bool)
fn set_undecorated_shadow(&self, shadow: bool)
Source§fn set_system_backdrop(&self, backdrop_type: BackdropType)
fn set_system_backdrop(&self, backdrop_type: BackdropType)
Source§fn set_border_color(&self, color: Option<Color>)
fn set_border_color(&self, color: Option<Color>)
Source§fn set_title_background_color(&self, color: Option<Color>)
fn set_title_background_color(&self, color: Option<Color>)
Source§fn set_title_text_color(&self, color: Color)
fn set_title_text_color(&self, color: Color)
Source§fn set_corner_preference(&self, preference: CornerPreference)
fn set_corner_preference(&self, preference: CornerPreference)
Source§unsafe fn window_handle_any_thread(
&self,
) -> Result<WindowHandle<'_>, HandleError>
unsafe fn window_handle_any_thread( &self, ) -> Result<WindowHandle<'_>, HandleError>
rwh_06
only.impl WindowExtWayland for Window
wayland_platform
only.impl WindowExtX11 for Window
x11_platform
only.Auto Trait Implementations§
impl Freeze for Window
impl !RefUnwindSafe for Window
impl Send for Window
impl Sync for Window
impl Unpin for Window
impl !UnwindSafe for Window
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait>
(where Trait: Downcast
) to Box<dyn Any>
. Box<dyn Any>
can
then be further downcast
into Box<ConcreteType>
where ConcreteType
implements Trait
.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait>
(where Trait: Downcast
) to Rc<Any>
. Rc<Any>
can then be
further downcast
into Rc<ConcreteType>
where ConcreteType
implements Trait
.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait
(where Trait: Downcast
) to &Any
. This is needed since Rust cannot
generate &Any
’s vtable from &Trait
’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait
(where Trait: Downcast
) to &Any
. This is needed since Rust cannot
generate &mut Any
’s vtable from &mut Trait
’s.Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
Source§impl<T> HasRawDisplayHandle for Twhere
T: HasDisplayHandle + ?Sized,
impl<T> HasRawDisplayHandle for Twhere
T: HasDisplayHandle + ?Sized,
Source§fn raw_display_handle(&self) -> Result<RawDisplayHandle, HandleError>
fn raw_display_handle(&self) -> Result<RawDisplayHandle, HandleError>
HasDisplayHandle
insteadSource§impl<T> HasRawWindowHandle for Twhere
T: HasWindowHandle + ?Sized,
impl<T> HasRawWindowHandle for Twhere
T: HasWindowHandle + ?Sized,
Source§fn raw_window_handle(&self) -> Result<RawWindowHandle, HandleError>
fn raw_window_handle(&self) -> Result<RawWindowHandle, HandleError>
HasWindowHandle
insteadSource§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<W> WindowBorrowExtWindows for W
impl<W> WindowBorrowExtWindows for W
Source§unsafe fn any_thread(self) -> AnyThread<Self>
unsafe fn any_thread(self) -> AnyThread<Self>
windows_platform
only.