2023-01-19 00:38:28 +00:00
use bevy_ecs ::{
entity ::{ Entity , EntityMap , MapEntities , MapEntitiesError } ,
prelude ::{ Component , ReflectComponent } ,
} ;
2023-01-20 14:25:24 +00:00
use bevy_math ::{ DVec2 , IVec2 , Vec2 } ;
2022-12-09 01:20:44 +00:00
use bevy_reflect ::{ std_traits ::ReflectDefault , FromReflect , Reflect } ;
2020-04-05 21:12:14 +00:00
2022-12-09 01:20:44 +00:00
#[ cfg(feature = " serialize " ) ]
use bevy_reflect ::{ ReflectDeserialize , ReflectSerialize } ;
2023-01-19 00:38:28 +00:00
use bevy_utils ::tracing ::warn ;
use crate ::CursorIcon ;
/// Marker component for the window considered the primary window.
///
/// Currently this is assumed to only exist on 1 entity at a time.
#[ derive(Default, Debug, Component, PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Reflect) ]
#[ reflect(Component) ]
pub struct PrimaryWindow ;
/// Reference to a window, whether it be a direct link to a specific entity or
/// a more vague defaulting choice.
#[ repr(C) ]
#[ derive(Default, Copy, Clone, Debug, Reflect, FromReflect) ]
2022-12-09 01:20:44 +00:00
#[ cfg_attr(
feature = " serialize " ,
derive ( serde ::Serialize , serde ::Deserialize ) ,
2023-01-19 00:38:28 +00:00
reflect ( Serialize , Deserialize )
2022-12-09 01:20:44 +00:00
) ]
2023-01-19 00:38:28 +00:00
pub enum WindowRef {
/// This will be linked to the primary window that is created by default
/// in the [`WindowPlugin`](crate::WindowPlugin::primary_window).
#[ default ]
Primary ,
/// A more direct link to a window entity.
///
/// Use this if you want to reference a secondary/tertiary/... window.
///
/// To create a new window you can spawn an entity with a [`Window`],
/// then you can use that entity here for usage in cameras.
Entity ( Entity ) ,
}
2020-04-05 21:12:14 +00:00
2023-01-19 00:38:28 +00:00
impl WindowRef {
/// Normalize the window reference so that it can be compared to other window references.
pub fn normalize ( & self , primary_window : Option < Entity > ) -> Option < NormalizedWindowRef > {
let entity = match self {
Self ::Primary = > primary_window ,
Self ::Entity ( entity ) = > Some ( * entity ) ,
} ;
entity . map ( NormalizedWindowRef )
}
}
impl MapEntities for WindowRef {
fn map_entities ( & mut self , entity_map : & EntityMap ) -> Result < ( ) , MapEntitiesError > {
match self {
Self ::Entity ( entity ) = > {
* entity = entity_map . get ( * entity ) ? ;
Ok ( ( ) )
}
Self ::Primary = > Ok ( ( ) ) ,
}
}
}
/// A flattened representation of a window reference for equality/hashing purposes.
2022-10-05 13:51:32 +00:00
///
2023-01-19 00:38:28 +00:00
/// For most purposes you probably want to use the unnormalized version [`WindowRef`].
2022-02-04 03:37:44 +00:00
#[ repr(C) ]
2023-01-19 00:38:28 +00:00
#[ derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Reflect, FromReflect) ]
2022-12-09 01:20:44 +00:00
#[ cfg_attr(
feature = " serialize " ,
derive ( serde ::Serialize , serde ::Deserialize ) ,
reflect ( Serialize , Deserialize )
) ]
2023-01-19 00:38:28 +00:00
pub struct NormalizedWindowRef ( Entity ) ;
impl NormalizedWindowRef {
/// Fetch the entity of this window reference
pub fn entity ( & self ) -> Entity {
self . 0
}
2022-02-04 03:37:44 +00:00
}
2023-01-19 00:38:28 +00:00
/// Define how a window will be created and how it will behave.
#[ derive(Component, Debug, Clone, Reflect, FromReflect) ]
2022-12-09 01:20:44 +00:00
#[ cfg_attr(
feature = " serialize " ,
derive ( serde ::Serialize , serde ::Deserialize ) ,
reflect ( Serialize , Deserialize )
) ]
2023-01-19 00:38:28 +00:00
#[ reflect(Component, Default) ]
pub struct Window {
/// The cursor of this window.
pub cursor : Cursor ,
/// What presentation mode to give the window.
pub present_mode : PresentMode ,
/// Which fullscreen or windowing mode should be used?
pub mode : WindowMode ,
/// Where the window should be placed.
pub position : WindowPosition ,
/// What resolution the window should have.
pub resolution : WindowResolution ,
/// Stores the title of the window.
pub title : String ,
/// How the alpha channel of textures should be handled while compositing.
pub composite_alpha_mode : CompositeAlphaMode ,
/// Which size limits to give the window.
pub resize_constraints : WindowResizeConstraints ,
/// Should the window be resizable?
///
/// Note: This does not stop the program from fullscreening/setting
/// the size programmatically.
pub resizable : bool ,
/// Should the window have decorations enabled?
///
/// (Decorations are the minimize, maximize, and close buttons on desktop apps)
///
// ## Platform-specific
//
// **`iOS`**, **`Android`**, and the **`Web`** do not have decorations.
pub decorations : bool ,
/// Should the window be transparent?
///
/// Defines whether the background of the window should be transparent.
///
/// ## Platform-specific
/// - iOS / Android / Web: Unsupported.
/// - macOS X: Not working as expected.
2023-03-04 14:51:23 +00:00
///
/// macOS X transparent works with winit out of the box, so this issue might be related to: <https://github.com/gfx-rs/wgpu/issues/687>.
/// You should also set the window `composite_alpha_mode` to `CompositeAlphaMode::PostMultiplied`.
2023-01-19 00:38:28 +00:00
pub transparent : bool ,
/// Should the window start focused?
pub focused : bool ,
/// Should the window always be on top of other windows?
///
/// ## Platform-specific
///
/// - iOS / Android / Web / Wayland: Unsupported.
2023-02-03 16:41:39 +00:00
pub window_level : WindowLevel ,
2023-01-19 00:38:28 +00:00
/// The "html canvas" element selector.
///
/// If set, this selector will be used to find a matching html canvas element,
/// rather than creating a new one.
/// Uses the [CSS selector format](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector).
///
/// This value has no effect on non-web platforms.
pub canvas : Option < String > ,
/// Whether or not to fit the canvas element's size to its parent element's size.
///
/// **Warning**: this will not behave as expected for parents that set their size according to the size of their
/// children. This creates a "feedback loop" that will result in the canvas growing on each resize. When using this
/// feature, ensure the parent's size is not affected by its children.
///
/// This value has no effect on non-web platforms.
pub fit_canvas_to_parent : bool ,
Allow not preventing default event behaviors on wasm (#7304)
# Objective
On wasm, bevy applications currently prevent any of the normal browser hotkeys from working normally (Ctrl+R, F12, F5, Ctrl+F5, tab, etc.).
Some of those events you may want to override, perhaps you can hold the tab key for showing in-game stats?
However, if you want to make a well-behaved game, you probably don't want to needlessly prevent that behavior unless you have a good reason.
Secondary motivation: Also, consider the workaround presented here to get audio working: https://developer.chrome.com/blog/web-audio-autoplay/#moving-forward ; It won't work (for keydown events) if we stop event propagation.
## Solution
- Winit has a field that allows it to not stop event propagation, expose it on the window settings to allow the user to choose the desired behavior. Default to `true` for backwards compatibility.
---
## Changelog
- Added `Window::prevent_default_event_handling` . This allows bevy apps to not override default browser behavior on hotkeys like F5, F12, Ctrl+R etc.
2023-01-22 23:35:32 +00:00
/// Whether or not to stop events from propagating out of the canvas element
///
/// When `true`, this will prevent common browser hotkeys like F5, F12, Ctrl+R, tab, etc.
/// from performing their default behavior while the bevy app has focus.
///
/// This value has no effect on non-web platforms.
pub prevent_default_event_handling : bool ,
2023-01-19 00:38:28 +00:00
/// Stores internal state that isn't directly accessible.
pub internal : InternalWindowState ,
2023-01-29 20:27:29 +00:00
/// Should the window use Input Method Editor?
///
/// If enabled, the window will receive [`Ime`](crate::Ime) events instead of
/// [`ReceivedCharacter`](crate::ReceivedCharacter) or
/// [`KeyboardInput`](bevy_input::keyboard::KeyboardInput).
///
/// IME should be enabled during text input, but not when you expect to get the exact key pressed.
///
/// ## Platform-specific
///
/// - iOS / Android / Web: Unsupported.
pub ime_enabled : bool ,
/// Sets location of IME candidate box in client area coordinates relative to the top left.
///
/// ## Platform-specific
///
/// - iOS / Android / Web: Unsupported.
pub ime_position : Vec2 ,
2023-01-19 00:38:28 +00:00
}
impl Default for Window {
fn default ( ) -> Self {
Self {
title : " Bevy App " . to_owned ( ) ,
cursor : Default ::default ( ) ,
present_mode : Default ::default ( ) ,
mode : Default ::default ( ) ,
position : Default ::default ( ) ,
resolution : Default ::default ( ) ,
internal : Default ::default ( ) ,
composite_alpha_mode : Default ::default ( ) ,
resize_constraints : Default ::default ( ) ,
2023-01-29 20:27:29 +00:00
ime_enabled : Default ::default ( ) ,
ime_position : Default ::default ( ) ,
2023-01-19 00:38:28 +00:00
resizable : true ,
decorations : true ,
transparent : false ,
focused : true ,
2023-02-03 16:41:39 +00:00
window_level : Default ::default ( ) ,
2023-01-19 00:38:28 +00:00
fit_canvas_to_parent : false ,
Allow not preventing default event behaviors on wasm (#7304)
# Objective
On wasm, bevy applications currently prevent any of the normal browser hotkeys from working normally (Ctrl+R, F12, F5, Ctrl+F5, tab, etc.).
Some of those events you may want to override, perhaps you can hold the tab key for showing in-game stats?
However, if you want to make a well-behaved game, you probably don't want to needlessly prevent that behavior unless you have a good reason.
Secondary motivation: Also, consider the workaround presented here to get audio working: https://developer.chrome.com/blog/web-audio-autoplay/#moving-forward ; It won't work (for keydown events) if we stop event propagation.
## Solution
- Winit has a field that allows it to not stop event propagation, expose it on the window settings to allow the user to choose the desired behavior. Default to `true` for backwards compatibility.
---
## Changelog
- Added `Window::prevent_default_event_handling` . This allows bevy apps to not override default browser behavior on hotkeys like F5, F12, Ctrl+R etc.
2023-01-22 23:35:32 +00:00
prevent_default_event_handling : true ,
2023-01-19 00:38:28 +00:00
canvas : None ,
}
}
2022-10-24 14:53:19 +00:00
}
2023-01-19 00:38:28 +00:00
impl Window {
/// Setting this to true will attempt to maximize the window.
///
/// Setting it to false will attempt to un-maximize the window.
pub fn set_maximized ( & mut self , maximized : bool ) {
self . internal . maximize_request = Some ( maximized ) ;
2020-06-25 23:02:21 +00:00
}
2023-01-19 00:38:28 +00:00
2023-01-19 04:35:46 +00:00
/// Setting this to true will attempt to minimize the window.
2023-01-19 00:38:28 +00:00
///
2023-01-19 04:35:46 +00:00
/// Setting it to false will attempt to un-minimize the window.
2023-01-19 00:38:28 +00:00
pub fn set_minimized ( & mut self , minimized : bool ) {
self . internal . minimize_request = Some ( minimized ) ;
2020-07-25 06:04:45 +00:00
}
2023-01-19 00:38:28 +00:00
/// The window's client area width in logical pixels.
#[ inline ]
pub fn width ( & self ) -> f32 {
self . resolution . width ( )
2020-07-25 06:04:45 +00:00
}
2020-08-16 07:30:04 +00:00
2023-01-19 00:38:28 +00:00
/// The window's client area height in logical pixels.
#[ inline ]
pub fn height ( & self ) -> f32 {
self . resolution . height ( )
}
2020-07-25 06:04:45 +00:00
2023-01-19 00:38:28 +00:00
/// The window's client area width in physical pixels.
#[ inline ]
pub fn physical_width ( & self ) -> u32 {
self . resolution . physical_width ( )
}
2021-06-02 02:59:17 +00:00
2023-01-19 00:38:28 +00:00
/// The window's client area height in physical pixels.
#[ inline ]
pub fn physical_height ( & self ) -> u32 {
self . resolution . physical_height ( )
2020-04-05 21:12:14 +00:00
}
2023-01-19 00:38:28 +00:00
/// The window's scale factor.
#[ inline ]
pub fn scale_factor ( & self ) -> f64 {
self . resolution . scale_factor ( )
2020-07-25 06:04:45 +00:00
}
2023-01-20 14:25:24 +00:00
/// The cursor position in this window
#[ inline ]
pub fn cursor_position ( & self ) -> Option < Vec2 > {
2023-03-09 05:39:01 +00:00
self . internal
. physical_cursor_position
2023-01-20 14:25:24 +00:00
. map ( | position | ( position / self . scale_factor ( ) ) . as_vec2 ( ) )
}
/// The physical cursor position in this window
#[ inline ]
pub fn physical_cursor_position ( & self ) -> Option < Vec2 > {
2023-03-09 05:39:01 +00:00
self . internal
. physical_cursor_position
2023-01-20 14:25:24 +00:00
. map ( | position | position . as_vec2 ( ) )
}
/// Set the cursor position in this window
pub fn set_cursor_position ( & mut self , position : Option < Vec2 > ) {
2023-03-09 05:39:01 +00:00
self . internal . physical_cursor_position =
position . map ( | p | p . as_dvec2 ( ) * self . scale_factor ( ) ) ;
2023-01-20 14:25:24 +00:00
}
/// Set the physical cursor position in this window
pub fn set_physical_cursor_position ( & mut self , position : Option < DVec2 > ) {
2023-03-09 05:39:01 +00:00
self . internal . physical_cursor_position = position ;
2023-01-20 14:25:24 +00:00
}
2020-07-25 06:04:45 +00:00
}
2021-03-03 02:56:50 +00:00
/// The size limits on a window.
2022-06-16 13:20:37 +00:00
///
2021-03-03 02:56:50 +00:00
/// These values are measured in logical pixels, so the user's
/// scale factor does affect the size limits on the window.
/// Please note that if the window is resizable, then when the window is
/// maximized it may have a size outside of these limits. The functionality
/// required to disable maximizing is not yet exposed by winit.
2022-12-09 01:20:44 +00:00
#[ derive(Debug, Clone, Copy, PartialEq, Reflect, FromReflect) ]
#[ cfg_attr(
feature = " serialize " ,
derive ( serde ::Serialize , serde ::Deserialize ) ,
reflect ( Serialize , Deserialize )
) ]
#[ reflect(Debug, PartialEq, Default) ]
2021-03-03 02:56:50 +00:00
pub struct WindowResizeConstraints {
2023-01-19 00:38:28 +00:00
/// The minimum width the window can have.
2021-03-03 02:56:50 +00:00
pub min_width : f32 ,
2023-01-19 00:38:28 +00:00
/// The minimum height the window can have.
2021-03-03 02:56:50 +00:00
pub min_height : f32 ,
2023-01-19 00:38:28 +00:00
/// The maximum width the window can have.
2021-03-03 02:56:50 +00:00
pub max_width : f32 ,
2023-01-19 00:38:28 +00:00
/// The maximum height the window can have.
2021-03-03 02:56:50 +00:00
pub max_height : f32 ,
}
impl Default for WindowResizeConstraints {
fn default ( ) -> Self {
Self {
min_width : 180. ,
min_height : 120. ,
max_width : f32 ::INFINITY ,
max_height : f32 ::INFINITY ,
}
}
}
impl WindowResizeConstraints {
2023-01-19 00:38:28 +00:00
/// Checks if the constraints are valid.
///
/// Will output warnings if it isn't.
2022-02-13 22:33:55 +00:00
#[ must_use ]
pub fn check_constraints ( & self ) -> Self {
2021-03-03 02:56:50 +00:00
let WindowResizeConstraints {
mut min_width ,
mut min_height ,
mut max_width ,
mut max_height ,
} = self ;
min_width = min_width . max ( 1. ) ;
min_height = min_height . max ( 1. ) ;
if max_width < min_width {
warn! (
" The given maximum width {} is smaller than the minimum width {} " ,
max_width , min_width
) ;
max_width = min_width ;
}
if max_height < min_height {
warn! (
" The given maximum height {} is smaller than the minimum height {} " ,
max_height , min_height
) ;
max_height = min_height ;
}
WindowResizeConstraints {
min_width ,
min_height ,
max_width ,
max_height ,
}
}
}
2023-01-19 00:38:28 +00:00
/// Stores data about the window's cursor.
#[ derive(Debug, Copy, Clone, Reflect, FromReflect) ]
2022-12-09 01:20:44 +00:00
#[ cfg_attr(
feature = " serialize " ,
derive ( serde ::Serialize , serde ::Deserialize ) ,
reflect ( Serialize , Deserialize )
) ]
2023-01-19 00:38:28 +00:00
#[ reflect(Debug, Default) ]
pub struct Cursor {
/// Get the current [`CursorIcon`] while inside the window.
pub icon : CursorIcon ,
/// Whether the cursor is visible or not.
///
/// ## Platform-specific
///
/// - **`Windows`**, **`X11`**, and **`Wayland`**: The cursor is hidden only when inside the window.
/// To stop the cursor from leaving the window, change [`Cursor::grab_mode`] to [`CursorGrabMode::Locked`] or [`CursorGrabMode::Confined`]
/// - **`macOS`**: The cursor is hidden only when the window is focused.
/// - **`iOS`** and **`Android`** do not have cursors
pub visible : bool ,
/// Whether or not the cursor is locked.
///
/// ## Platform-specific
///
/// - **`Windows`** doesn't support [`CursorGrabMode::Locked`]
/// - **`macOS`** doesn't support [`CursorGrabMode::Confined`]
/// - **`iOS/Android`** don't have cursors.
///
/// 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.
pub grab_mode : CursorGrabMode ,
/// Set whether or not mouse events within *this* window are captured or fall through to the Window below.
///
/// ## Platform-specific
///
/// - iOS / Android / Web / X11: Unsupported.
pub hit_test : bool ,
Update `wgpu` to 0.14.0, `naga` to `0.10.0`, `winit` to 0.27.4, `raw-window-handle` to 0.5.0, `ndk` to 0.7 (#6218)
# Objective
- Update `wgpu` to 0.14.0, `naga` to `0.10.0`, `winit` to 0.27.4, `raw-window-handle` to 0.5.0, `ndk` to 0.7.
## Solution
---
## Changelog
### Changed
- Changed `RawWindowHandleWrapper` to `RawHandleWrapper` which wraps both `RawWindowHandle` and `RawDisplayHandle`, which satisfies the `impl HasRawWindowHandle and HasRawDisplayHandle` that `wgpu` 0.14.0 requires.
- Changed `bevy_window::WindowDescriptor`'s `cursor_locked` to `cursor_grab_mode`, change its type from `bool` to `bevy_window::CursorGrabMode`.
## Migration Guide
- Adjust usage of `bevy_window::WindowDescriptor`'s `cursor_locked` to `cursor_grab_mode`, and adjust its type from `bool` to `bevy_window::CursorGrabMode`.
2022-10-19 17:40:23 +00:00
}
2023-01-19 00:38:28 +00:00
impl Default for Cursor {
fn default ( ) -> Self {
Cursor {
icon : CursorIcon ::Default ,
visible : true ,
grab_mode : CursorGrabMode ::None ,
hit_test : true ,
}
}
}
/// Defines where window should be placed at on creation.
#[ derive(Default, Debug, Clone, Copy, PartialEq, Reflect, FromReflect) ]
2022-12-09 01:20:44 +00:00
#[ cfg_attr(
feature = " serialize " ,
derive ( serde ::Serialize , serde ::Deserialize ) ,
reflect ( Serialize , Deserialize )
) ]
#[ reflect(Debug, PartialEq) ]
2023-01-19 00:38:28 +00:00
pub enum WindowPosition {
/// Position will be set by the window manager
#[ default ]
Automatic ,
/// Window will be centered on the selected monitor
2022-06-16 13:20:37 +00:00
///
2023-01-19 00:38:28 +00:00
/// Note that this does not account for window decorations.
Centered ( MonitorSelection ) ,
/// The window's top-left corner will be placed at the specified position (in physical pixels)
///
/// (0,0) represents top-left corner of screen space.
At ( IVec2 ) ,
2020-04-05 21:12:14 +00:00
}
2023-01-19 00:38:28 +00:00
impl WindowPosition {
/// Creates a new [`WindowPosition`] at a position.
pub fn new ( position : IVec2 ) -> Self {
Self ::At ( position )
2020-10-15 18:42:19 +00:00
}
2023-01-19 00:38:28 +00:00
/// Set the position to a specific point.
pub fn set ( & mut self , position : IVec2 ) {
* self = WindowPosition ::At ( position ) ;
2020-12-02 04:25:31 +00:00
}
2023-01-19 00:38:28 +00:00
/// Set the window to a specific monitor.
pub fn center ( & mut self , monitor : MonitorSelection ) {
* self = WindowPosition ::Centered ( monitor ) ;
2020-12-02 04:25:31 +00:00
}
2023-01-19 00:38:28 +00:00
}
2020-12-02 04:25:31 +00:00
2023-01-19 00:38:28 +00:00
/// ## Window Sizes
///
/// There are three sizes associated with a window. The physical size which is
/// the height and width in physical pixels on the monitor. The logical size
/// which is the physical size scaled by an operating system provided factor to
/// account for monitors with differing pixel densities or user preference. And
/// the requested size, measured in logical pixels, which is the value submitted
/// to the API when creating the window, or requesting that it be resized.
///
/// The actual size, in logical pixels, of the window may not match the
/// requested size due to operating system limits on the window size, or the
/// quantization of the logical size when converting the physical size to the
/// logical size through the scaling factor.
#[ derive(Debug, Clone, PartialEq, Reflect, FromReflect) ]
#[ cfg_attr(
feature = " serialize " ,
derive ( serde ::Serialize , serde ::Deserialize ) ,
reflect ( Serialize , Deserialize )
) ]
#[ reflect(Debug, PartialEq, Default) ]
pub struct WindowResolution {
physical_width : u32 ,
physical_height : u32 ,
scale_factor_override : Option < f64 > ,
scale_factor : f64 ,
}
2020-12-04 22:31:17 +00:00
2023-01-19 00:38:28 +00:00
impl Default for WindowResolution {
fn default ( ) -> Self {
WindowResolution {
physical_width : 1280 ,
physical_height : 720 ,
scale_factor_override : None ,
scale_factor : 1.0 ,
}
2021-01-25 04:06:06 +00:00
}
2023-01-19 00:38:28 +00:00
}
2021-01-25 04:06:06 +00:00
2023-01-19 00:38:28 +00:00
impl WindowResolution {
/// Creates a new [`WindowResolution`].
pub fn new ( logical_width : f32 , logical_height : f32 ) -> Self {
Self {
physical_width : logical_width as u32 ,
physical_height : logical_height as u32 ,
.. Default ::default ( )
}
2021-01-25 04:06:06 +00:00
}
2023-01-19 00:38:28 +00:00
/// Builder method for adding a scale factor override to the resolution.
pub fn with_scale_factor_override ( mut self , scale_factor_override : f64 ) -> Self {
self . scale_factor_override = Some ( scale_factor_override ) ;
self
2022-07-04 13:04:14 +00:00
}
2023-01-19 00:38:28 +00:00
/// The window's client area width in logical pixels.
2021-03-03 02:56:50 +00:00
#[ inline ]
2023-01-19 00:38:28 +00:00
pub fn width ( & self ) -> f32 {
( self . physical_width ( ) as f64 / self . scale_factor ( ) ) as f32
2020-10-15 18:42:19 +00:00
}
2023-01-19 00:38:28 +00:00
/// The window's client area width in logical pixels.
2020-12-03 19:30:27 +00:00
#[ inline ]
2023-01-19 00:38:28 +00:00
pub fn height ( & self ) -> f32 {
( self . physical_height ( ) as f64 / self . scale_factor ( ) ) as f32
2020-10-15 18:42:19 +00:00
}
2020-12-01 02:24:49 +00:00
2023-01-19 00:38:28 +00:00
/// The window's client area width in physical pixels.
2020-12-03 19:30:27 +00:00
#[ inline ]
2023-01-19 00:38:28 +00:00
pub fn physical_width ( & self ) -> u32 {
self . physical_width
2020-12-01 02:24:49 +00:00
}
2023-01-19 00:38:28 +00:00
/// The window's client area height in physical pixels.
2021-01-25 04:06:06 +00:00
#[ inline ]
2023-01-19 00:38:28 +00:00
pub fn physical_height ( & self ) -> u32 {
self . physical_height
2021-01-25 04:06:06 +00:00
}
2020-12-13 23:05:56 +00:00
/// The ratio of physical pixels to logical pixels
///
/// `physical_pixels = logical_pixels * scale_factor`
2020-12-01 02:24:49 +00:00
pub fn scale_factor ( & self ) -> f64 {
2020-12-28 20:26:50 +00:00
self . scale_factor_override
2023-01-19 00:38:28 +00:00
. unwrap_or_else ( | | self . base_scale_factor ( ) )
2020-12-28 20:26:50 +00:00
}
/// The window scale factor as reported by the window backend.
2022-06-16 13:20:37 +00:00
///
2023-01-19 00:38:28 +00:00
/// This value is unaffected by [`WindowResolution::scale_factor_override`].
2020-12-28 20:26:50 +00:00
#[ inline ]
2023-01-19 00:38:28 +00:00
pub fn base_scale_factor ( & self ) -> f64 {
self . scale_factor
2020-12-28 20:26:50 +00:00
}
2023-01-19 00:38:28 +00:00
/// The scale factor set with [`WindowResolution::set_scale_factor_override`].
2022-06-16 13:20:37 +00:00
///
/// This value may be different from the scale factor reported by the window backend.
2020-12-28 20:26:50 +00:00
#[ inline ]
pub fn scale_factor_override ( & self ) -> Option < f64 > {
self . scale_factor_override
2020-12-01 02:24:49 +00:00
}
2020-10-15 18:42:19 +00:00
2023-01-19 00:38:28 +00:00
/// Set the window's logical resolution.
2022-10-24 14:53:19 +00:00
#[ inline ]
2023-01-19 00:38:28 +00:00
pub fn set ( & mut self , width : f32 , height : f32 ) {
self . set_physical_resolution (
( width as f64 * self . scale_factor ( ) ) as u32 ,
( height as f64 * self . scale_factor ( ) ) as u32 ,
) ;
2022-10-24 14:53:19 +00:00
}
2023-01-19 00:38:28 +00:00
/// Set the window's physical resolution.
2022-06-16 13:20:37 +00:00
///
2023-01-19 00:38:28 +00:00
/// This will ignore the scale factor setting, so most of the time you should
/// prefer to use [`WindowResolution::set`].
2020-11-11 01:20:31 +00:00
#[ inline ]
2023-01-19 00:38:28 +00:00
pub fn set_physical_resolution ( & mut self , width : u32 , height : u32 ) {
self . physical_width = width ;
self . physical_height = height ;
2021-12-20 22:04:45 +00:00
}
2023-01-27 12:12:53 +00:00
/// Set the window's scale factor, this may get overridden by the backend.
2021-10-15 23:47:42 +00:00
#[ inline ]
2023-01-19 00:38:28 +00:00
pub fn set_scale_factor ( & mut self , scale_factor : f64 ) {
let ( width , height ) = ( self . width ( ) , self . height ( ) ) ;
self . scale_factor = scale_factor ;
self . set ( width , height ) ;
2021-10-15 23:47:42 +00:00
}
2023-01-19 00:38:28 +00:00
/// Set the window's scale factor, this will be used over what the backend decides.
2022-11-21 12:59:10 +00:00
#[ inline ]
2023-01-19 00:38:28 +00:00
pub fn set_scale_factor_override ( & mut self , scale_factor_override : Option < f64 > ) {
let ( width , height ) = ( self . width ( ) , self . height ( ) ) ;
self . scale_factor_override = scale_factor_override ;
self . set ( width , height ) ;
2021-02-13 05:32:32 +00:00
}
2023-01-19 00:38:28 +00:00
}
2021-02-13 05:32:32 +00:00
2023-01-19 00:38:28 +00:00
impl < I > From < ( I , I ) > for WindowResolution
where
I : Into < f32 > ,
{
fn from ( ( width , height ) : ( I , I ) ) -> WindowResolution {
WindowResolution ::new ( width . into ( ) , height . into ( ) )
2022-11-14 22:34:29 +00:00
}
2023-01-19 00:38:28 +00:00
}
2022-11-14 22:34:29 +00:00
2023-01-19 00:38:28 +00:00
impl < I > From < [ I ; 2 ] > for WindowResolution
where
I : Into < f32 > ,
{
fn from ( [ width , height ] : [ I ; 2 ] ) -> WindowResolution {
WindowResolution ::new ( width . into ( ) , height . into ( ) )
2021-06-02 02:59:17 +00:00
}
2023-01-19 00:38:28 +00:00
}
Optionally resize Window canvas element to fit parent element (#4726)
Currently Bevy's web canvases are "fixed size". They are manually set to specific dimensions. This might be fine for some games and website layouts, but for sites with flexible layouts, or games that want to "fill" the browser window, Bevy doesn't provide the tools needed to make this easy out of the box.
There are third party plugins like [bevy-web-resizer](https://github.com/frewsxcv/bevy-web-resizer/) that listen for window resizes, take the new dimensions, and resize the winit window accordingly. However this only covers a subset of cases and this is common enough functionality that it should be baked into Bevy.
A significant motivating use case here is the [Bevy WASM Examples page](https://bevyengine.org/examples/). This scales the canvas to fit smaller windows (such as mobile). But this approach both breaks winit's mouse events and removes pixel-perfect rendering (which means we might be rendering too many or too few pixels). https://github.com/bevyengine/bevy-website/issues/371
In an ideal world, winit would support this behavior out of the box. But unfortunately that seems blocked for now: https://github.com/rust-windowing/winit/pull/2074. And it builds on the ResizeObserver api, which isn't supported in all browsers yet (and is only supported in very new versions of the popular browsers).
While we wait for a complete winit solution, I've added a `fit_canvas_to_parent` option to WindowDescriptor / Window, which when enabled will listen for window resizes and resize the Bevy canvas/window to fit its parent element. This enables users to scale bevy canvases using arbitrary CSS, by "inheriting" their parents' size. Note that the wrapper element _is_ required because winit overrides the canvas sizing with absolute values on each resize.
There is one limitation worth calling out here: while the majority of canvas resizes will be triggered by window resizes, modifying element layout at runtime (css animations, javascript-driven element changes, dev-tool-injected changes, etc) will not be detected here. I'm not aware of a good / efficient event-driven way to do this outside of the ResizeObserver api. In practice, window-resize-driven canvas resizing should cover the majority of use cases. Users that want to actively poll for element resizes can just do that (or we can build another feature and let people choose based on their specific needs).
I also took the chance to make a couple of minor tweaks:
* Made the `canvas` window setting available on all platforms. Users shouldn't need to deal with cargo feature selection to support web scenarios. We can just ignore the value on non-web platforms. I added documentation that explains this.
* Removed the redundant "initial create windows" handler. With the addition of the code in this pr, the code duplication was untenable.
This enables a number of patterns:
## Easy "fullscreen window" mode for the default canvas
The "parent element" defaults to the `<body>` element.
```rust
app
.insert_resource(WindowDescriptor {
fit_canvas_to_parent: true,
..default()
})
```
And CSS:
```css
html, body {
margin: 0;
height: 100%;
}
```
## Fit custom canvas to "wrapper" parent element
```rust
app
.insert_resource(WindowDescriptor {
fit_canvas_to_parent: true,
canvas: Some("#bevy".to_string()),
..default()
})
```
And the HTML:
```html
<div style="width: 50%; height: 100%">
<canvas id="bevy"></canvas>
</div>
```
2022-05-20 23:13:48 +00:00
2023-01-19 00:38:28 +00:00
impl From < bevy_math ::Vec2 > for WindowResolution {
fn from ( res : bevy_math ::Vec2 ) -> WindowResolution {
WindowResolution ::new ( res . x , res . y )
Optionally resize Window canvas element to fit parent element (#4726)
Currently Bevy's web canvases are "fixed size". They are manually set to specific dimensions. This might be fine for some games and website layouts, but for sites with flexible layouts, or games that want to "fill" the browser window, Bevy doesn't provide the tools needed to make this easy out of the box.
There are third party plugins like [bevy-web-resizer](https://github.com/frewsxcv/bevy-web-resizer/) that listen for window resizes, take the new dimensions, and resize the winit window accordingly. However this only covers a subset of cases and this is common enough functionality that it should be baked into Bevy.
A significant motivating use case here is the [Bevy WASM Examples page](https://bevyengine.org/examples/). This scales the canvas to fit smaller windows (such as mobile). But this approach both breaks winit's mouse events and removes pixel-perfect rendering (which means we might be rendering too many or too few pixels). https://github.com/bevyengine/bevy-website/issues/371
In an ideal world, winit would support this behavior out of the box. But unfortunately that seems blocked for now: https://github.com/rust-windowing/winit/pull/2074. And it builds on the ResizeObserver api, which isn't supported in all browsers yet (and is only supported in very new versions of the popular browsers).
While we wait for a complete winit solution, I've added a `fit_canvas_to_parent` option to WindowDescriptor / Window, which when enabled will listen for window resizes and resize the Bevy canvas/window to fit its parent element. This enables users to scale bevy canvases using arbitrary CSS, by "inheriting" their parents' size. Note that the wrapper element _is_ required because winit overrides the canvas sizing with absolute values on each resize.
There is one limitation worth calling out here: while the majority of canvas resizes will be triggered by window resizes, modifying element layout at runtime (css animations, javascript-driven element changes, dev-tool-injected changes, etc) will not be detected here. I'm not aware of a good / efficient event-driven way to do this outside of the ResizeObserver api. In practice, window-resize-driven canvas resizing should cover the majority of use cases. Users that want to actively poll for element resizes can just do that (or we can build another feature and let people choose based on their specific needs).
I also took the chance to make a couple of minor tweaks:
* Made the `canvas` window setting available on all platforms. Users shouldn't need to deal with cargo feature selection to support web scenarios. We can just ignore the value on non-web platforms. I added documentation that explains this.
* Removed the redundant "initial create windows" handler. With the addition of the code in this pr, the code duplication was untenable.
This enables a number of patterns:
## Easy "fullscreen window" mode for the default canvas
The "parent element" defaults to the `<body>` element.
```rust
app
.insert_resource(WindowDescriptor {
fit_canvas_to_parent: true,
..default()
})
```
And CSS:
```css
html, body {
margin: 0;
height: 100%;
}
```
## Fit custom canvas to "wrapper" parent element
```rust
app
.insert_resource(WindowDescriptor {
fit_canvas_to_parent: true,
canvas: Some("#bevy".to_string()),
..default()
})
```
And the HTML:
```html
<div style="width: 50%; height: 100%">
<canvas id="bevy"></canvas>
</div>
```
2022-05-20 23:13:48 +00:00
}
2023-01-19 00:38:28 +00:00
}
Optionally resize Window canvas element to fit parent element (#4726)
Currently Bevy's web canvases are "fixed size". They are manually set to specific dimensions. This might be fine for some games and website layouts, but for sites with flexible layouts, or games that want to "fill" the browser window, Bevy doesn't provide the tools needed to make this easy out of the box.
There are third party plugins like [bevy-web-resizer](https://github.com/frewsxcv/bevy-web-resizer/) that listen for window resizes, take the new dimensions, and resize the winit window accordingly. However this only covers a subset of cases and this is common enough functionality that it should be baked into Bevy.
A significant motivating use case here is the [Bevy WASM Examples page](https://bevyengine.org/examples/). This scales the canvas to fit smaller windows (such as mobile). But this approach both breaks winit's mouse events and removes pixel-perfect rendering (which means we might be rendering too many or too few pixels). https://github.com/bevyengine/bevy-website/issues/371
In an ideal world, winit would support this behavior out of the box. But unfortunately that seems blocked for now: https://github.com/rust-windowing/winit/pull/2074. And it builds on the ResizeObserver api, which isn't supported in all browsers yet (and is only supported in very new versions of the popular browsers).
While we wait for a complete winit solution, I've added a `fit_canvas_to_parent` option to WindowDescriptor / Window, which when enabled will listen for window resizes and resize the Bevy canvas/window to fit its parent element. This enables users to scale bevy canvases using arbitrary CSS, by "inheriting" their parents' size. Note that the wrapper element _is_ required because winit overrides the canvas sizing with absolute values on each resize.
There is one limitation worth calling out here: while the majority of canvas resizes will be triggered by window resizes, modifying element layout at runtime (css animations, javascript-driven element changes, dev-tool-injected changes, etc) will not be detected here. I'm not aware of a good / efficient event-driven way to do this outside of the ResizeObserver api. In practice, window-resize-driven canvas resizing should cover the majority of use cases. Users that want to actively poll for element resizes can just do that (or we can build another feature and let people choose based on their specific needs).
I also took the chance to make a couple of minor tweaks:
* Made the `canvas` window setting available on all platforms. Users shouldn't need to deal with cargo feature selection to support web scenarios. We can just ignore the value on non-web platforms. I added documentation that explains this.
* Removed the redundant "initial create windows" handler. With the addition of the code in this pr, the code duplication was untenable.
This enables a number of patterns:
## Easy "fullscreen window" mode for the default canvas
The "parent element" defaults to the `<body>` element.
```rust
app
.insert_resource(WindowDescriptor {
fit_canvas_to_parent: true,
..default()
})
```
And CSS:
```css
html, body {
margin: 0;
height: 100%;
}
```
## Fit custom canvas to "wrapper" parent element
```rust
app
.insert_resource(WindowDescriptor {
fit_canvas_to_parent: true,
canvas: Some("#bevy".to_string()),
..default()
})
```
And the HTML:
```html
<div style="width: 50%; height: 100%">
<canvas id="bevy"></canvas>
</div>
```
2022-05-20 23:13:48 +00:00
2023-01-19 00:38:28 +00:00
impl From < bevy_math ::DVec2 > for WindowResolution {
fn from ( res : bevy_math ::DVec2 ) -> WindowResolution {
WindowResolution ::new ( res . x as f32 , res . y as f32 )
Optionally resize Window canvas element to fit parent element (#4726)
Currently Bevy's web canvases are "fixed size". They are manually set to specific dimensions. This might be fine for some games and website layouts, but for sites with flexible layouts, or games that want to "fill" the browser window, Bevy doesn't provide the tools needed to make this easy out of the box.
There are third party plugins like [bevy-web-resizer](https://github.com/frewsxcv/bevy-web-resizer/) that listen for window resizes, take the new dimensions, and resize the winit window accordingly. However this only covers a subset of cases and this is common enough functionality that it should be baked into Bevy.
A significant motivating use case here is the [Bevy WASM Examples page](https://bevyengine.org/examples/). This scales the canvas to fit smaller windows (such as mobile). But this approach both breaks winit's mouse events and removes pixel-perfect rendering (which means we might be rendering too many or too few pixels). https://github.com/bevyengine/bevy-website/issues/371
In an ideal world, winit would support this behavior out of the box. But unfortunately that seems blocked for now: https://github.com/rust-windowing/winit/pull/2074. And it builds on the ResizeObserver api, which isn't supported in all browsers yet (and is only supported in very new versions of the popular browsers).
While we wait for a complete winit solution, I've added a `fit_canvas_to_parent` option to WindowDescriptor / Window, which when enabled will listen for window resizes and resize the Bevy canvas/window to fit its parent element. This enables users to scale bevy canvases using arbitrary CSS, by "inheriting" their parents' size. Note that the wrapper element _is_ required because winit overrides the canvas sizing with absolute values on each resize.
There is one limitation worth calling out here: while the majority of canvas resizes will be triggered by window resizes, modifying element layout at runtime (css animations, javascript-driven element changes, dev-tool-injected changes, etc) will not be detected here. I'm not aware of a good / efficient event-driven way to do this outside of the ResizeObserver api. In practice, window-resize-driven canvas resizing should cover the majority of use cases. Users that want to actively poll for element resizes can just do that (or we can build another feature and let people choose based on their specific needs).
I also took the chance to make a couple of minor tweaks:
* Made the `canvas` window setting available on all platforms. Users shouldn't need to deal with cargo feature selection to support web scenarios. We can just ignore the value on non-web platforms. I added documentation that explains this.
* Removed the redundant "initial create windows" handler. With the addition of the code in this pr, the code duplication was untenable.
This enables a number of patterns:
## Easy "fullscreen window" mode for the default canvas
The "parent element" defaults to the `<body>` element.
```rust
app
.insert_resource(WindowDescriptor {
fit_canvas_to_parent: true,
..default()
})
```
And CSS:
```css
html, body {
margin: 0;
height: 100%;
}
```
## Fit custom canvas to "wrapper" parent element
```rust
app
.insert_resource(WindowDescriptor {
fit_canvas_to_parent: true,
canvas: Some("#bevy".to_string()),
..default()
})
```
And the HTML:
```html
<div style="width: 50%; height: 100%">
<canvas id="bevy"></canvas>
</div>
```
2022-05-20 23:13:48 +00:00
}
2020-04-05 21:12:14 +00:00
}
2023-01-19 00:38:28 +00:00
/// Defines if and how the cursor is grabbed.
///
/// ## Platform-specific
///
/// - **`Windows`** doesn't support [`CursorGrabMode::Locked`]
/// - **`macOS`** doesn't support [`CursorGrabMode::Confined`]
/// - **`iOS/Android`** don't have cursors.
///
/// 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.
#[ derive(Default, Debug, Clone, Copy, PartialEq, Eq, Reflect, FromReflect) ]
2022-12-09 01:20:44 +00:00
#[ cfg_attr(
feature = " serialize " ,
derive ( serde ::Serialize , serde ::Deserialize ) ,
reflect ( Serialize , Deserialize )
) ]
2023-01-19 00:38:28 +00:00
#[ reflect(Debug, PartialEq, Default) ]
pub enum CursorGrabMode {
/// The cursor can freely leave the window.
#[ default ]
None ,
/// The cursor is confined to the window area.
Confined ,
/// The cursor is locked inside the window area to a certain position.
Locked ,
}
/// Stores internal state that isn't directly accessible.
2023-03-09 05:39:01 +00:00
#[ derive(Default, Debug, Copy, Clone, PartialEq, Reflect, FromReflect) ]
2023-01-19 00:38:28 +00:00
#[ cfg_attr(
feature = " serialize " ,
derive ( serde ::Serialize , serde ::Deserialize ) ,
reflect ( Serialize , Deserialize )
) ]
#[ reflect(Debug, PartialEq, Default) ]
pub struct InternalWindowState {
/// If this is true then next frame we will ask to minimize the window.
minimize_request : Option < bool > ,
/// If this is true then next frame we will ask to maximize/un-maximize the window depending on `maximized`.
maximize_request : Option < bool > ,
2023-03-09 05:39:01 +00:00
/// Unscaled cursor position.
physical_cursor_position : Option < DVec2 > ,
2023-01-19 00:38:28 +00:00
}
impl InternalWindowState {
/// Consumes the current maximize request, if it exists. This should only be called by window backends.
pub fn take_maximize_request ( & mut self ) -> Option < bool > {
self . maximize_request . take ( )
}
/// Consumes the current minimize request, if it exists. This should only be called by window backends.
pub fn take_minimize_request ( & mut self ) -> Option < bool > {
self . minimize_request . take ( )
}
2022-07-04 13:04:14 +00:00
}
/// Defines which monitor to use.
2022-12-09 01:20:44 +00:00
#[ derive(Debug, Clone, Copy, PartialEq, Eq, Reflect, FromReflect) ]
#[ cfg_attr(
feature = " serialize " ,
derive ( serde ::Serialize , serde ::Deserialize ) ,
reflect ( Serialize , Deserialize )
) ]
#[ reflect(Debug, PartialEq) ]
2022-07-04 13:04:14 +00:00
pub enum MonitorSelection {
/// Uses current monitor of the window.
2022-09-06 14:45:44 +00:00
///
/// Will fall back to the system default if the window has not yet been created.
2022-07-04 13:04:14 +00:00
Current ,
/// Uses primary monitor of the system.
Primary ,
/// Uses monitor with the specified index.
2022-09-06 14:45:44 +00:00
Index ( usize ) ,
2022-07-04 13:04:14 +00:00
}
2023-01-19 00:38:28 +00:00
/// Presentation mode for a window.
2022-05-30 16:59:41 +00:00
///
2023-04-23 17:28:36 +00:00
/// The presentation mode specifies when a frame is presented to the window. The [`Fifo`]
2023-01-19 00:38:28 +00:00
/// option corresponds to a traditional `VSync`, where the framerate is capped by the
2023-04-23 17:28:36 +00:00
/// display refresh rate. Both [`Immediate`] and [`Mailbox`] are low-latency and are not
2023-01-19 00:38:28 +00:00
/// capped by the refresh rate, but may not be available on all platforms. Tearing
2023-04-23 17:28:36 +00:00
/// may be observed with [`Immediate`] mode, but will not be observed with [`Mailbox`] or
/// [`Fifo`].
2022-05-30 16:59:41 +00:00
///
2023-04-23 17:28:36 +00:00
/// [`AutoVsync`] or [`AutoNoVsync`] will gracefully fallback to [`Fifo`] when unavailable.
///
/// [`Immediate`] or [`Mailbox`] will panic if not supported by the platform.
///
/// [`Fifo`]: PresentMode::Fifo
/// [`Immediate`]: PresentMode::Immediate
/// [`Mailbox`]: PresentMode::Mailbox
/// [`AutoVsync`]: PresentMode::AutoVsync
/// [`AutoNoVsync`]: PresentMode::AutoNoVsync
2022-05-30 16:59:41 +00:00
///
2023-01-19 00:38:28 +00:00
#[ repr(C) ]
#[ derive(Default, Copy, Clone, Debug, PartialEq, Eq, Hash, Reflect, FromReflect) ]
2022-12-09 01:20:44 +00:00
#[ cfg_attr(
feature = " serialize " ,
derive ( serde ::Serialize , serde ::Deserialize ) ,
reflect ( Serialize , Deserialize )
) ]
2023-01-19 00:38:28 +00:00
#[ reflect(Debug, PartialEq, Hash) ]
#[ doc(alias = " vsync " ) ]
pub enum PresentMode {
/// Chooses FifoRelaxed -> Fifo based on availability.
Optionally resize Window canvas element to fit parent element (#4726)
Currently Bevy's web canvases are "fixed size". They are manually set to specific dimensions. This might be fine for some games and website layouts, but for sites with flexible layouts, or games that want to "fill" the browser window, Bevy doesn't provide the tools needed to make this easy out of the box.
There are third party plugins like [bevy-web-resizer](https://github.com/frewsxcv/bevy-web-resizer/) that listen for window resizes, take the new dimensions, and resize the winit window accordingly. However this only covers a subset of cases and this is common enough functionality that it should be baked into Bevy.
A significant motivating use case here is the [Bevy WASM Examples page](https://bevyengine.org/examples/). This scales the canvas to fit smaller windows (such as mobile). But this approach both breaks winit's mouse events and removes pixel-perfect rendering (which means we might be rendering too many or too few pixels). https://github.com/bevyengine/bevy-website/issues/371
In an ideal world, winit would support this behavior out of the box. But unfortunately that seems blocked for now: https://github.com/rust-windowing/winit/pull/2074. And it builds on the ResizeObserver api, which isn't supported in all browsers yet (and is only supported in very new versions of the popular browsers).
While we wait for a complete winit solution, I've added a `fit_canvas_to_parent` option to WindowDescriptor / Window, which when enabled will listen for window resizes and resize the Bevy canvas/window to fit its parent element. This enables users to scale bevy canvases using arbitrary CSS, by "inheriting" their parents' size. Note that the wrapper element _is_ required because winit overrides the canvas sizing with absolute values on each resize.
There is one limitation worth calling out here: while the majority of canvas resizes will be triggered by window resizes, modifying element layout at runtime (css animations, javascript-driven element changes, dev-tool-injected changes, etc) will not be detected here. I'm not aware of a good / efficient event-driven way to do this outside of the ResizeObserver api. In practice, window-resize-driven canvas resizing should cover the majority of use cases. Users that want to actively poll for element resizes can just do that (or we can build another feature and let people choose based on their specific needs).
I also took the chance to make a couple of minor tweaks:
* Made the `canvas` window setting available on all platforms. Users shouldn't need to deal with cargo feature selection to support web scenarios. We can just ignore the value on non-web platforms. I added documentation that explains this.
* Removed the redundant "initial create windows" handler. With the addition of the code in this pr, the code duplication was untenable.
This enables a number of patterns:
## Easy "fullscreen window" mode for the default canvas
The "parent element" defaults to the `<body>` element.
```rust
app
.insert_resource(WindowDescriptor {
fit_canvas_to_parent: true,
..default()
})
```
And CSS:
```css
html, body {
margin: 0;
height: 100%;
}
```
## Fit custom canvas to "wrapper" parent element
```rust
app
.insert_resource(WindowDescriptor {
fit_canvas_to_parent: true,
canvas: Some("#bevy".to_string()),
..default()
})
```
And the HTML:
```html
<div style="width: 50%; height: 100%">
<canvas id="bevy"></canvas>
</div>
```
2022-05-20 23:13:48 +00:00
///
2023-01-19 00:38:28 +00:00
/// Because of the fallback behavior, it is supported everywhere.
AutoVsync = 0 ,
/// Chooses Immediate -> Mailbox -> Fifo (on web) based on availability.
Optionally resize Window canvas element to fit parent element (#4726)
Currently Bevy's web canvases are "fixed size". They are manually set to specific dimensions. This might be fine for some games and website layouts, but for sites with flexible layouts, or games that want to "fill" the browser window, Bevy doesn't provide the tools needed to make this easy out of the box.
There are third party plugins like [bevy-web-resizer](https://github.com/frewsxcv/bevy-web-resizer/) that listen for window resizes, take the new dimensions, and resize the winit window accordingly. However this only covers a subset of cases and this is common enough functionality that it should be baked into Bevy.
A significant motivating use case here is the [Bevy WASM Examples page](https://bevyengine.org/examples/). This scales the canvas to fit smaller windows (such as mobile). But this approach both breaks winit's mouse events and removes pixel-perfect rendering (which means we might be rendering too many or too few pixels). https://github.com/bevyengine/bevy-website/issues/371
In an ideal world, winit would support this behavior out of the box. But unfortunately that seems blocked for now: https://github.com/rust-windowing/winit/pull/2074. And it builds on the ResizeObserver api, which isn't supported in all browsers yet (and is only supported in very new versions of the popular browsers).
While we wait for a complete winit solution, I've added a `fit_canvas_to_parent` option to WindowDescriptor / Window, which when enabled will listen for window resizes and resize the Bevy canvas/window to fit its parent element. This enables users to scale bevy canvases using arbitrary CSS, by "inheriting" their parents' size. Note that the wrapper element _is_ required because winit overrides the canvas sizing with absolute values on each resize.
There is one limitation worth calling out here: while the majority of canvas resizes will be triggered by window resizes, modifying element layout at runtime (css animations, javascript-driven element changes, dev-tool-injected changes, etc) will not be detected here. I'm not aware of a good / efficient event-driven way to do this outside of the ResizeObserver api. In practice, window-resize-driven canvas resizing should cover the majority of use cases. Users that want to actively poll for element resizes can just do that (or we can build another feature and let people choose based on their specific needs).
I also took the chance to make a couple of minor tweaks:
* Made the `canvas` window setting available on all platforms. Users shouldn't need to deal with cargo feature selection to support web scenarios. We can just ignore the value on non-web platforms. I added documentation that explains this.
* Removed the redundant "initial create windows" handler. With the addition of the code in this pr, the code duplication was untenable.
This enables a number of patterns:
## Easy "fullscreen window" mode for the default canvas
The "parent element" defaults to the `<body>` element.
```rust
app
.insert_resource(WindowDescriptor {
fit_canvas_to_parent: true,
..default()
})
```
And CSS:
```css
html, body {
margin: 0;
height: 100%;
}
```
## Fit custom canvas to "wrapper" parent element
```rust
app
.insert_resource(WindowDescriptor {
fit_canvas_to_parent: true,
canvas: Some("#bevy".to_string()),
..default()
})
```
And the HTML:
```html
<div style="width: 50%; height: 100%">
<canvas id="bevy"></canvas>
</div>
```
2022-05-20 23:13:48 +00:00
///
2023-01-19 00:38:28 +00:00
/// Because of the fallback behavior, it is supported everywhere.
AutoNoVsync = 1 ,
/// The presentation engine does **not** wait for a vertical blanking period and
/// the request is presented immediately. This is a low-latency presentation mode,
/// but visible tearing may be observed. Not optimal for mobile.
Optionally resize Window canvas element to fit parent element (#4726)
Currently Bevy's web canvases are "fixed size". They are manually set to specific dimensions. This might be fine for some games and website layouts, but for sites with flexible layouts, or games that want to "fill" the browser window, Bevy doesn't provide the tools needed to make this easy out of the box.
There are third party plugins like [bevy-web-resizer](https://github.com/frewsxcv/bevy-web-resizer/) that listen for window resizes, take the new dimensions, and resize the winit window accordingly. However this only covers a subset of cases and this is common enough functionality that it should be baked into Bevy.
A significant motivating use case here is the [Bevy WASM Examples page](https://bevyengine.org/examples/). This scales the canvas to fit smaller windows (such as mobile). But this approach both breaks winit's mouse events and removes pixel-perfect rendering (which means we might be rendering too many or too few pixels). https://github.com/bevyengine/bevy-website/issues/371
In an ideal world, winit would support this behavior out of the box. But unfortunately that seems blocked for now: https://github.com/rust-windowing/winit/pull/2074. And it builds on the ResizeObserver api, which isn't supported in all browsers yet (and is only supported in very new versions of the popular browsers).
While we wait for a complete winit solution, I've added a `fit_canvas_to_parent` option to WindowDescriptor / Window, which when enabled will listen for window resizes and resize the Bevy canvas/window to fit its parent element. This enables users to scale bevy canvases using arbitrary CSS, by "inheriting" their parents' size. Note that the wrapper element _is_ required because winit overrides the canvas sizing with absolute values on each resize.
There is one limitation worth calling out here: while the majority of canvas resizes will be triggered by window resizes, modifying element layout at runtime (css animations, javascript-driven element changes, dev-tool-injected changes, etc) will not be detected here. I'm not aware of a good / efficient event-driven way to do this outside of the ResizeObserver api. In practice, window-resize-driven canvas resizing should cover the majority of use cases. Users that want to actively poll for element resizes can just do that (or we can build another feature and let people choose based on their specific needs).
I also took the chance to make a couple of minor tweaks:
* Made the `canvas` window setting available on all platforms. Users shouldn't need to deal with cargo feature selection to support web scenarios. We can just ignore the value on non-web platforms. I added documentation that explains this.
* Removed the redundant "initial create windows" handler. With the addition of the code in this pr, the code duplication was untenable.
This enables a number of patterns:
## Easy "fullscreen window" mode for the default canvas
The "parent element" defaults to the `<body>` element.
```rust
app
.insert_resource(WindowDescriptor {
fit_canvas_to_parent: true,
..default()
})
```
And CSS:
```css
html, body {
margin: 0;
height: 100%;
}
```
## Fit custom canvas to "wrapper" parent element
```rust
app
.insert_resource(WindowDescriptor {
fit_canvas_to_parent: true,
canvas: Some("#bevy".to_string()),
..default()
})
```
And the HTML:
```html
<div style="width: 50%; height: 100%">
<canvas id="bevy"></canvas>
</div>
```
2022-05-20 23:13:48 +00:00
///
2023-01-19 00:38:28 +00:00
/// Selecting this variant will panic if not supported, it is preferred to use
/// [`PresentMode::AutoNoVsync`].
Immediate = 2 ,
/// The presentation engine waits for the next vertical blanking period to update
/// the current image, but frames may be submitted without delay. This is a low-latency
/// presentation mode and visible tearing will **not** be observed. Not optimal for mobile.
2022-11-14 22:34:29 +00:00
///
2023-01-19 00:38:28 +00:00
/// Selecting this variant will panic if not supported, it is preferred to use
/// [`PresentMode::AutoNoVsync`].
Mailbox = 3 ,
/// The presentation engine waits for the next vertical blanking period to update
/// the current image. The framerate will be capped at the display refresh rate,
/// corresponding to the `VSync`. Tearing cannot be observed. Optimal for mobile.
#[ default ]
Fifo = 4 , // NOTE: The explicit ordinal values mirror wgpu.
2020-04-05 21:12:14 +00:00
}
2023-01-19 00:38:28 +00:00
/// Specifies how the alpha channel of the textures should be handled during compositing.
#[ repr(C) ]
#[ derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash, Reflect, FromReflect) ]
#[ cfg_attr(
feature = " serialize " ,
derive ( serde ::Serialize , serde ::Deserialize ) ,
reflect ( Serialize , Deserialize )
) ]
#[ reflect(Debug, PartialEq, Hash) ]
pub enum CompositeAlphaMode {
2023-04-23 17:28:36 +00:00
/// Chooses either [`Opaque`](CompositeAlphaMode::Opaque) or [`Inherit`](CompositeAlphaMode::Inherit)
/// automatically, depending on the `alpha_mode` that the current surface can support.
2023-01-19 00:38:28 +00:00
#[ default ]
Auto = 0 ,
/// The alpha channel, if it exists, of the textures is ignored in the
/// compositing process. Instead, the textures is treated as if it has a
/// constant alpha of 1.0.
Opaque = 1 ,
/// The alpha channel, if it exists, of the textures is respected in the
/// compositing process. The non-alpha channels of the textures are
/// expected to already be multiplied by the alpha channel by the
/// application.
PreMultiplied = 2 ,
/// The alpha channel, if it exists, of the textures is respected in the
/// compositing process. The non-alpha channels of the textures are not
/// expected to already be multiplied by the alpha channel by the
/// application; instead, the compositor will multiply the non-alpha
/// channels of the texture by the alpha channel during compositing.
PostMultiplied = 3 ,
/// The alpha channel, if it exists, of the textures is unknown for processing
/// during compositing. Instead, the application is responsible for setting
/// the composite alpha blending mode using native WSI command. If not set,
/// then a platform-specific default will be used.
Inherit = 4 ,
}
/// Defines the way a window is displayed
#[ derive(Default, Debug, Clone, Copy, PartialEq, Eq, Reflect, FromReflect) ]
#[ cfg_attr(
feature = " serialize " ,
derive ( serde ::Serialize , serde ::Deserialize ) ,
reflect ( Serialize , Deserialize )
) ]
#[ reflect(Debug, PartialEq) ]
pub enum WindowMode {
/// Creates a window that uses the given size.
#[ default ]
Windowed ,
/// Creates a borderless window that uses the full size of the screen.
BorderlessFullscreen ,
/// Creates a fullscreen window that will render at desktop resolution. The app will use the closest supported size
/// from the given size and scale it to fit the screen.
SizedFullscreen ,
/// Creates a fullscreen window that uses the maximum supported size.
Fullscreen ,
2020-04-06 23:15:59 +00:00
}
2023-02-03 16:41:39 +00:00
/// A window level groups windows with respect to their z-position.
///
/// The relative ordering between windows in different window levels is fixed.
/// The z-order of a window within the same window level may change dynamically on user interaction.
///
/// ## Platform-specific
///
/// - **iOS / Android / Web / Wayland:** Unsupported.
#[ derive(Default, Debug, Clone, Copy, PartialEq, Eq, Reflect, FromReflect) ]
#[ cfg_attr(
feature = " serialize " ,
derive ( serde ::Serialize , serde ::Deserialize ) ,
reflect ( Serialize , Deserialize )
) ]
#[ reflect(Debug, PartialEq) ]
pub enum WindowLevel {
/// The window will always be below normal windows.
///
/// This is useful for a widget-based app.
AlwaysOnBottom ,
/// The default.
#[ default ]
Normal ,
/// The window will always be on top of normal windows.
AlwaysOnTop ,
}