bevy_window/
window.rs

1use alloc::{borrow::ToOwned, string::String};
2use core::num::NonZero;
3
4use bevy_ecs::{
5    entity::{ContainsEntity, Entity},
6    prelude::Component,
7};
8use bevy_math::{CompassOctant, DVec2, IVec2, UVec2, Vec2};
9use log::warn;
10
11#[cfg(feature = "bevy_reflect")]
12use {
13    bevy_ecs::prelude::ReflectComponent,
14    bevy_reflect::{std_traits::ReflectDefault, Reflect},
15};
16
17#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
18use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
19
20use crate::VideoMode;
21
22/// Marker [`Component`] for the window considered the primary window.
23///
24/// Currently this is assumed to only exist on 1 entity at a time.
25///
26/// [`WindowPlugin`](crate::WindowPlugin) will spawn a [`Window`] entity
27/// with this component if [`primary_window`](crate::WindowPlugin::primary_window)
28/// is `Some`.
29#[derive(Default, Debug, Component, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
30#[cfg_attr(
31    feature = "bevy_reflect",
32    derive(Reflect),
33    reflect(Component, Debug, Default, PartialEq, Clone)
34)]
35pub struct PrimaryWindow;
36
37/// Reference to a [`Window`], whether it be a direct link to a specific entity or
38/// a more vague defaulting choice.
39#[repr(C)]
40#[derive(Default, Copy, Clone, Debug)]
41#[cfg_attr(
42    feature = "bevy_reflect",
43    derive(Reflect),
44    reflect(Debug, Default, Clone)
45)]
46#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
47#[cfg_attr(
48    all(feature = "serialize", feature = "bevy_reflect"),
49    reflect(Serialize, Deserialize)
50)]
51pub enum WindowRef {
52    /// This will be linked to the primary window that is created by default
53    /// in the [`WindowPlugin`](crate::WindowPlugin::primary_window).
54    #[default]
55    Primary,
56    /// A more direct link to a window entity.
57    ///
58    /// Use this if you want to reference a secondary/tertiary/... window.
59    ///
60    /// To create a new window you can spawn an entity with a [`Window`],
61    /// then you can use that entity here for usage in cameras.
62    Entity(Entity),
63}
64
65impl WindowRef {
66    /// Normalize the window reference so that it can be compared to other window references.
67    pub fn normalize(&self, primary_window: Option<Entity>) -> Option<NormalizedWindowRef> {
68        let entity = match self {
69            Self::Primary => primary_window,
70            Self::Entity(entity) => Some(*entity),
71        };
72
73        entity.map(NormalizedWindowRef)
74    }
75}
76
77/// A flattened representation of a window reference for equality/hashing purposes.
78///
79/// For most purposes you probably want to use the unnormalized version [`WindowRef`].
80#[repr(C)]
81#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
82#[cfg_attr(
83    feature = "bevy_reflect",
84    derive(Reflect),
85    reflect(Debug, PartialEq, Hash, Clone)
86)]
87#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
88#[cfg_attr(
89    all(feature = "serialize", feature = "bevy_reflect"),
90    reflect(Serialize, Deserialize)
91)]
92pub struct NormalizedWindowRef(Entity);
93
94impl ContainsEntity for NormalizedWindowRef {
95    fn entity(&self) -> Entity {
96        self.0
97    }
98}
99
100/// The defining [`Component`] for window entities,
101/// storing information about how it should appear and behave.
102///
103/// Each window corresponds to an entity, and is uniquely identified by the value of their [`Entity`].
104/// When the [`Window`] component is added to an entity, a new window will be opened.
105/// When it is removed or the entity is despawned, the window will close.
106///
107/// The primary window entity (and the corresponding window) is spawned by default
108/// by [`WindowPlugin`](crate::WindowPlugin) and is marked with the [`PrimaryWindow`] component.
109///
110/// This component is synchronized with `winit` through `bevy_winit`:
111/// it will reflect the current state of the window and can be modified to change this state.
112///
113/// # Example
114///
115/// Because this component is synchronized with `winit`, it can be used to perform
116/// OS-integrated windowing operations. For example, here's a simple system
117/// to change the window mode:
118///
119/// ```
120/// # use bevy_ecs::query::With;
121/// # use bevy_ecs::system::Query;
122/// # use bevy_window::{WindowMode, PrimaryWindow, Window, MonitorSelection, VideoModeSelection};
123/// fn change_window_mode(mut windows: Query<&mut Window, With<PrimaryWindow>>) {
124///     // Query returns one window typically.
125///     for mut window in windows.iter_mut() {
126///         window.mode =
127///             WindowMode::Fullscreen(MonitorSelection::Current, VideoModeSelection::Current);
128///     }
129/// }
130/// ```
131#[derive(Component, Debug, Clone)]
132#[cfg_attr(
133    feature = "bevy_reflect",
134    derive(Reflect),
135    reflect(Component, Default, Debug, Clone)
136)]
137#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
138#[cfg_attr(
139    all(feature = "serialize", feature = "bevy_reflect"),
140    reflect(Serialize, Deserialize)
141)]
142pub struct Window {
143    /// The cursor options of this window. Cursor icons are set with the `Cursor` component on the
144    /// window entity.
145    pub cursor_options: CursorOptions,
146    /// What presentation mode to give the window.
147    pub present_mode: PresentMode,
148    /// Which fullscreen or windowing mode should be used.
149    pub mode: WindowMode,
150    /// Where the window should be placed.
151    pub position: WindowPosition,
152    /// What resolution the window should have.
153    pub resolution: WindowResolution,
154    /// Stores the title of the window.
155    pub title: String,
156    /// Stores the application ID (on **`Wayland`**), `WM_CLASS` (on **`X11`**) or window class name (on **`Windows`**) of the window.
157    ///
158    /// For details about application ID conventions, see the [Desktop Entry Spec](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#desktop-file-id).
159    /// For details about `WM_CLASS`, see the [X11 Manual Pages](https://www.x.org/releases/current/doc/man/man3/XAllocClassHint.3.xhtml).
160    /// For details about **`Windows`**'s window class names, see [About Window Classes](https://learn.microsoft.com/en-us/windows/win32/winmsg/about-window-classes).
161    ///
162    /// ## Platform-specific
163    ///
164    /// - **`Windows`**: Can only be set while building the window, setting the window's window class name.
165    /// - **`Wayland`**: Can only be set while building the window, setting the window's application ID.
166    /// - **`X11`**: Can only be set while building the window, setting the window's `WM_CLASS`.
167    /// - **`macOS`**, **`iOS`**, **`Android`**, and **`Web`**: not applicable.
168    ///
169    /// Notes: Changing this field during runtime will have no effect for now.
170    pub name: Option<String>,
171    /// How the alpha channel of textures should be handled while compositing.
172    pub composite_alpha_mode: CompositeAlphaMode,
173    /// The limits of the window's logical size
174    /// (found in its [`resolution`](WindowResolution)) when resizing.
175    pub resize_constraints: WindowResizeConstraints,
176    /// Should the window be resizable?
177    ///
178    /// Note: This does not stop the program from fullscreening/setting
179    /// the size programmatically.
180    pub resizable: bool,
181    /// Specifies which window control buttons should be enabled.
182    ///
183    /// ## Platform-specific
184    ///
185    /// **`iOS`**, **`Android`**, and the **`Web`** do not have window control buttons.
186    ///
187    /// On some **`Linux`** environments these values have no effect.
188    pub enabled_buttons: EnabledButtons,
189    /// Should the window have decorations enabled?
190    ///
191    /// (Decorations are the minimize, maximize, and close buttons on desktop apps)
192    ///
193    /// ## Platform-specific
194    ///
195    /// **`iOS`**, **`Android`**, and the **`Web`** do not have decorations.
196    pub decorations: bool,
197    /// Should the window be transparent?
198    ///
199    /// Defines whether the background of the window should be transparent.
200    ///
201    /// ## Platform-specific
202    /// - iOS / Android / Web: Unsupported.
203    /// - macOS: Not working as expected.
204    ///
205    /// macOS transparent works with winit out of the box, so this issue might be related to: <https://github.com/gfx-rs/wgpu/issues/687>.
206    /// You should also set the window `composite_alpha_mode` to `CompositeAlphaMode::PostMultiplied`.
207    pub transparent: bool,
208    /// Get/set whether the window is focused.
209    pub focused: bool,
210    /// Where should the window appear relative to other overlapping window.
211    ///
212    /// ## Platform-specific
213    ///
214    /// - iOS / Android / Web / Wayland: Unsupported.
215    pub window_level: WindowLevel,
216    /// The "html canvas" element selector.
217    ///
218    /// If set, this selector will be used to find a matching html canvas element,
219    /// rather than creating a new one.
220    /// Uses the [CSS selector format](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector).
221    ///
222    /// This value has no effect on non-web platforms.
223    pub canvas: Option<String>,
224    /// Whether or not to fit the canvas element's size to its parent element's size.
225    ///
226    /// **Warning**: this will not behave as expected for parents that set their size according to the size of their
227    /// children. This creates a "feedback loop" that will result in the canvas growing on each resize. When using this
228    /// feature, ensure the parent's size is not affected by its children.
229    ///
230    /// This value has no effect on non-web platforms.
231    pub fit_canvas_to_parent: bool,
232    /// Whether or not to stop events from propagating out of the canvas element
233    ///
234    ///  When `true`, this will prevent common browser hotkeys like F5, F12, Ctrl+R, tab, etc.
235    /// from performing their default behavior while the bevy app has focus.
236    ///
237    /// This value has no effect on non-web platforms.
238    pub prevent_default_event_handling: bool,
239    /// Stores internal state that isn't directly accessible.
240    pub internal: InternalWindowState,
241    /// Should the window use Input Method Editor?
242    ///
243    /// If enabled, the window will receive [`Ime`](crate::Ime) events instead of
244    /// `KeyboardInput` from `bevy_input`.
245    ///
246    /// IME should be enabled during text input, but not when you expect to get the exact key pressed.
247    ///
248    ///  ## Platform-specific
249    ///
250    /// - iOS / Android / Web: Unsupported.
251    pub ime_enabled: bool,
252    /// Sets location of IME candidate box in client area coordinates relative to the top left.
253    ///
254    ///  ## Platform-specific
255    ///
256    /// - iOS / Android / Web: Unsupported.
257    pub ime_position: Vec2,
258    /// Sets a specific theme for the window.
259    ///
260    /// If `None` is provided, the window will use the system theme.
261    ///
262    /// ## Platform-specific
263    ///
264    /// - iOS / Android / Web: Unsupported.
265    pub window_theme: Option<WindowTheme>,
266    /// Sets the window's visibility.
267    ///
268    /// If `false`, this will hide the window completely, it won't appear on the screen or in the task bar.
269    /// If `true`, this will show the window.
270    /// Note that this doesn't change its focused or minimized state.
271    ///
272    /// ## Platform-specific
273    ///
274    /// - **Android / Wayland / Web:** Unsupported.
275    pub visible: bool,
276    /// Sets whether the window should be shown in the taskbar.
277    ///
278    /// If `true`, the window will not appear in the taskbar.
279    /// If `false`, the window will appear in the taskbar.
280    ///
281    /// Note that this will only take effect on window creation.
282    ///
283    /// ## Platform-specific
284    ///
285    /// - Only supported on Windows.
286    pub skip_taskbar: bool,
287    /// Sets whether the window should draw over its child windows.
288    ///
289    /// If `true`, the window excludes drawing over areas obscured by child windows.
290    /// If `false`, the window can draw over child windows.
291    ///
292    /// ## Platform-specific
293    ///
294    /// - Only supported on Windows.
295    pub clip_children: bool,
296    /// Optional hint given to the rendering API regarding the maximum number of queued frames admissible on the GPU.
297    ///
298    /// Given values are usually within the 1-3 range. If not provided, this will default to 2.
299    ///
300    /// See [`wgpu::SurfaceConfiguration::desired_maximum_frame_latency`].
301    ///
302    /// [`wgpu::SurfaceConfiguration::desired_maximum_frame_latency`]:
303    /// https://docs.rs/wgpu/latest/wgpu/type.SurfaceConfiguration.html#structfield.desired_maximum_frame_latency
304    pub desired_maximum_frame_latency: Option<NonZero<u32>>,
305    /// Sets whether this window recognizes [`PinchGesture`](https://docs.rs/bevy/latest/bevy/input/gestures/struct.PinchGesture.html)
306    ///
307    /// ## Platform-specific
308    ///
309    /// - Only used on iOS.
310    /// - On macOS, they are recognized by default and can't be disabled.
311    pub recognize_pinch_gesture: bool,
312    /// Sets whether this window recognizes [`RotationGesture`](https://docs.rs/bevy/latest/bevy/input/gestures/struct.RotationGesture.html)
313    ///
314    /// ## Platform-specific
315    ///
316    /// - Only used on iOS.
317    /// - On macOS, they are recognized by default and can't be disabled.
318    pub recognize_rotation_gesture: bool,
319    /// Sets whether this window recognizes [`DoubleTapGesture`](https://docs.rs/bevy/latest/bevy/input/gestures/struct.DoubleTapGesture.html)
320    ///
321    /// ## Platform-specific
322    ///
323    /// - Only used on iOS.
324    /// - On macOS, they are recognized by default and can't be disabled.
325    pub recognize_doubletap_gesture: bool,
326    /// Sets whether this window recognizes [`PanGesture`](https://docs.rs/bevy/latest/bevy/input/gestures/struct.PanGesture.html),
327    /// with a number of fingers between the first value and the last.
328    ///
329    /// ## Platform-specific
330    ///
331    /// - Only used on iOS.
332    pub recognize_pan_gesture: Option<(u8, u8)>,
333    /// Enables click-and-drag behavior for the entire window, not just the titlebar.
334    ///
335    /// Corresponds to [`WindowAttributesExtMacOS::with_movable_by_window_background`].
336    ///
337    /// # Platform-specific
338    ///
339    /// - Only used on macOS.
340    ///
341    /// [`WindowAttributesExtMacOS::with_movable_by_window_background`]: https://docs.rs/winit/latest/x86_64-apple-darwin/winit/platform/macos/trait.WindowAttributesExtMacOS.html#tymethod.with_movable_by_window_background
342    pub movable_by_window_background: bool,
343    /// Makes the window content appear behind the titlebar.
344    ///
345    /// Corresponds to [`WindowAttributesExtMacOS::with_fullsize_content_view`].
346    ///
347    /// For apps which want to render the window buttons on top of the apps
348    /// itself, this should be enabled along with [`titlebar_transparent`].
349    ///
350    /// # Platform-specific
351    ///
352    /// - Only used on macOS.
353    ///
354    /// [`WindowAttributesExtMacOS::with_fullsize_content_view`]: https://docs.rs/winit/latest/x86_64-apple-darwin/winit/platform/macos/trait.WindowAttributesExtMacOS.html#tymethod.with_fullsize_content_view
355    /// [`titlebar_transparent`]: Self::titlebar_transparent
356    pub fullsize_content_view: bool,
357    /// Toggles drawing the drop shadow behind the window.
358    ///
359    /// Corresponds to [`WindowAttributesExtMacOS::with_has_shadow`].
360    ///
361    /// # Platform-specific
362    ///
363    /// - Only used on macOS.
364    ///
365    /// [`WindowAttributesExtMacOS::with_has_shadow`]: https://docs.rs/winit/latest/x86_64-apple-darwin/winit/platform/macos/trait.WindowAttributesExtMacOS.html#tymethod.with_has_shadow
366    pub has_shadow: bool,
367    /// Toggles drawing the titlebar.
368    ///
369    /// Corresponds to [`WindowAttributesExtMacOS::with_titlebar_hidden`].
370    ///
371    /// # Platform-specific
372    ///
373    /// - Only used on macOS.
374    ///
375    /// [`WindowAttributesExtMacOS::with_titlebar_hidden`]: https://docs.rs/winit/latest/x86_64-apple-darwin/winit/platform/macos/trait.WindowAttributesExtMacOS.html#tymethod.with_titlebar_hidden
376    pub titlebar_shown: bool,
377    /// Makes the titlebar transparent, allowing the app content to appear behind it.
378    ///
379    /// Corresponds to [`WindowAttributesExtMacOS::with_titlebar_transparent`].
380    ///
381    /// # Platform-specific
382    ///
383    /// - Only used on macOS.
384    ///
385    /// [`WindowAttributesExtMacOS::with_titlebar_transparent`]: https://docs.rs/winit/latest/x86_64-apple-darwin/winit/platform/macos/trait.WindowAttributesExtMacOS.html#tymethod.with_titlebar_transparent
386    pub titlebar_transparent: bool,
387    /// Toggles showing the window title.
388    ///
389    /// Corresponds to [`WindowAttributesExtMacOS::with_title_hidden`].
390    ///
391    /// # Platform-specific
392    ///
393    /// - Only used on macOS.
394    ///
395    /// [`WindowAttributesExtMacOS::with_title_hidden`]: https://docs.rs/winit/latest/x86_64-apple-darwin/winit/platform/macos/trait.WindowAttributesExtMacOS.html#tymethod.with_title_hidden
396    pub titlebar_show_title: bool,
397    /// Toggles showing the traffic light window buttons.
398    ///
399    /// Corresponds to [`WindowAttributesExtMacOS::with_titlebar_buttons_hidden`].
400    ///
401    /// # Platform-specific
402    ///
403    /// - Only used on macOS.
404    ///
405    /// [`WindowAttributesExtMacOS::with_titlebar_buttons_hidden`]: https://docs.rs/winit/latest/x86_64-apple-darwin/winit/platform/macos/trait.WindowAttributesExtMacOS.html#tymethod.with_titlebar_buttons_hidden
406    pub titlebar_show_buttons: bool,
407    /// Sets whether the Window prefers the home indicator hidden.
408    ///
409    /// Corresponds to [`WindowAttributesExtIOS::with_prefers_home_indicator_hidden`].
410    ///
411    /// # Platform-specific
412    ///
413    /// - Only used on iOS.
414    ///
415    /// [`WindowAttributesExtIOS::with_prefers_home_indicator_hidden`]: https://docs.rs/winit/latest/x86_64-apple-darwin/winit/platform/ios/trait.WindowAttributesExtIOS.html#tymethod.with_prefers_home_indicator_hidden
416    pub prefers_home_indicator_hidden: bool,
417    /// Sets whether the Window prefers the status bar hidden.
418    ///
419    /// Corresponds to [`WindowAttributesExtIOS::with_prefers_status_bar_hidden`].
420    ///
421    /// # Platform-specific
422    ///
423    /// - Only used on iOS.
424    ///
425    /// [`WindowAttributesExtIOS::with_prefers_status_bar_hidden`]: https://docs.rs/winit/latest/x86_64-apple-darwin/winit/platform/ios/trait.WindowAttributesExtIOS.html#tymethod.with_prefers_status_bar_hidden
426    pub prefers_status_bar_hidden: bool,
427}
428
429impl Default for Window {
430    fn default() -> Self {
431        Self {
432            title: "App".to_owned(),
433            name: None,
434            cursor_options: Default::default(),
435            present_mode: Default::default(),
436            mode: Default::default(),
437            position: Default::default(),
438            resolution: Default::default(),
439            internal: Default::default(),
440            composite_alpha_mode: Default::default(),
441            resize_constraints: Default::default(),
442            ime_enabled: Default::default(),
443            ime_position: Default::default(),
444            resizable: true,
445            enabled_buttons: Default::default(),
446            decorations: true,
447            transparent: false,
448            focused: true,
449            window_level: Default::default(),
450            fit_canvas_to_parent: false,
451            prevent_default_event_handling: true,
452            canvas: None,
453            window_theme: None,
454            visible: true,
455            skip_taskbar: false,
456            clip_children: true,
457            desired_maximum_frame_latency: None,
458            recognize_pinch_gesture: false,
459            recognize_rotation_gesture: false,
460            recognize_doubletap_gesture: false,
461            recognize_pan_gesture: None,
462            movable_by_window_background: false,
463            fullsize_content_view: false,
464            has_shadow: true,
465            titlebar_shown: true,
466            titlebar_transparent: false,
467            titlebar_show_title: true,
468            titlebar_show_buttons: true,
469            prefers_home_indicator_hidden: false,
470            prefers_status_bar_hidden: false,
471        }
472    }
473}
474
475impl Window {
476    /// Setting to true will attempt to maximize the window.
477    ///
478    /// Setting to false will attempt to un-maximize the window.
479    pub fn set_maximized(&mut self, maximized: bool) {
480        self.internal.maximize_request = Some(maximized);
481    }
482
483    /// Setting to true will attempt to minimize the window.
484    ///
485    /// Setting to false will attempt to un-minimize the window.
486    pub fn set_minimized(&mut self, minimized: bool) {
487        self.internal.minimize_request = Some(minimized);
488    }
489
490    /// Calling this will attempt to start a drag-move of the window.
491    ///
492    /// There is no guarantee that this will work unless the left mouse button was
493    /// pressed immediately before this function was called.
494    pub fn start_drag_move(&mut self) {
495        self.internal.drag_move_request = true;
496    }
497
498    /// Calling this will attempt to start a drag-resize of the window.
499    ///
500    /// There is no guarantee that this will work unless the left mouse button was
501    /// pressed immediately before this function was called.
502    pub fn start_drag_resize(&mut self, direction: CompassOctant) {
503        self.internal.drag_resize_request = Some(direction);
504    }
505
506    /// The window's client area width in logical pixels.
507    ///
508    /// See [`WindowResolution`] for an explanation about logical/physical sizes.
509    #[inline]
510    pub fn width(&self) -> f32 {
511        self.resolution.width()
512    }
513
514    /// The window's client area height in logical pixels.
515    ///
516    /// See [`WindowResolution`] for an explanation about logical/physical sizes.
517    #[inline]
518    pub fn height(&self) -> f32 {
519        self.resolution.height()
520    }
521
522    /// The window's client size in logical pixels
523    ///
524    /// See [`WindowResolution`] for an explanation about logical/physical sizes.
525    #[inline]
526    pub fn size(&self) -> Vec2 {
527        self.resolution.size()
528    }
529
530    /// The window's client area width in physical pixels.
531    ///
532    /// See [`WindowResolution`] for an explanation about logical/physical sizes.
533    #[inline]
534    pub fn physical_width(&self) -> u32 {
535        self.resolution.physical_width()
536    }
537
538    /// The window's client area height in physical pixels.
539    ///
540    /// See [`WindowResolution`] for an explanation about logical/physical sizes.
541    #[inline]
542    pub fn physical_height(&self) -> u32 {
543        self.resolution.physical_height()
544    }
545
546    /// The window's client size in physical pixels
547    ///
548    /// See [`WindowResolution`] for an explanation about logical/physical sizes.
549    #[inline]
550    pub fn physical_size(&self) -> UVec2 {
551        self.resolution.physical_size()
552    }
553
554    /// The window's scale factor.
555    ///
556    /// Ratio of physical size to logical size, see [`WindowResolution`].
557    #[inline]
558    pub fn scale_factor(&self) -> f32 {
559        self.resolution.scale_factor()
560    }
561
562    /// The cursor position in this window in logical pixels.
563    ///
564    /// Returns `None` if the cursor is outside the window area.
565    ///
566    /// See [`WindowResolution`] for an explanation about logical/physical sizes.
567    #[inline]
568    pub fn cursor_position(&self) -> Option<Vec2> {
569        self.physical_cursor_position()
570            .map(|position| (position.as_dvec2() / self.scale_factor() as f64).as_vec2())
571    }
572
573    /// The cursor position in this window in physical pixels.
574    ///
575    /// Returns `None` if the cursor is outside the window area.
576    ///
577    /// See [`WindowResolution`] for an explanation about logical/physical sizes.
578    #[inline]
579    pub fn physical_cursor_position(&self) -> Option<Vec2> {
580        match self.internal.physical_cursor_position {
581            Some(position) => {
582                if position.x >= 0.
583                    && position.y >= 0.
584                    && position.x < self.physical_width() as f64
585                    && position.y < self.physical_height() as f64
586                {
587                    Some(position.as_vec2())
588                } else {
589                    None
590                }
591            }
592            None => None,
593        }
594    }
595
596    /// Set the cursor position in this window in logical pixels.
597    ///
598    /// See [`WindowResolution`] for an explanation about logical/physical sizes.
599    pub fn set_cursor_position(&mut self, position: Option<Vec2>) {
600        self.internal.physical_cursor_position =
601            position.map(|p| p.as_dvec2() * self.scale_factor() as f64);
602    }
603
604    /// Set the cursor position in this window in physical pixels.
605    ///
606    /// See [`WindowResolution`] for an explanation about logical/physical sizes.
607    pub fn set_physical_cursor_position(&mut self, position: Option<DVec2>) {
608        self.internal.physical_cursor_position = position;
609    }
610}
611
612/// The size limits on a [`Window`].
613///
614/// These values are measured in logical pixels (see [`WindowResolution`]), so the user's
615/// scale factor does affect the size limits on the window.
616///
617/// Please note that if the window is resizable, then when the window is
618/// maximized it may have a size outside of these limits. The functionality
619/// required to disable maximizing is not yet exposed by winit.
620#[derive(Debug, Clone, Copy, PartialEq)]
621#[cfg_attr(
622    feature = "bevy_reflect",
623    derive(Reflect),
624    reflect(Debug, PartialEq, Default, Clone)
625)]
626#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
627#[cfg_attr(
628    all(feature = "serialize", feature = "bevy_reflect"),
629    reflect(Serialize, Deserialize)
630)]
631pub struct WindowResizeConstraints {
632    /// The minimum width the window can have.
633    pub min_width: f32,
634    /// The minimum height the window can have.
635    pub min_height: f32,
636    /// The maximum width the window can have.
637    pub max_width: f32,
638    /// The maximum height the window can have.
639    pub max_height: f32,
640}
641
642impl Default for WindowResizeConstraints {
643    fn default() -> Self {
644        Self {
645            min_width: 180.,
646            min_height: 120.,
647            max_width: f32::INFINITY,
648            max_height: f32::INFINITY,
649        }
650    }
651}
652
653impl WindowResizeConstraints {
654    /// Checks if the constraints are valid.
655    ///
656    /// Will output warnings if it isn't.
657    #[must_use]
658    pub fn check_constraints(&self) -> Self {
659        let &WindowResizeConstraints {
660            mut min_width,
661            mut min_height,
662            mut max_width,
663            mut max_height,
664        } = self;
665        min_width = min_width.max(1.);
666        min_height = min_height.max(1.);
667        if max_width < min_width {
668            warn!(
669                "The given maximum width {} is smaller than the minimum width {}",
670                max_width, min_width
671            );
672            max_width = min_width;
673        }
674        if max_height < min_height {
675            warn!(
676                "The given maximum height {} is smaller than the minimum height {}",
677                max_height, min_height
678            );
679            max_height = min_height;
680        }
681        WindowResizeConstraints {
682            min_width,
683            min_height,
684            max_width,
685            max_height,
686        }
687    }
688}
689
690/// Cursor data for a [`Window`].
691#[derive(Debug, Clone)]
692#[cfg_attr(
693    feature = "bevy_reflect",
694    derive(Reflect),
695    reflect(Debug, Default, Clone)
696)]
697#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
698#[cfg_attr(
699    all(feature = "serialize", feature = "bevy_reflect"),
700    reflect(Serialize, Deserialize)
701)]
702pub struct CursorOptions {
703    /// Whether the cursor is visible or not.
704    ///
705    /// ## Platform-specific
706    ///
707    /// - **`Windows`**, **`X11`**, and **`Wayland`**: The cursor is hidden only when inside the window.
708    ///   To stop the cursor from leaving the window, change [`CursorOptions::grab_mode`] to [`CursorGrabMode::Locked`] or [`CursorGrabMode::Confined`]
709    /// - **`macOS`**: The cursor is hidden only when the window is focused.
710    /// - **`iOS`** and **`Android`** do not have cursors
711    pub visible: bool,
712
713    /// Whether or not the cursor is locked by or confined within the window.
714    ///
715    /// ## Platform-specific
716    ///
717    /// - **`Windows`** doesn't support [`CursorGrabMode::Locked`]
718    /// - **`macOS`** doesn't support [`CursorGrabMode::Confined`]
719    /// - **`iOS/Android`** don't have cursors.
720    ///
721    /// Since `Windows` and `macOS` have different [`CursorGrabMode`] support, we first try to set the grab mode that was asked for. If it doesn't work then use the alternate grab mode.
722    pub grab_mode: CursorGrabMode,
723
724    /// Set whether or not mouse events within *this* window are captured or fall through to the Window below.
725    ///
726    /// ## Platform-specific
727    ///
728    /// - iOS / Android / Web / X11: Unsupported.
729    pub hit_test: bool,
730}
731
732impl Default for CursorOptions {
733    fn default() -> Self {
734        CursorOptions {
735            visible: true,
736            grab_mode: CursorGrabMode::None,
737            hit_test: true,
738        }
739    }
740}
741
742/// Defines where a [`Window`] should be placed on the screen.
743#[derive(Default, Debug, Clone, Copy, PartialEq)]
744#[cfg_attr(
745    feature = "bevy_reflect",
746    derive(Reflect),
747    reflect(Debug, PartialEq, Clone)
748)]
749#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
750#[cfg_attr(
751    all(feature = "serialize", feature = "bevy_reflect"),
752    reflect(Serialize, Deserialize)
753)]
754pub enum WindowPosition {
755    /// Position will be set by the window manager.
756    /// Bevy will delegate this decision to the window manager and no guarantees can be made about where the window will be placed.
757    ///
758    /// Used at creation but will be changed to [`At`](WindowPosition::At).
759    #[default]
760    Automatic,
761    /// Window will be centered on the selected monitor.
762    ///
763    /// Note that this does not account for window decorations.
764    ///
765    /// Used at creation or for update but will be changed to [`At`](WindowPosition::At)
766    Centered(MonitorSelection),
767    /// The window's top-left corner should be placed at the specified position (in physical pixels).
768    ///
769    /// (0,0) represents top-left corner of screen space.
770    At(IVec2),
771}
772
773impl WindowPosition {
774    /// Creates a new [`WindowPosition`] at a position.
775    pub fn new(position: IVec2) -> Self {
776        Self::At(position)
777    }
778
779    /// Set the position to a specific point.
780    pub fn set(&mut self, position: IVec2) {
781        *self = WindowPosition::At(position);
782    }
783
784    /// Set the window to a specific monitor.
785    pub fn center(&mut self, monitor: MonitorSelection) {
786        *self = WindowPosition::Centered(monitor);
787    }
788}
789
790/// Controls the size of a [`Window`]
791///
792/// ## Physical, logical and requested sizes
793///
794/// There are three sizes associated with a window:
795/// - the physical size,
796///   which represents the actual height and width in physical pixels
797///   the window occupies on the monitor,
798/// - the logical size,
799///   which represents the size that should be used to scale elements
800///   inside the window, measured in logical pixels,
801/// - the requested size,
802///   measured in logical pixels, which is the value submitted
803///   to the API when creating the window, or requesting that it be resized.
804///
805/// ## Scale factor
806///
807/// The reason logical size and physical size are separated and can be different
808/// is to account for the cases where:
809/// - several monitors have different pixel densities,
810/// - the user has set up a pixel density preference in its operating system,
811/// - the Bevy `App` has specified a specific scale factor between both.
812///
813/// The factor between physical size and logical size can be retrieved with
814/// [`WindowResolution::scale_factor`].
815///
816/// For the first two cases, a scale factor is set automatically by the operating
817/// system through the window backend. You can get it with
818/// [`WindowResolution::base_scale_factor`].
819///
820/// For the third case, you can override this automatic scale factor with
821/// [`WindowResolution::set_scale_factor_override`].
822///
823/// ## Requested and obtained sizes
824///
825/// The logical size should be equal to the requested size after creating/resizing,
826/// when possible.
827/// The reason the requested size and logical size might be different
828/// is because the corresponding physical size might exceed limits (either the
829/// size limits of the monitor, or limits defined in [`WindowResizeConstraints`]).
830///
831/// Note: The requested size is not kept in memory, for example requesting a size
832/// too big for the screen, making the logical size different from the requested size,
833/// and then setting a scale factor that makes the previous requested size within
834/// the limits of the screen will not get back that previous requested size.
835
836#[derive(Debug, Clone, PartialEq)]
837#[cfg_attr(
838    feature = "bevy_reflect",
839    derive(Reflect),
840    reflect(Debug, PartialEq, Default, Clone)
841)]
842#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
843#[cfg_attr(
844    all(feature = "serialize", feature = "bevy_reflect"),
845    reflect(Serialize, Deserialize)
846)]
847pub struct WindowResolution {
848    /// Width of the window in physical pixels.
849    physical_width: u32,
850    /// Height of the window in physical pixels.
851    physical_height: u32,
852    /// Code-provided ratio of physical size to logical size.
853    ///
854    /// Should be used instead of `scale_factor` when set.
855    scale_factor_override: Option<f32>,
856    /// OS-provided ratio of physical size to logical size.
857    ///
858    /// Set automatically depending on the pixel density of the screen.
859    scale_factor: f32,
860}
861
862impl Default for WindowResolution {
863    fn default() -> Self {
864        WindowResolution {
865            physical_width: 1280,
866            physical_height: 720,
867            scale_factor_override: None,
868            scale_factor: 1.0,
869        }
870    }
871}
872
873impl WindowResolution {
874    /// Creates a new [`WindowResolution`].
875    pub fn new(physical_width: f32, physical_height: f32) -> Self {
876        Self {
877            physical_width: physical_width as u32,
878            physical_height: physical_height as u32,
879            ..Default::default()
880        }
881    }
882
883    /// Builder method for adding a scale factor override to the resolution.
884    pub fn with_scale_factor_override(mut self, scale_factor_override: f32) -> Self {
885        self.set_scale_factor_override(Some(scale_factor_override));
886        self
887    }
888
889    /// The window's client area width in logical pixels.
890    #[inline]
891    pub fn width(&self) -> f32 {
892        self.physical_width() as f32 / self.scale_factor()
893    }
894
895    /// The window's client area height in logical pixels.
896    #[inline]
897    pub fn height(&self) -> f32 {
898        self.physical_height() as f32 / self.scale_factor()
899    }
900
901    /// The window's client size in logical pixels
902    #[inline]
903    pub fn size(&self) -> Vec2 {
904        Vec2::new(self.width(), self.height())
905    }
906
907    /// The window's client area width in physical pixels.
908    #[inline]
909    pub fn physical_width(&self) -> u32 {
910        self.physical_width
911    }
912
913    /// The window's client area height in physical pixels.
914    #[inline]
915    pub fn physical_height(&self) -> u32 {
916        self.physical_height
917    }
918
919    /// The window's client size in physical pixels
920    #[inline]
921    pub fn physical_size(&self) -> UVec2 {
922        UVec2::new(self.physical_width, self.physical_height)
923    }
924
925    /// The ratio of physical pixels to logical pixels.
926    ///
927    /// `physical_pixels = logical_pixels * scale_factor`
928    pub fn scale_factor(&self) -> f32 {
929        self.scale_factor_override
930            .unwrap_or_else(|| self.base_scale_factor())
931    }
932
933    /// The window scale factor as reported by the window backend.
934    ///
935    /// This value is unaffected by [`WindowResolution::scale_factor_override`].
936    #[inline]
937    pub fn base_scale_factor(&self) -> f32 {
938        self.scale_factor
939    }
940
941    /// The scale factor set with [`WindowResolution::set_scale_factor_override`].
942    ///
943    /// This value may be different from the scale factor reported by the window backend.
944    #[inline]
945    pub fn scale_factor_override(&self) -> Option<f32> {
946        self.scale_factor_override
947    }
948
949    /// Set the window's logical resolution.
950    #[inline]
951    pub fn set(&mut self, width: f32, height: f32) {
952        self.set_physical_resolution(
953            (width * self.scale_factor()) as u32,
954            (height * self.scale_factor()) as u32,
955        );
956    }
957
958    /// Set the window's physical resolution.
959    ///
960    /// This will ignore the scale factor setting, so most of the time you should
961    /// prefer to use [`WindowResolution::set`].
962    #[inline]
963    pub fn set_physical_resolution(&mut self, width: u32, height: u32) {
964        self.physical_width = width;
965        self.physical_height = height;
966    }
967
968    /// Set the window's scale factor, this may get overridden by the backend.
969    #[inline]
970    pub fn set_scale_factor(&mut self, scale_factor: f32) {
971        self.scale_factor = scale_factor;
972    }
973
974    /// Set the window's scale factor, and apply it to the currently known physical size.
975    /// This may get overridden by the backend. This is mostly useful on window creation,
976    /// so that the window is created with the expected size instead of waiting for a resize
977    /// event after its creation.
978    #[inline]
979    #[doc(hidden)]
980    pub fn set_scale_factor_and_apply_to_physical_size(&mut self, scale_factor: f32) {
981        self.scale_factor = scale_factor;
982        self.physical_width = (self.physical_width as f32 * scale_factor) as u32;
983        self.physical_height = (self.physical_height as f32 * scale_factor) as u32;
984    }
985
986    /// Set the window's scale factor, this will be used over what the backend decides.
987    ///
988    /// This can change the logical and physical sizes if the resulting physical
989    /// size is not within the limits.
990    #[inline]
991    pub fn set_scale_factor_override(&mut self, scale_factor_override: Option<f32>) {
992        self.scale_factor_override = scale_factor_override;
993    }
994}
995
996impl<I> From<(I, I)> for WindowResolution
997where
998    I: Into<f32>,
999{
1000    fn from((width, height): (I, I)) -> WindowResolution {
1001        WindowResolution::new(width.into(), height.into())
1002    }
1003}
1004
1005impl<I> From<[I; 2]> for WindowResolution
1006where
1007    I: Into<f32>,
1008{
1009    fn from([width, height]: [I; 2]) -> WindowResolution {
1010        WindowResolution::new(width.into(), height.into())
1011    }
1012}
1013
1014impl From<Vec2> for WindowResolution {
1015    fn from(res: Vec2) -> WindowResolution {
1016        WindowResolution::new(res.x, res.y)
1017    }
1018}
1019
1020impl From<DVec2> for WindowResolution {
1021    fn from(res: DVec2) -> WindowResolution {
1022        WindowResolution::new(res.x as f32, res.y as f32)
1023    }
1024}
1025
1026/// Defines if and how the cursor is grabbed by a [`Window`].
1027///
1028/// ## Platform-specific
1029///
1030/// - **`Windows`** doesn't support [`CursorGrabMode::Locked`]
1031/// - **`macOS`** doesn't support [`CursorGrabMode::Confined`]
1032/// - **`iOS/Android`** don't have cursors.
1033///
1034/// Since `Windows` and `macOS` have different [`CursorGrabMode`] support, we first try to set the grab mode that was asked for. If it doesn't work then use the alternate grab mode.
1035#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
1036#[cfg_attr(
1037    feature = "bevy_reflect",
1038    derive(Reflect),
1039    reflect(Debug, PartialEq, Default, Clone)
1040)]
1041#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1042#[cfg_attr(
1043    all(feature = "serialize", feature = "bevy_reflect"),
1044    reflect(Serialize, Deserialize)
1045)]
1046pub enum CursorGrabMode {
1047    /// The cursor can freely leave the window.
1048    #[default]
1049    None,
1050    /// The cursor is confined to the window area.
1051    Confined,
1052    /// The cursor is locked inside the window area to a certain position.
1053    Locked,
1054}
1055
1056/// Stores internal [`Window`] state that isn't directly accessible.
1057#[derive(Default, Debug, Copy, Clone, PartialEq)]
1058#[cfg_attr(
1059    feature = "bevy_reflect",
1060    derive(Reflect),
1061    reflect(Debug, PartialEq, Default, Clone)
1062)]
1063#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1064#[cfg_attr(
1065    all(feature = "serialize", feature = "bevy_reflect"),
1066    reflect(Serialize, Deserialize)
1067)]
1068pub struct InternalWindowState {
1069    /// If this is true then next frame we will ask to minimize the window.
1070    minimize_request: Option<bool>,
1071    /// If this is true then next frame we will ask to maximize/un-maximize the window depending on `maximized`.
1072    maximize_request: Option<bool>,
1073    /// If this is true then next frame we will ask to drag-move the window.
1074    drag_move_request: bool,
1075    /// If this is `Some` then the next frame we will ask to drag-resize the window.
1076    drag_resize_request: Option<CompassOctant>,
1077    /// Unscaled cursor position.
1078    physical_cursor_position: Option<DVec2>,
1079}
1080
1081impl InternalWindowState {
1082    /// Consumes the current maximize request, if it exists. This should only be called by window backends.
1083    pub fn take_maximize_request(&mut self) -> Option<bool> {
1084        self.maximize_request.take()
1085    }
1086
1087    /// Consumes the current minimize request, if it exists. This should only be called by window backends.
1088    pub fn take_minimize_request(&mut self) -> Option<bool> {
1089        self.minimize_request.take()
1090    }
1091
1092    /// Consumes the current move request, if it exists. This should only be called by window backends.
1093    pub fn take_move_request(&mut self) -> bool {
1094        core::mem::take(&mut self.drag_move_request)
1095    }
1096
1097    /// Consumes the current resize request, if it exists. This should only be called by window backends.
1098    pub fn take_resize_request(&mut self) -> Option<CompassOctant> {
1099        self.drag_resize_request.take()
1100    }
1101}
1102
1103/// References a screen monitor.
1104///
1105/// Used when centering a [`Window`] on a monitor.
1106#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1107#[cfg_attr(
1108    feature = "bevy_reflect",
1109    derive(Reflect),
1110    reflect(Debug, PartialEq, Clone)
1111)]
1112#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1113#[cfg_attr(
1114    all(feature = "serialize", feature = "bevy_reflect"),
1115    reflect(Serialize, Deserialize)
1116)]
1117pub enum MonitorSelection {
1118    /// Uses the current monitor of the window.
1119    ///
1120    /// If [`WindowPosition::Centered(MonitorSelection::Current)`](WindowPosition::Centered) is used when creating a window,
1121    /// the window doesn't have a monitor yet, this will fall back to [`WindowPosition::Automatic`].
1122    Current,
1123    /// Uses the primary monitor of the system.
1124    Primary,
1125    /// Uses the monitor with the specified index.
1126    Index(usize),
1127    /// Uses a given [`crate::monitor::Monitor`] entity.
1128    Entity(Entity),
1129}
1130
1131/// References an exclusive fullscreen video mode.
1132///
1133/// Used when setting [`WindowMode::Fullscreen`] on a window.
1134#[derive(Debug, Clone, Copy, PartialEq, Eq, Reflect)]
1135#[cfg_attr(
1136    feature = "serialize",
1137    derive(serde::Serialize, serde::Deserialize),
1138    reflect(Serialize, Deserialize)
1139)]
1140#[reflect(Debug, PartialEq, Clone)]
1141pub enum VideoModeSelection {
1142    /// Uses the video mode that the monitor is already in.
1143    Current,
1144    /// Uses a given [`crate::monitor::VideoMode`]. A list of video modes supported by the monitor
1145    /// is supplied by [`crate::monitor::Monitor::video_modes`].
1146    Specific(VideoMode),
1147}
1148
1149/// Presentation mode for a [`Window`].
1150///
1151/// The presentation mode specifies when a frame is presented to the window. The [`Fifo`]
1152/// option corresponds to a traditional `VSync`, where the framerate is capped by the
1153/// display refresh rate. Both [`Immediate`] and [`Mailbox`] are low-latency and are not
1154/// capped by the refresh rate, but may not be available on all platforms. Tearing
1155/// may be observed with [`Immediate`] mode, but will not be observed with [`Mailbox`] or
1156/// [`Fifo`].
1157///
1158/// [`AutoVsync`] or [`AutoNoVsync`] will gracefully fallback to [`Fifo`] when unavailable.
1159///
1160/// [`Immediate`] or [`Mailbox`] will panic if not supported by the platform.
1161///
1162/// [`Fifo`]: PresentMode::Fifo
1163/// [`FifoRelaxed`]: PresentMode::FifoRelaxed
1164/// [`Immediate`]: PresentMode::Immediate
1165/// [`Mailbox`]: PresentMode::Mailbox
1166/// [`AutoVsync`]: PresentMode::AutoVsync
1167/// [`AutoNoVsync`]: PresentMode::AutoNoVsync
1168#[repr(C)]
1169#[derive(Default, Copy, Clone, Debug, PartialEq, Eq, Hash)]
1170#[cfg_attr(
1171    feature = "bevy_reflect",
1172    derive(Reflect),
1173    reflect(Debug, PartialEq, Hash, Clone)
1174)]
1175#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1176#[cfg_attr(
1177    all(feature = "serialize", feature = "bevy_reflect"),
1178    reflect(Serialize, Deserialize)
1179)]
1180#[doc(alias = "vsync")]
1181pub enum PresentMode {
1182    /// Chooses [`FifoRelaxed`](Self::FifoRelaxed) -> [`Fifo`](Self::Fifo) based on availability.
1183    ///
1184    /// Because of the fallback behavior, it is supported everywhere.
1185    AutoVsync = 0, // NOTE: The explicit ordinal values mirror wgpu.
1186    /// Chooses [`Immediate`](Self::Immediate) -> [`Mailbox`](Self::Mailbox) -> [`Fifo`](Self::Fifo) (on web) based on availability.
1187    ///
1188    /// Because of the fallback behavior, it is supported everywhere.
1189    AutoNoVsync = 1,
1190    /// Presentation frames are kept in a First-In-First-Out queue approximately 3 frames
1191    /// long. Every vertical blanking period, the presentation engine will pop a frame
1192    /// off the queue to display. If there is no frame to display, it will present the same
1193    /// frame again until the next vblank.
1194    ///
1195    /// When a present command is executed on the gpu, the presented image is added on the queue.
1196    ///
1197    /// No tearing will be observed.
1198    ///
1199    /// Calls to `get_current_texture` will block until there is a spot in the queue.
1200    ///
1201    /// Supported on all platforms.
1202    ///
1203    /// If you don't know what mode to choose, choose this mode. This is traditionally called "Vsync On".
1204    #[default]
1205    Fifo = 2,
1206    /// Presentation frames are kept in a First-In-First-Out queue approximately 3 frames
1207    /// long. Every vertical blanking period, the presentation engine will pop a frame
1208    /// off the queue to display. If there is no frame to display, it will present the
1209    /// same frame until there is a frame in the queue. The moment there is a frame in the
1210    /// queue, it will immediately pop the frame off the queue.
1211    ///
1212    /// When a present command is executed on the gpu, the presented image is added on the queue.
1213    ///
1214    /// Tearing will be observed if frames last more than one vblank as the front buffer.
1215    ///
1216    /// Calls to `get_current_texture` will block until there is a spot in the queue.
1217    ///
1218    /// Supported on AMD on Vulkan.
1219    ///
1220    /// This is traditionally called "Adaptive Vsync"
1221    FifoRelaxed = 3,
1222    /// Presentation frames are not queued at all. The moment a present command
1223    /// is executed on the GPU, the presented image is swapped onto the front buffer
1224    /// immediately.
1225    ///
1226    /// Tearing can be observed.
1227    ///
1228    /// Supported on most platforms except older DX12 and Wayland.
1229    ///
1230    /// This is traditionally called "Vsync Off".
1231    Immediate = 4,
1232    /// Presentation frames are kept in a single-frame queue. Every vertical blanking period,
1233    /// the presentation engine will pop a frame from the queue. If there is no frame to display,
1234    /// it will present the same frame again until the next vblank.
1235    ///
1236    /// When a present command is executed on the gpu, the frame will be put into the queue.
1237    /// If there was already a frame in the queue, the new frame will _replace_ the old frame
1238    /// on the queue.
1239    ///
1240    /// No tearing will be observed.
1241    ///
1242    /// Supported on DX11/12 on Windows 10, NVidia on Vulkan and Wayland on Vulkan.
1243    ///
1244    /// This is traditionally called "Fast Vsync"
1245    Mailbox = 5,
1246}
1247
1248/// Specifies how the alpha channel of the textures should be handled during compositing, for a [`Window`].
1249#[repr(C)]
1250#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
1251#[cfg_attr(
1252    feature = "bevy_reflect",
1253    derive(Reflect),
1254    reflect(Debug, PartialEq, Hash, Clone)
1255)]
1256#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1257#[cfg_attr(
1258    all(feature = "serialize", feature = "bevy_reflect"),
1259    reflect(Serialize, Deserialize)
1260)]
1261pub enum CompositeAlphaMode {
1262    /// Chooses either [`Opaque`](CompositeAlphaMode::Opaque) or [`Inherit`](CompositeAlphaMode::Inherit)
1263    /// automatically, depending on the `alpha_mode` that the current surface can support.
1264    #[default]
1265    Auto = 0,
1266    /// The alpha channel, if it exists, of the textures is ignored in the
1267    /// compositing process. Instead, the textures is treated as if it has a
1268    /// constant alpha of 1.0.
1269    Opaque = 1,
1270    /// The alpha channel, if it exists, of the textures is respected in the
1271    /// compositing process. The non-alpha channels of the textures are
1272    /// expected to already be multiplied by the alpha channel by the
1273    /// application.
1274    PreMultiplied = 2,
1275    /// The alpha channel, if it exists, of the textures is respected in the
1276    /// compositing process. The non-alpha channels of the textures are not
1277    /// expected to already be multiplied by the alpha channel by the
1278    /// application; instead, the compositor will multiply the non-alpha
1279    /// channels of the texture by the alpha channel during compositing.
1280    PostMultiplied = 3,
1281    /// The alpha channel, if it exists, of the textures is unknown for processing
1282    /// during compositing. Instead, the application is responsible for setting
1283    /// the composite alpha blending mode using native WSI command. If not set,
1284    /// then a platform-specific default will be used.
1285    Inherit = 4,
1286}
1287
1288/// Defines the way a [`Window`] is displayed.
1289#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
1290#[cfg_attr(
1291    feature = "bevy_reflect",
1292    derive(Reflect),
1293    reflect(Debug, PartialEq, Clone)
1294)]
1295#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1296#[cfg_attr(
1297    all(feature = "serialize", feature = "bevy_reflect"),
1298    reflect(Serialize, Deserialize)
1299)]
1300pub enum WindowMode {
1301    /// The window should take a portion of the screen, using the window resolution size.
1302    #[default]
1303    Windowed,
1304    /// The window should appear fullscreen by being borderless and using the full
1305    /// size of the screen on the given [`MonitorSelection`].
1306    ///
1307    /// When setting this, the window's physical size will be modified to match the size
1308    /// of the current monitor resolution, and the logical size will follow based
1309    /// on the scale factor, see [`WindowResolution`].
1310    ///
1311    /// Note: As this mode respects the scale factor provided by the operating system,
1312    /// the window's logical size may be different from its physical size.
1313    /// If you want to avoid that behavior, you can use the [`WindowResolution::set_scale_factor_override`] function
1314    /// or the [`WindowResolution::with_scale_factor_override`] builder method to set the scale factor to 1.0.
1315    BorderlessFullscreen(MonitorSelection),
1316    /// The window should be in "true"/"legacy"/"exclusive" Fullscreen mode on the given [`MonitorSelection`].
1317    ///
1318    /// The resolution, refresh rate, and bit depth are selected based on the given [`VideoModeSelection`].
1319    ///
1320    /// Note: As this mode respects the scale factor provided by the operating system,
1321    /// the window's logical size may be different from its physical size.
1322    /// If you want to avoid that behavior, you can use the [`WindowResolution::set_scale_factor_override`] function
1323    /// or the [`WindowResolution::with_scale_factor_override`] builder method to set the scale factor to 1.0.
1324    Fullscreen(MonitorSelection, VideoModeSelection),
1325}
1326
1327/// Specifies where a [`Window`] should appear relative to other overlapping windows (on top or under) .
1328///
1329/// Levels are groups of windows with respect to their z-position.
1330///
1331/// The relative ordering between windows in different window levels is fixed.
1332/// The z-order of windows within the same window level may change dynamically on user interaction.
1333///
1334/// ## Platform-specific
1335///
1336/// - **iOS / Android / Web / Wayland:** Unsupported.
1337#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
1338#[cfg_attr(
1339    feature = "bevy_reflect",
1340    derive(Reflect),
1341    reflect(Debug, PartialEq, Clone)
1342)]
1343#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1344#[cfg_attr(
1345    all(feature = "serialize", feature = "bevy_reflect"),
1346    reflect(Serialize, Deserialize)
1347)]
1348pub enum WindowLevel {
1349    /// The window will always be below [`WindowLevel::Normal`] and [`WindowLevel::AlwaysOnTop`] windows.
1350    ///
1351    /// This is useful for a widget-based app.
1352    AlwaysOnBottom,
1353    /// The default group.
1354    #[default]
1355    Normal,
1356    /// The window will always be on top of [`WindowLevel::Normal`] and [`WindowLevel::AlwaysOnBottom`] windows.
1357    AlwaysOnTop,
1358}
1359
1360/// The [`Window`] theme variant to use.
1361#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1362#[cfg_attr(
1363    feature = "bevy_reflect",
1364    derive(Reflect),
1365    reflect(Debug, PartialEq, Clone)
1366)]
1367#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1368#[cfg_attr(
1369    all(feature = "serialize", feature = "bevy_reflect"),
1370    reflect(Serialize, Deserialize)
1371)]
1372pub enum WindowTheme {
1373    /// Use the light variant.
1374    Light,
1375
1376    /// Use the dark variant.
1377    Dark,
1378}
1379
1380/// Specifies which [`Window`] control buttons should be enabled.
1381///
1382/// ## Platform-specific
1383///
1384/// **`iOS`**, **`Android`**, and the **`Web`** do not have window control buttons.
1385///
1386/// On some **`Linux`** environments these values have no effect.
1387#[derive(Debug, Copy, Clone, PartialEq)]
1388#[cfg_attr(
1389    feature = "bevy_reflect",
1390    derive(Reflect),
1391    reflect(Debug, PartialEq, Default, Clone)
1392)]
1393#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1394#[cfg_attr(
1395    all(feature = "serialize", feature = "bevy_reflect"),
1396    reflect(Serialize, Deserialize)
1397)]
1398pub struct EnabledButtons {
1399    /// Enables the functionality of the minimize button.
1400    pub minimize: bool,
1401    /// Enables the functionality of the maximize button.
1402    ///
1403    /// macOS note: When [`Window`] `resizable` member is set to `false`
1404    /// the maximize button will be disabled regardless of this value.
1405    /// Additionally, when `resizable` is set to `true` the window will
1406    /// be maximized when its bar is double-clicked regardless of whether
1407    /// the maximize button is enabled or not.
1408    pub maximize: bool,
1409    /// Enables the functionality of the close button.
1410    pub close: bool,
1411}
1412
1413impl Default for EnabledButtons {
1414    fn default() -> Self {
1415        Self {
1416            minimize: true,
1417            maximize: true,
1418            close: true,
1419        }
1420    }
1421}
1422
1423/// Marker component for a [`Window`] that has been requested to close and
1424/// is in the process of closing (on the next frame).
1425#[derive(Component, Default)]
1426pub struct ClosingWindow;
1427
1428#[cfg(test)]
1429mod tests {
1430    use super::*;
1431
1432    // Checks that `Window::physical_cursor_position` returns the cursor position if it is within
1433    // the bounds of the window.
1434    #[test]
1435    fn cursor_position_within_window_bounds() {
1436        let mut window = Window {
1437            resolution: WindowResolution::new(800., 600.),
1438            ..Default::default()
1439        };
1440
1441        window.set_physical_cursor_position(Some(DVec2::new(0., 300.)));
1442        assert_eq!(window.physical_cursor_position(), Some(Vec2::new(0., 300.)));
1443
1444        window.set_physical_cursor_position(Some(DVec2::new(400., 0.)));
1445        assert_eq!(window.physical_cursor_position(), Some(Vec2::new(400., 0.)));
1446
1447        window.set_physical_cursor_position(Some(DVec2::new(799.999, 300.)));
1448        assert_eq!(
1449            window.physical_cursor_position(),
1450            Some(Vec2::new(799.999, 300.)),
1451        );
1452
1453        window.set_physical_cursor_position(Some(DVec2::new(400., 599.999)));
1454        assert_eq!(
1455            window.physical_cursor_position(),
1456            Some(Vec2::new(400., 599.999))
1457        );
1458    }
1459
1460    // Checks that `Window::physical_cursor_position` returns `None` if the cursor position is not
1461    // within the bounds of the window.
1462    #[test]
1463    fn cursor_position_not_within_window_bounds() {
1464        let mut window = Window {
1465            resolution: WindowResolution::new(800., 600.),
1466            ..Default::default()
1467        };
1468
1469        window.set_physical_cursor_position(Some(DVec2::new(-0.001, 300.)));
1470        assert!(window.physical_cursor_position().is_none());
1471
1472        window.set_physical_cursor_position(Some(DVec2::new(400., -0.001)));
1473        assert!(window.physical_cursor_position().is_none());
1474
1475        window.set_physical_cursor_position(Some(DVec2::new(800., 300.)));
1476        assert!(window.physical_cursor_position().is_none());
1477
1478        window.set_physical_cursor_position(Some(DVec2::new(400., 600.)));
1479        assert!(window.physical_cursor_position().is_none());
1480    }
1481}