Add docs and common helper functions to Windows (#4107)

# Objective

- Improve documentation.
- Provide helper functions for common uses of `Windows` relating to getting the primary `Window`.
- Reduce repeated `Window` code.

# Solution

- Adds infallible `primary()` and `primary_mut()` functions with standard error text. This replaces the commonly used `get_primary().unwrap()` seen throughout bevy which has inconsistent or nonexistent error messages.
- Adds `scale_factor(WindowId)` to replace repeated code blocks throughout.

# Considerations

- The added functions can panic if the primary window does not exist.
    - It is very uncommon for the primary window to not exist, as seen by the regular use of `get_primary().unwrap()`. Most users will have a single window and will need to reference the primary window in their code multiple times.
    - The panic provides a consistent error message to make this class of error easy to spot from the panic text.
    - This follows the established standard of short names for infallible-but-unlikely-to-panic functions in bevy.
- Removes line noise for common usage of `Windows`.
This commit is contained in:
Aevyrie 2022-03-08 00:46:04 +00:00
parent e36c9b6cf0
commit b3aff9a7b1
12 changed files with 55 additions and 39 deletions

View file

@ -9,7 +9,7 @@ use bevy_math::{Size, Vec3};
use bevy_render::{texture::Image, view::Visibility, RenderWorld}; use bevy_render::{texture::Image, view::Visibility, RenderWorld};
use bevy_sprite::{ExtractedSprite, ExtractedSprites, TextureAtlas}; use bevy_sprite::{ExtractedSprite, ExtractedSprites, TextureAtlas};
use bevy_transform::prelude::{GlobalTransform, Transform}; use bevy_transform::prelude::{GlobalTransform, Transform};
use bevy_window::Windows; use bevy_window::{WindowId, Windows};
use crate::{ use crate::{
DefaultTextPipeline, Font, FontAtlasSet, HorizontalAlign, Text, Text2dSize, TextError, DefaultTextPipeline, Font, FontAtlasSet, HorizontalAlign, Text, Text2dSize, TextError,
@ -50,11 +50,7 @@ pub fn extract_text2d_sprite(
) { ) {
let mut extracted_sprites = render_world.resource_mut::<ExtractedSprites>(); let mut extracted_sprites = render_world.resource_mut::<ExtractedSprites>();
let scale_factor = if let Some(window) = windows.get_primary() { let scale_factor = windows.scale_factor(WindowId::primary()) as f32;
window.scale_factor() as f32
} else {
1.
};
for (entity, visibility, text, transform, calculated_size) in text2d_query.iter() { for (entity, visibility, text, transform, calculated_size) in text2d_query.iter() {
if !visibility.is_visible { if !visibility.is_visible {
@ -139,11 +135,7 @@ pub fn text2d_system(
return; return;
} }
let scale_factor = if let Some(window) = windows.get_primary() { let scale_factor = windows.scale_factor(WindowId::primary());
window.scale_factor()
} else {
1.
};
// Computes all text in the local queue // Computes all text in the local queue
let mut new_queue = Vec::new(); let mut new_queue = Vec::new();

View file

@ -218,11 +218,7 @@ pub fn flex_node_system(
} }
// assume one window for time being... // assume one window for time being...
let logical_to_physical_factor = if let Some(primary_window) = windows.get_primary() { let logical_to_physical_factor = windows.scale_factor(WindowId::primary());
primary_window.scale_factor()
} else {
1.
};
if scale_factor_events.iter().next_back().is_some() { if scale_factor_events.iter().next_back().is_some() {
update_changed( update_changed(

View file

@ -30,7 +30,7 @@ use bevy_sprite::{Rect, SpriteAssetEvents, TextureAtlas};
use bevy_text::{DefaultTextPipeline, Text}; use bevy_text::{DefaultTextPipeline, Text};
use bevy_transform::components::GlobalTransform; use bevy_transform::components::GlobalTransform;
use bevy_utils::HashMap; use bevy_utils::HashMap;
use bevy_window::Windows; use bevy_window::{WindowId, Windows};
use bytemuck::{Pod, Zeroable}; use bytemuck::{Pod, Zeroable};
@ -186,11 +186,7 @@ pub fn extract_text_uinodes(
) { ) {
let mut extracted_uinodes = render_world.resource_mut::<ExtractedUiNodes>(); let mut extracted_uinodes = render_world.resource_mut::<ExtractedUiNodes>();
let scale_factor = if let Some(window) = windows.get_primary() { let scale_factor = windows.scale_factor(WindowId::primary()) as f32;
window.scale_factor() as f32
} else {
1.
};
for (entity, uinode, transform, text, visibility, clip) in uinode_query.iter() { for (entity, uinode, transform, text, visibility, clip) in uinode_query.iter() {
if !visibility.is_visible { if !visibility.is_visible {

View file

@ -10,7 +10,7 @@ use bevy_math::Size;
use bevy_render::texture::Image; use bevy_render::texture::Image;
use bevy_sprite::TextureAtlas; use bevy_sprite::TextureAtlas;
use bevy_text::{DefaultTextPipeline, Font, FontAtlasSet, Text, TextError}; use bevy_text::{DefaultTextPipeline, Font, FontAtlasSet, Text, TextError};
use bevy_window::Windows; use bevy_window::{WindowId, Windows};
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct QueuedText { pub struct QueuedText {
@ -52,11 +52,7 @@ pub fn text_system(
QueryState<(&Text, &Style, &mut CalculatedSize)>, QueryState<(&Text, &Style, &mut CalculatedSize)>,
)>, )>,
) { ) {
let scale_factor = if let Some(window) = windows.get_primary() { let scale_factor = windows.scale_factor(WindowId::primary());
window.scale_factor()
} else {
1.
};
let inv_scale_factor = 1. / scale_factor; let inv_scale_factor = 1. / scale_factor;

View file

@ -1,36 +1,72 @@
use super::{Window, WindowId}; use super::{Window, WindowId};
use bevy_utils::HashMap; use bevy_utils::HashMap;
/// A collection of [`Window`]s with unique [`WindowId`]s.
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct Windows { pub struct Windows {
windows: HashMap<WindowId, Window>, windows: HashMap<WindowId, Window>,
} }
impl Windows { impl Windows {
/// Add the provided [`Window`] to the [`Windows`] resource.
pub fn add(&mut self, window: Window) { pub fn add(&mut self, window: Window) {
self.windows.insert(window.id(), window); self.windows.insert(window.id(), window);
} }
/// Get a reference to the [`Window`] of `id`
pub fn get(&self, id: WindowId) -> Option<&Window> { pub fn get(&self, id: WindowId) -> Option<&Window> {
self.windows.get(&id) self.windows.get(&id)
} }
/// Get a mutable reference to the provided [`WindowId`].
pub fn get_mut(&mut self, id: WindowId) -> Option<&mut Window> { pub fn get_mut(&mut self, id: WindowId) -> Option<&mut Window> {
self.windows.get_mut(&id) self.windows.get_mut(&id)
} }
/// Get a reference to the primary [`Window`].
pub fn get_primary(&self) -> Option<&Window> { pub fn get_primary(&self) -> Option<&Window> {
self.get(WindowId::primary()) self.get(WindowId::primary())
} }
/// Get a reference to the primary [`Window`].
///
/// # Panics
///
/// Panics if the primary window does not exist in [`Windows`]
pub fn primary(&self) -> &Window {
self.get_primary().expect("Primary window does not exist")
}
/// Get a mutable reference to the primary [`Window`].
pub fn get_primary_mut(&mut self) -> Option<&mut Window> { pub fn get_primary_mut(&mut self) -> Option<&mut Window> {
self.get_mut(WindowId::primary()) self.get_mut(WindowId::primary())
} }
/// Get a mutable reference to the primary [`Window`].
///
/// # Panics
///
/// Panics if the primary window does not exist in [`Windows`]
pub fn primary_mut(&mut self) -> &mut Window {
self.get_primary_mut()
.expect("Primary window does not exist")
}
/// Returns the scale factor for the [`Window`] of `id`, or `1.0` if the window does not exist.
pub fn scale_factor(&self, id: WindowId) -> f64 {
if let Some(window) = self.get(id) {
window.scale_factor()
} else {
1.0
}
}
/// An iterator over all registered [`Window`]s
pub fn iter(&self) -> impl Iterator<Item = &Window> { pub fn iter(&self) -> impl Iterator<Item = &Window> {
self.windows.values() self.windows.values()
} }
/// A mutable iterator over all registered [`Window`]s
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Window> { pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Window> {
self.windows.values_mut() self.windows.values_mut()
} }

View file

@ -412,7 +412,7 @@ pub fn winit_runner_with(mut app: App) {
// On a mobile window, the start is from the top while on PC/Linux/OSX from // On a mobile window, the start is from the top while on PC/Linux/OSX from
// bottom // bottom
if cfg!(target_os = "android") || cfg!(target_os = "ios") { if cfg!(target_os = "android") || cfg!(target_os = "ios") {
let window_height = windows.get_primary().unwrap().height(); let window_height = windows.primary().height();
location.y = window_height - location.y; location.y = window_height - location.y;
} }
touch_input_events.send(converters::convert_touch_input(touch, location)); touch_input_events.send(converters::convert_touch_input(touch, location));

View file

@ -245,7 +245,7 @@ fn collision_system(
) { ) {
let mut rnd = rand::thread_rng(); let mut rnd = rand::thread_rng();
let window = windows.get_primary().unwrap(); let window = windows.primary();
let ceiling = window.height() / 2.; let ceiling = window.height() / 2.;
let ground = -(window.height() / 2.); let ground = -(window.height() / 2.);

View file

@ -41,7 +41,7 @@ fn bounce_system(
windows: Res<Windows>, windows: Res<Windows>,
mut sprites: Query<(&Transform, &mut Velocity)>, mut sprites: Query<(&Transform, &mut Velocity)>,
) { ) {
let window = windows.get_primary().expect("No primary window."); let window = windows.primary();
let width = window.width(); let width = window.width();
let height = window.height(); let height = window.height();
let left = width / -2.0; let left = width / -2.0;

View file

@ -28,7 +28,7 @@ fn touch_camera(
*last_position = None; *last_position = None;
} }
if let Some(last_position) = *last_position { if let Some(last_position) = *last_position {
let window = windows.get_primary().unwrap(); let window = windows.primary();
let mut transform = camera.single_mut(); let mut transform = camera.single_mut();
*transform = Transform::from_xyz( *transform = Transform::from_xyz(
transform.translation.x transform.translation.x

View file

@ -189,7 +189,7 @@ fn spawn_birds(
spawn_count: usize, spawn_count: usize,
texture: Handle<Image>, texture: Handle<Image>,
) { ) {
let window = windows.get_primary().unwrap(); let window = windows.primary();
let bird_x = (window.width() as f32 / -2.) + HALF_BIRD_SIZE; let bird_x = (window.width() as f32 / -2.) + HALF_BIRD_SIZE;
let bird_y = (window.height() as f32 / 2.) - HALF_BIRD_SIZE; let bird_y = (window.height() as f32 / 2.) - HALF_BIRD_SIZE;
let mut rng = thread_rng(); let mut rng = thread_rng();
@ -230,7 +230,7 @@ fn movement_system(time: Res<Time>, mut bird_query: Query<(&mut Bird, &mut Trans
} }
fn collision_system(windows: Res<Windows>, mut bird_query: Query<(&mut Bird, &Transform)>) { fn collision_system(windows: Res<Windows>, mut bird_query: Query<(&mut Bird, &Transform)>) {
let window = windows.get_primary().unwrap(); let window = windows.primary();
let half_width = window.width() as f32 * 0.5; let half_width = window.width() as f32 * 0.5;
let half_height = window.height() as f32 * 0.5; let half_height = window.height() as f32 * 0.5;

View file

@ -64,7 +64,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
/// This system toggles scale factor overrides when enter is pressed /// This system toggles scale factor overrides when enter is pressed
fn toggle_override(input: Res<Input<KeyCode>>, mut windows: ResMut<Windows>) { fn toggle_override(input: Res<Input<KeyCode>>, mut windows: ResMut<Windows>) {
let window = windows.get_primary_mut().unwrap(); let window = windows.primary_mut();
if input.just_pressed(KeyCode::Return) { if input.just_pressed(KeyCode::Return) {
window.set_scale_factor_override(window.scale_factor_override().xor(Some(1.))); window.set_scale_factor_override(window.scale_factor_override().xor(Some(1.)));
} }
@ -72,7 +72,7 @@ fn toggle_override(input: Res<Input<KeyCode>>, mut windows: ResMut<Windows>) {
/// This system changes the scale factor override when up or down is pressed /// This system changes the scale factor override when up or down is pressed
fn change_scale_factor(input: Res<Input<KeyCode>>, mut windows: ResMut<Windows>) { fn change_scale_factor(input: Res<Input<KeyCode>>, mut windows: ResMut<Windows>) {
let window = windows.get_primary_mut().unwrap(); let window = windows.primary_mut();
if input.just_pressed(KeyCode::Up) { if input.just_pressed(KeyCode::Up) {
window.set_scale_factor_override(window.scale_factor_override().map(|n| n + 1.)); window.set_scale_factor_override(window.scale_factor_override().map(|n| n + 1.));
} else if input.just_pressed(KeyCode::Down) { } else if input.just_pressed(KeyCode::Down) {

View file

@ -19,7 +19,7 @@ fn main() {
/// This system will then change the title during execution /// This system will then change the title during execution
fn change_title(time: Res<Time>, mut windows: ResMut<Windows>) { fn change_title(time: Res<Time>, mut windows: ResMut<Windows>) {
let window = windows.get_primary_mut().unwrap(); let window = windows.primary_mut();
window.set_title(format!( window.set_title(format!(
"Seconds since startup: {}", "Seconds since startup: {}",
time.seconds_since_startup().round() time.seconds_since_startup().round()
@ -28,7 +28,7 @@ fn change_title(time: Res<Time>, mut windows: ResMut<Windows>) {
/// This system toggles the cursor's visibility when the space bar is pressed /// This system toggles the cursor's visibility when the space bar is pressed
fn toggle_cursor(input: Res<Input<KeyCode>>, mut windows: ResMut<Windows>) { fn toggle_cursor(input: Res<Input<KeyCode>>, mut windows: ResMut<Windows>) {
let window = windows.get_primary_mut().unwrap(); let window = windows.primary_mut();
if input.just_pressed(KeyCode::Space) { if input.just_pressed(KeyCode::Space) {
window.set_cursor_lock_mode(!window.cursor_locked()); window.set_cursor_lock_mode(!window.cursor_locked());
window.set_cursor_visibility(!window.cursor_visible()); window.set_cursor_visibility(!window.cursor_visible());
@ -48,7 +48,7 @@ fn cycle_cursor_icon(
CursorIcon::Text, CursorIcon::Text,
CursorIcon::Copy, CursorIcon::Copy,
]; ];
let window = windows.get_primary_mut().unwrap(); let window = windows.primary_mut();
if input.just_pressed(MouseButton::Left) { if input.just_pressed(MouseButton::Left) {
*index = (*index + 1) % ICONS.len(); *index = (*index + 1) % ICONS.len();
window.set_cursor_icon(ICONS[*index]); window.set_cursor_icon(ICONS[*index]);