Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

View File

@@ -0,0 +1,24 @@
//! Shows how to set the solid color that is used to paint the window before the frame gets drawn.
//!
//! Acts as background color, since pixels that are not drawn in a frame remain unchanged.
use bevy::{color::palettes::css::PURPLE, prelude::*};
fn main() {
App::new()
.insert_resource(ClearColor(Color::srgb(0.5, 0.5, 0.9)))
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.add_systems(Update, change_clear_color)
.run();
}
fn setup(mut commands: Commands) {
commands.spawn(Camera2d);
}
fn change_clear_color(input: Res<ButtonInput<KeyCode>>, mut clear_color: ResMut<ClearColor>) {
if input.just_pressed(KeyCode::Space) {
clear_color.0 = PURPLE.into();
}
}

View File

@@ -0,0 +1,227 @@
//! Illustrates how to use a custom cursor image with a texture atlas and
//! animation.
use std::time::Duration;
use bevy::{
prelude::*,
winit::cursor::{CursorIcon, CustomCursor, CustomCursorImage},
};
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(
Startup,
(setup_cursor_icon, setup_camera, setup_instructions),
)
.add_systems(
Update,
(
execute_animation,
toggle_texture_atlas,
toggle_flip_x,
toggle_flip_y,
cycle_rect,
),
)
.run();
}
fn setup_cursor_icon(
mut commands: Commands,
asset_server: Res<AssetServer>,
mut texture_atlas_layouts: ResMut<Assets<TextureAtlasLayout>>,
window: Single<Entity, With<Window>>,
) {
let layout =
TextureAtlasLayout::from_grid(UVec2::splat(64), 20, 10, Some(UVec2::splat(5)), None);
let texture_atlas_layout = texture_atlas_layouts.add(layout);
let animation_config = AnimationConfig::new(0, 199, 1, 4);
commands.entity(*window).insert((
CursorIcon::Custom(CustomCursor::Image(CustomCursorImage {
// Image to use as the cursor.
handle: asset_server
.load("cursors/kenney_crosshairPack/Tilesheet/crosshairs_tilesheet_white.png"),
// Optional texture atlas allows you to pick a section of the image
// and animate it.
texture_atlas: Some(TextureAtlas {
layout: texture_atlas_layout.clone(),
index: animation_config.first_sprite_index,
}),
flip_x: false,
flip_y: false,
// Optional section of the image to use as the cursor.
rect: None,
// The hotspot is the point in the cursor image that will be
// positioned at the mouse cursor's position.
hotspot: (0, 0),
})),
animation_config,
));
}
fn setup_camera(mut commands: Commands) {
commands.spawn(Camera3d::default());
}
fn setup_instructions(mut commands: Commands) {
commands.spawn((
Text::new(
"Press T to toggle the cursor's `texture_atlas`.\n
Press X to toggle the cursor's `flip_x` setting.\n
Press Y to toggle the cursor's `flip_y` setting.\n
Press C to cycle through the sections of the cursor's image using `rect`.",
),
Node {
position_type: PositionType::Absolute,
bottom: Val::Px(12.0),
left: Val::Px(12.0),
..default()
},
));
}
#[derive(Component)]
struct AnimationConfig {
first_sprite_index: usize,
last_sprite_index: usize,
increment: usize,
fps: u8,
frame_timer: Timer,
}
impl AnimationConfig {
fn new(first: usize, last: usize, increment: usize, fps: u8) -> Self {
Self {
first_sprite_index: first,
last_sprite_index: last,
increment,
fps,
frame_timer: Self::timer_from_fps(fps),
}
}
fn timer_from_fps(fps: u8) -> Timer {
Timer::new(Duration::from_secs_f32(1.0 / (fps as f32)), TimerMode::Once)
}
}
/// This system loops through all the sprites in the [`CursorIcon`]'s
/// [`TextureAtlas`], from [`AnimationConfig`]'s `first_sprite_index` to
/// `last_sprite_index`.
fn execute_animation(time: Res<Time>, mut query: Query<(&mut AnimationConfig, &mut CursorIcon)>) {
for (mut config, mut cursor_icon) in &mut query {
if let CursorIcon::Custom(CustomCursor::Image(ref mut image)) = *cursor_icon {
config.frame_timer.tick(time.delta());
if config.frame_timer.finished() {
if let Some(atlas) = image.texture_atlas.as_mut() {
atlas.index += config.increment;
if atlas.index > config.last_sprite_index {
atlas.index = config.first_sprite_index;
}
config.frame_timer = AnimationConfig::timer_from_fps(config.fps);
}
}
}
}
}
fn toggle_texture_atlas(
input: Res<ButtonInput<KeyCode>>,
mut query: Query<&mut CursorIcon, With<Window>>,
mut cached_atlas: Local<Option<TextureAtlas>>, // this lets us restore the previous value
) {
if input.just_pressed(KeyCode::KeyT) {
for mut cursor_icon in &mut query {
if let CursorIcon::Custom(CustomCursor::Image(ref mut image)) = *cursor_icon {
match image.texture_atlas.take() {
Some(a) => {
// Save the current texture atlas.
*cached_atlas = Some(a.clone());
}
None => {
// Restore the cached texture atlas.
if let Some(cached_a) = cached_atlas.take() {
image.texture_atlas = Some(cached_a);
}
}
}
}
}
}
}
fn toggle_flip_x(
input: Res<ButtonInput<KeyCode>>,
mut query: Query<&mut CursorIcon, With<Window>>,
) {
if input.just_pressed(KeyCode::KeyX) {
for mut cursor_icon in &mut query {
if let CursorIcon::Custom(CustomCursor::Image(ref mut image)) = *cursor_icon {
image.flip_x = !image.flip_x;
}
}
}
}
fn toggle_flip_y(
input: Res<ButtonInput<KeyCode>>,
mut query: Query<&mut CursorIcon, With<Window>>,
) {
if input.just_pressed(KeyCode::KeyY) {
for mut cursor_icon in &mut query {
if let CursorIcon::Custom(CustomCursor::Image(ref mut image)) = *cursor_icon {
image.flip_y = !image.flip_y;
}
}
}
}
/// This system alternates the [`CursorIcon`]'s `rect` field between `None` and
/// 4 sections/rectangles of the cursor's image.
fn cycle_rect(input: Res<ButtonInput<KeyCode>>, mut query: Query<&mut CursorIcon, With<Window>>) {
if !input.just_pressed(KeyCode::KeyC) {
return;
}
const RECT_SIZE: u32 = 32; // half the size of a tile in the texture atlas
const SECTIONS: [Option<URect>; 5] = [
Some(URect {
min: UVec2::ZERO,
max: UVec2::splat(RECT_SIZE),
}),
Some(URect {
min: UVec2::new(RECT_SIZE, 0),
max: UVec2::new(2 * RECT_SIZE, RECT_SIZE),
}),
Some(URect {
min: UVec2::new(0, RECT_SIZE),
max: UVec2::new(RECT_SIZE, 2 * RECT_SIZE),
}),
Some(URect {
min: UVec2::new(RECT_SIZE, RECT_SIZE),
max: UVec2::splat(2 * RECT_SIZE),
}),
None, // reset to None
];
for mut cursor_icon in &mut query {
if let CursorIcon::Custom(CustomCursor::Image(ref mut image)) = *cursor_icon {
let next_rect = SECTIONS
.iter()
.cycle()
.skip_while(|&&corner| corner != image.rect)
.nth(1) // move to the next element
.unwrap_or(&None);
image.rect = *next_rect;
}
}
}

View File

@@ -0,0 +1,113 @@
//! Shows how to create a custom event that can be handled by `winit`'s event loop.
use bevy::{
prelude::*,
winit::{EventLoopProxyWrapper, WakeUp, WinitPlugin},
};
use std::fmt::Formatter;
#[derive(Default, Debug, Event)]
enum CustomEvent {
#[default]
WakeUp,
Key(char),
}
impl std::fmt::Display for CustomEvent {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::WakeUp => write!(f, "Wake up"),
Self::Key(ch) => write!(f, "Key: {ch}"),
}
}
}
fn main() {
let winit_plugin = WinitPlugin::<CustomEvent>::default();
App::new()
.add_plugins(
DefaultPlugins
.build()
// Only one event type can be handled at once
// so we must disable the default event type
.disable::<WinitPlugin<WakeUp>>()
.add(winit_plugin),
)
.add_systems(
Startup,
(
setup,
#[cfg(target_arch = "wasm32")]
wasm::setup_js_closure,
),
)
.add_systems(Update, (send_event, handle_event))
.run();
}
fn setup(mut commands: Commands) {
commands.spawn(Camera2d);
}
fn send_event(
input: Res<ButtonInput<KeyCode>>,
event_loop_proxy: Res<EventLoopProxyWrapper<CustomEvent>>,
) {
if input.just_pressed(KeyCode::Space) {
let _ = event_loop_proxy.send_event(CustomEvent::WakeUp);
}
// This simulates sending a custom event through an external thread.
#[cfg(not(target_arch = "wasm32"))]
if input.just_pressed(KeyCode::KeyE) {
let event_loop_proxy = event_loop_proxy.clone();
let handler = std::thread::spawn(move || {
let _ = event_loop_proxy.clone().send_event(CustomEvent::Key('e'));
});
handler.join().unwrap();
}
}
fn handle_event(mut events: EventReader<CustomEvent>) {
for evt in events.read() {
info!("Received event: {evt:?}");
}
}
/// Since the [`EventLoopProxy`] can be exposed to the javascript environment, it can
/// be used to send events inside the loop, to be handled by a system or simply to wake up
/// the loop if that's currently waiting for a timeout or a user event.
#[cfg(target_arch = "wasm32")]
pub(crate) mod wasm {
use super::*;
use bevy::winit::EventLoopProxy;
use wasm_bindgen::{prelude::*, JsCast};
use web_sys::KeyboardEvent;
pub(crate) fn setup_js_closure(event_loop: Res<EventLoopProxyWrapper<CustomEvent>>) {
let window = web_sys::window().unwrap();
let document = window.document().unwrap();
let event_loop = event_loop.clone();
let closure = Closure::wrap(Box::new(move |event: KeyboardEvent| {
let key = event.key();
if key == "e" {
send_custom_event('e', &event_loop).unwrap();
}
}) as Box<dyn FnMut(KeyboardEvent)>);
document
.add_event_listener_with_callback("keydown", closure.as_ref().unchecked_ref())
.unwrap();
closure.forget();
}
fn send_custom_event(ch: char, proxy: &EventLoopProxy<CustomEvent>) -> Result<(), String> {
proxy
.send_event(CustomEvent::Key(ch))
.map_err(|_| "Failed to send event".to_string())
}
}

207
vendor/bevy/examples/window/low_power.rs vendored Normal file
View File

@@ -0,0 +1,207 @@
//! This example illustrates how to run a winit window in a reactive, low power mode.
//!
//! This is useful for making desktop applications, or any other program that doesn't need to be
//! running the event loop non-stop.
use bevy::{
prelude::*,
window::{PresentMode, RequestRedraw, WindowPlugin},
winit::{EventLoopProxyWrapper, WakeUp, WinitSettings},
};
use core::time::Duration;
fn main() {
App::new()
// Continuous rendering for games - bevy's default.
.insert_resource(WinitSettings::game())
// Power-saving reactive rendering for applications.
.insert_resource(WinitSettings::desktop_app())
// You can also customize update behavior with the fields of [`WinitSettings`]
.insert_resource(WinitSettings {
focused_mode: bevy::winit::UpdateMode::Continuous,
unfocused_mode: bevy::winit::UpdateMode::reactive_low_power(Duration::from_millis(10)),
})
.insert_resource(ExampleMode::Game)
.add_plugins(DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
// Turn off vsync to maximize CPU/GPU usage
present_mode: PresentMode::AutoNoVsync,
..default()
}),
..default()
}))
.add_systems(Startup, test_setup::setup)
.add_systems(
Update,
(
test_setup::cycle_modes,
test_setup::rotate_cube,
test_setup::update_text,
update_winit,
),
)
.run();
}
#[derive(Resource, Debug)]
enum ExampleMode {
Game,
Application,
ApplicationWithRequestRedraw,
ApplicationWithWakeUp,
}
/// Update winit based on the current `ExampleMode`
fn update_winit(
mode: Res<ExampleMode>,
mut winit_config: ResMut<WinitSettings>,
event_loop_proxy: Res<EventLoopProxyWrapper<WakeUp>>,
mut redraw_request_events: EventWriter<RequestRedraw>,
) {
use ExampleMode::*;
*winit_config = match *mode {
Game => {
// In the default `WinitSettings::game()` mode:
// * When focused: the event loop runs as fast as possible
// * When not focused: the app will update when the window is directly interacted with
// (e.g. the mouse hovers over a visible part of the out of focus window), a
// [`RequestRedraw`] event is received, or one sixtieth of a second has passed
// without the app updating (60 Hz refresh rate max).
WinitSettings::game()
}
Application => {
// While in `WinitSettings::desktop_app()` mode:
// * When focused: the app will update any time a winit event (e.g. the window is
// moved/resized, the mouse moves, a button is pressed, etc.), a [`RequestRedraw`]
// event is received, or after 5 seconds if the app has not updated.
// * When not focused: the app will update when the window is directly interacted with
// (e.g. the mouse hovers over a visible part of the out of focus window), a
// [`RequestRedraw`] event is received, or one minute has passed without the app
// updating.
WinitSettings::desktop_app()
}
ApplicationWithRequestRedraw => {
// Sending a `RequestRedraw` event is useful when you want the app to update the next
// frame regardless of any user input. For example, your application might use
// `WinitSettings::desktop_app()` to reduce power use, but UI animations need to play even
// when there are no inputs, so you send redraw requests while the animation is playing.
// Note that in this example the RequestRedraw winit event will make the app run in the same
// way as continuous
redraw_request_events.write(RequestRedraw);
WinitSettings::desktop_app()
}
ApplicationWithWakeUp => {
// Sending a `WakeUp` event is useful when you want the app to update the next
// frame regardless of any user input. This can be used from outside Bevy, see example
// `window/custom_user_event.rs` for an example usage from outside.
// Note that in this example the `WakeUp` winit event will make the app run in the same
// way as continuous
let _ = event_loop_proxy.send_event(WakeUp);
WinitSettings::desktop_app()
}
};
}
/// Everything in this module is for setting up and animating the scene, and is not important to the
/// demonstrated features.
pub(crate) mod test_setup {
use crate::ExampleMode;
use bevy::{
color::palettes::basic::{LIME, YELLOW},
prelude::*,
window::RequestRedraw,
};
/// Switch between update modes when the spacebar is pressed.
pub(crate) fn cycle_modes(
mut mode: ResMut<ExampleMode>,
button_input: Res<ButtonInput<KeyCode>>,
) {
if button_input.just_pressed(KeyCode::Space) {
*mode = match *mode {
ExampleMode::Game => ExampleMode::Application,
ExampleMode::Application => ExampleMode::ApplicationWithRequestRedraw,
ExampleMode::ApplicationWithRequestRedraw => ExampleMode::ApplicationWithWakeUp,
ExampleMode::ApplicationWithWakeUp => ExampleMode::Game,
};
}
}
#[derive(Component)]
pub(crate) struct Rotator;
/// Rotate the cube to make it clear when the app is updating
pub(crate) fn rotate_cube(
time: Res<Time>,
mut cube_transform: Query<&mut Transform, With<Rotator>>,
) {
for mut transform in &mut cube_transform {
transform.rotate_x(time.delta_secs());
transform.rotate_local_y(time.delta_secs());
}
}
#[derive(Component)]
pub struct ModeText;
pub(crate) fn update_text(
mut frame: Local<usize>,
mode: Res<ExampleMode>,
text: Single<Entity, With<ModeText>>,
mut writer: TextUiWriter,
) {
*frame += 1;
let mode = match *mode {
ExampleMode::Game => "game(), continuous, default",
ExampleMode::Application => "desktop_app(), reactive",
ExampleMode::ApplicationWithRequestRedraw => {
"desktop_app(), reactive, RequestRedraw sent"
}
ExampleMode::ApplicationWithWakeUp => "desktop_app(), reactive, WakeUp sent",
};
*writer.text(*text, 2) = mode.to_string();
*writer.text(*text, 4) = frame.to_string();
}
/// Set up a scene with a cube and some text
pub fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
mut event: EventWriter<RequestRedraw>,
) {
commands.spawn((
Mesh3d(meshes.add(Cuboid::new(0.5, 0.5, 0.5))),
MeshMaterial3d(materials.add(Color::srgb(0.8, 0.7, 0.6))),
Rotator,
));
commands.spawn((
DirectionalLight::default(),
Transform::from_xyz(1.0, 1.0, 1.0).looking_at(Vec3::ZERO, Vec3::Y),
));
commands.spawn((
Camera3d::default(),
Transform::from_xyz(-2.0, 2.0, 2.0).looking_at(Vec3::ZERO, Vec3::Y),
));
event.write(RequestRedraw);
commands
.spawn((
Text::default(),
Node {
align_self: AlignSelf::FlexStart,
position_type: PositionType::Absolute,
top: Val::Px(12.0),
left: Val::Px(12.0),
..default()
},
ModeText,
))
.with_children(|p| {
p.spawn(TextSpan::new("Press space bar to cycle modes\n"));
p.spawn((TextSpan::default(), TextColor(LIME.into())));
p.spawn((TextSpan::new("\nFrame: "), TextColor(YELLOW.into())));
p.spawn((TextSpan::new(""), TextColor(YELLOW.into())));
});
}
}

View File

@@ -0,0 +1,108 @@
//! Displays information about available monitors (displays).
use bevy::{
prelude::*,
render::camera::RenderTarget,
window::{ExitCondition, Monitor, WindowMode, WindowRef},
};
fn main() {
App::new()
.add_plugins(DefaultPlugins.set(WindowPlugin {
primary_window: None,
exit_condition: ExitCondition::DontExit,
..default()
}))
.add_systems(Update, (update, close_on_esc))
.run();
}
#[derive(Component)]
struct MonitorRef(Entity);
fn update(
mut commands: Commands,
monitors_added: Query<(Entity, &Monitor), Added<Monitor>>,
mut monitors_removed: RemovedComponents<Monitor>,
monitor_refs: Query<(Entity, &MonitorRef)>,
) {
for (entity, monitor) in monitors_added.iter() {
// Spawn a new window on each monitor
let name = monitor.name.clone().unwrap_or_else(|| "<no name>".into());
let size = format!("{}x{}px", monitor.physical_height, monitor.physical_width);
let hz = monitor
.refresh_rate_millihertz
.map(|x| format!("{}Hz", x as f32 / 1000.0))
.unwrap_or_else(|| "<unknown>".into());
let position = format!(
"x={} y={}",
monitor.physical_position.x, monitor.physical_position.y
);
let scale = format!("{:.2}", monitor.scale_factor);
let window = commands
.spawn((
Window {
title: name.clone(),
mode: WindowMode::Fullscreen(
MonitorSelection::Entity(entity),
VideoModeSelection::Current,
),
position: WindowPosition::Centered(MonitorSelection::Entity(entity)),
..default()
},
MonitorRef(entity),
))
.id();
let camera = commands
.spawn((
Camera2d,
Camera {
target: RenderTarget::Window(WindowRef::Entity(window)),
..default()
},
))
.id();
let info_text = format!(
"Monitor: {name}\nSize: {size}\nRefresh rate: {hz}\nPosition: {position}\nScale: {scale}\n\n",
);
commands.spawn((
Text(info_text),
Node {
position_type: PositionType::Relative,
height: Val::Percent(100.0),
width: Val::Percent(100.0),
..default()
},
UiTargetCamera(camera),
MonitorRef(entity),
));
}
// Remove windows for removed monitors
for monitor_entity in monitors_removed.read() {
for (ref_entity, monitor_ref) in monitor_refs.iter() {
if monitor_ref.0 == monitor_entity {
commands.entity(ref_entity).despawn();
}
}
}
}
fn close_on_esc(
mut commands: Commands,
focused_windows: Query<(Entity, &Window)>,
input: Res<ButtonInput<KeyCode>>,
) {
for (window, focus) in focused_windows.iter() {
if !focus.focused {
continue;
}
if input.just_pressed(KeyCode::Escape) {
commands.entity(window).despawn();
}
}
}

View File

@@ -0,0 +1,68 @@
//! Uses two windows to visualize a 3D model from different angles.
use bevy::{prelude::*, render::camera::RenderTarget, window::WindowRef};
fn main() {
App::new()
// By default, a primary window gets spawned by `WindowPlugin`, contained in `DefaultPlugins`
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup_scene)
.run();
}
fn setup_scene(mut commands: Commands, asset_server: Res<AssetServer>) {
// add entities to the world
commands.spawn(SceneRoot(
asset_server.load(GltfAssetLabel::Scene(0).from_asset("models/torus/torus.gltf")),
));
// light
commands.spawn((
DirectionalLight::default(),
Transform::from_xyz(3.0, 3.0, 3.0).looking_at(Vec3::ZERO, Vec3::Y),
));
let first_window_camera = commands
.spawn((
Camera3d::default(),
Transform::from_xyz(0.0, 0.0, 6.0).looking_at(Vec3::ZERO, Vec3::Y),
))
.id();
// Spawn a second window
let second_window = commands
.spawn(Window {
title: "Second window".to_owned(),
..default()
})
.id();
let second_window_camera = commands
.spawn((
Camera3d::default(),
Transform::from_xyz(6.0, 0.0, 0.0).looking_at(Vec3::ZERO, Vec3::Y),
Camera {
target: RenderTarget::Window(WindowRef::Entity(second_window)),
..default()
},
))
.id();
let node = Node {
position_type: PositionType::Absolute,
top: Val::Px(12.0),
left: Val::Px(12.0),
..default()
};
commands
.spawn((
node.clone(),
// Since we are using multiple cameras, we need to specify which camera UI should be rendered to
UiTargetCamera(first_window_camera),
))
.with_child((Text::new("First window"), TextShadow::default()));
commands
.spawn((node, UiTargetCamera(second_window_camera)))
.with_child((Text::new("Second window"), TextShadow::default()));
}

View File

@@ -0,0 +1,105 @@
//! This example illustrates how to override the window scale factor imposed by the
//! operating system.
use bevy::{prelude::*, window::WindowResolution};
#[derive(Component)]
struct CustomText;
fn main() {
App::new()
.add_plugins(DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
resolution: WindowResolution::new(500., 300.).with_scale_factor_override(1.0),
..default()
}),
..default()
}))
.add_systems(Startup, setup)
.add_systems(
Update,
(display_override, toggle_override, change_scale_factor),
)
.run();
}
fn setup(mut commands: Commands) {
// camera
commands.spawn(Camera2d);
// root node
commands
.spawn(Node {
width: Val::Percent(100.0),
height: Val::Percent(100.0),
justify_content: JustifyContent::SpaceBetween,
..default()
})
.with_children(|parent| {
// left vertical fill (border)
parent
.spawn((
Node {
width: Val::Px(300.0),
height: Val::Percent(100.0),
border: UiRect::all(Val::Px(2.0)),
..default()
},
BackgroundColor(Color::srgb(0.65, 0.65, 0.65)),
))
.with_child((
CustomText,
Text::new("Example text"),
TextFont {
font_size: 25.0,
..default()
},
Node {
align_self: AlignSelf::FlexEnd,
..default()
},
));
});
}
/// Set the title of the window to the current override
fn display_override(
mut window: Single<&mut Window>,
mut custom_text: Single<&mut Text, With<CustomText>>,
) {
let text = format!(
"Scale factor: {:.1} {}",
window.scale_factor(),
if window.resolution.scale_factor_override().is_some() {
"(overridden)"
} else {
"(default)"
}
);
window.title.clone_from(&text);
custom_text.0 = text;
}
/// This system toggles scale factor overrides when enter is pressed
fn toggle_override(input: Res<ButtonInput<KeyCode>>, mut window: Single<&mut Window>) {
if input.just_pressed(KeyCode::Enter) {
let scale_factor_override = window.resolution.scale_factor_override();
window
.resolution
.set_scale_factor_override(scale_factor_override.xor(Some(1.0)));
}
}
/// This system changes the scale factor override when up or down is pressed
fn change_scale_factor(input: Res<ButtonInput<KeyCode>>, mut window: Single<&mut Window>) {
let scale_factor_override = window.resolution.scale_factor_override();
if input.just_pressed(KeyCode::ArrowUp) {
window
.resolution
.set_scale_factor_override(scale_factor_override.map(|n| n + 1.0));
} else if input.just_pressed(KeyCode::ArrowDown) {
window
.resolution
.set_scale_factor_override(scale_factor_override.map(|n| (n - 1.0).max(1.0)));
}
}

View File

@@ -0,0 +1,90 @@
//! An example showing how to save screenshots to disk
use bevy::{
prelude::*,
render::view::screenshot::{save_to_disk, Capturing, Screenshot},
window::SystemCursorIcon,
winit::cursor::CursorIcon,
};
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.add_systems(Update, (screenshot_on_spacebar, screenshot_saving))
.run();
}
fn screenshot_on_spacebar(
mut commands: Commands,
input: Res<ButtonInput<KeyCode>>,
mut counter: Local<u32>,
) {
if input.just_pressed(KeyCode::Space) {
let path = format!("./screenshot-{}.png", *counter);
*counter += 1;
commands
.spawn(Screenshot::primary_window())
.observe(save_to_disk(path));
}
}
fn screenshot_saving(
mut commands: Commands,
screenshot_saving: Query<Entity, With<Capturing>>,
window: Single<Entity, With<Window>>,
) {
match screenshot_saving.iter().count() {
0 => {
commands.entity(*window).remove::<CursorIcon>();
}
x if x > 0 => {
commands
.entity(*window)
.insert(CursorIcon::from(SystemCursorIcon::Progress));
}
_ => {}
}
}
/// set up a simple 3D scene
fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
// plane
commands.spawn((
Mesh3d(meshes.add(Plane3d::default().mesh().size(5.0, 5.0))),
MeshMaterial3d(materials.add(Color::srgb(0.3, 0.5, 0.3))),
));
// cube
commands.spawn((
Mesh3d(meshes.add(Cuboid::default())),
MeshMaterial3d(materials.add(Color::srgb(0.8, 0.7, 0.6))),
Transform::from_xyz(0.0, 0.5, 0.0),
));
// light
commands.spawn((
PointLight {
shadows_enabled: true,
..default()
},
Transform::from_xyz(4.0, 8.0, 4.0),
));
// camera
commands.spawn((
Camera3d::default(),
Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
));
commands.spawn((
Text::new("Press <spacebar> to save a screenshot to disk"),
Node {
position_type: PositionType::Absolute,
top: Val::Px(12.0),
left: Val::Px(12.0),
..default()
},
));
}

View File

@@ -0,0 +1,36 @@
//! Shows how to display a window in transparent mode.
//!
//! This feature works as expected depending on the platform. Please check the
//! [documentation](https://docs.rs/bevy/latest/bevy/prelude/struct.Window.html#structfield.transparent)
//! for more details.
use bevy::prelude::*;
#[cfg(any(target_os = "macos", target_os = "linux"))]
use bevy::window::CompositeAlphaMode;
fn main() {
App::new()
.add_plugins(DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
// Setting `transparent` allows the `ClearColor`'s alpha value to take effect
transparent: true,
// Disabling window decorations to make it feel more like a widget than a window
decorations: false,
#[cfg(target_os = "macos")]
composite_alpha_mode: CompositeAlphaMode::PostMultiplied,
#[cfg(target_os = "linux")]
composite_alpha_mode: CompositeAlphaMode::PreMultiplied,
..default()
}),
..default()
}))
// ClearColor must have 0 alpha, otherwise some color will bleed through
.insert_resource(ClearColor(Color::NONE))
.add_systems(Startup, setup)
.run();
}
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn(Camera2d);
commands.spawn(Sprite::from_image(asset_server.load("branding/icon.png")));
}

View File

@@ -0,0 +1,144 @@
//! This example illustrates drag move and drag resize without window
//! decorations.
//!
//! When window decorations are not present, the user cannot drag a window by
//! its titlebar to change its position. The `start_drag_move()` function
//! permits a user to drag a window by left clicking anywhere in the window;
//! left click must be pressed and other constraints can be imposed. For
//! instance an application could require a user to hold down alt and left click
//! to drag a window.
//!
//! The `start_drag_resize()` function behaves similarly but permits a window to
//! be resized.
use bevy::{math::CompassOctant, prelude::*};
/// Determine what do on left click.
#[derive(Resource, Debug)]
enum LeftClickAction {
/// Do nothing.
Nothing,
/// Move the window on left click.
Move,
/// Resize the window on left click.
Resize,
}
/// What direction index should the window resize toward.
#[derive(Resource)]
struct ResizeDir(usize);
/// Directions that the drag resizes the window toward.
const DIRECTIONS: [CompassOctant; 8] = [
CompassOctant::North,
CompassOctant::NorthEast,
CompassOctant::East,
CompassOctant::SouthEast,
CompassOctant::South,
CompassOctant::SouthWest,
CompassOctant::West,
CompassOctant::NorthWest,
];
fn main() {
App::new()
.add_plugins(DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
decorations: false,
..default()
}),
..default()
}))
.insert_resource(ResizeDir(7))
.insert_resource(LeftClickAction::Move)
.add_systems(Startup, setup)
.add_systems(Update, (handle_input, move_or_resize_windows))
.run();
}
fn setup(mut commands: Commands) {
// Camera
commands.spawn(Camera3d::default());
// UI
commands
.spawn((
Node {
position_type: PositionType::Absolute,
padding: UiRect::all(Val::Px(5.0)),
..default()
},
BackgroundColor(Color::BLACK.with_alpha(0.75)),
GlobalZIndex(i32::MAX),
))
.with_children(|p| {
p.spawn(Text::default()).with_children(|p| {
p.spawn(TextSpan::new(
"Demonstrate drag move and drag resize without window decorations.\n\n",
));
p.spawn(TextSpan::new("Controls:\n"));
p.spawn(TextSpan::new("A - change left click action ["));
p.spawn(TextSpan::new("Move"));
p.spawn(TextSpan::new("]\n"));
p.spawn(TextSpan::new("S / D - change resize direction ["));
p.spawn(TextSpan::new("NorthWest"));
p.spawn(TextSpan::new("]\n"));
});
});
}
fn handle_input(
input: Res<ButtonInput<KeyCode>>,
mut action: ResMut<LeftClickAction>,
mut dir: ResMut<ResizeDir>,
example_text: Query<Entity, With<Text>>,
mut writer: TextUiWriter,
) -> Result {
use LeftClickAction::*;
if input.just_pressed(KeyCode::KeyA) {
*action = match *action {
Move => Resize,
Resize => Nothing,
Nothing => Move,
};
*writer.text(example_text.single()?, 4) = format!("{:?}", *action);
}
if input.just_pressed(KeyCode::KeyS) {
dir.0 = dir
.0
.checked_sub(1)
.unwrap_or(DIRECTIONS.len().saturating_sub(1));
*writer.text(example_text.single()?, 7) = format!("{:?}", DIRECTIONS[dir.0]);
}
if input.just_pressed(KeyCode::KeyD) {
dir.0 = (dir.0 + 1) % DIRECTIONS.len();
*writer.text(example_text.single()?, 7) = format!("{:?}", DIRECTIONS[dir.0]);
}
Ok(())
}
fn move_or_resize_windows(
mut windows: Query<&mut Window>,
action: Res<LeftClickAction>,
input: Res<ButtonInput<MouseButton>>,
dir: Res<ResizeDir>,
) {
// Both `start_drag_move()` and `start_drag_resize()` must be called after a
// left mouse button press as done here.
//
// winit 0.30.5 may panic when initiated without a left mouse button press.
if input.just_pressed(MouseButton::Left) {
for mut window in windows.iter_mut() {
match *action {
LeftClickAction::Nothing => (),
LeftClickAction::Move => window.start_drag_move(),
LeftClickAction::Resize => {
let d = DIRECTIONS[dir.0];
window.start_drag_resize(d);
}
}
}
}
}

View File

@@ -0,0 +1,83 @@
//! This example illustrates how to resize windows, and how to respond to a window being resized.
use bevy::{prelude::*, window::WindowResized};
fn main() {
App::new()
.insert_resource(ResolutionSettings {
large: Vec2::new(1920.0, 1080.0),
medium: Vec2::new(800.0, 600.0),
small: Vec2::new(640.0, 360.0),
})
.add_plugins(DefaultPlugins)
.add_systems(Startup, (setup_camera, setup_ui))
.add_systems(Update, (on_resize_system, toggle_resolution))
.run();
}
/// Marker component for the text that displays the current resolution.
#[derive(Component)]
struct ResolutionText;
/// Stores the various window-resolutions we can select between.
#[derive(Resource)]
struct ResolutionSettings {
large: Vec2,
medium: Vec2,
small: Vec2,
}
// Spawns the camera that draws UI
fn setup_camera(mut commands: Commands) {
commands.spawn(Camera2d);
}
// Spawns the UI
fn setup_ui(mut commands: Commands) {
// Node that fills entire background
commands
.spawn(Node {
width: Val::Percent(100.),
..default()
})
// Text where we display current resolution
.with_child((
Text::new("Resolution"),
TextFont {
font_size: 42.0,
..default()
},
ResolutionText,
));
}
/// This system shows how to request the window to a new resolution
fn toggle_resolution(
keys: Res<ButtonInput<KeyCode>>,
mut window: Single<&mut Window>,
resolution: Res<ResolutionSettings>,
) {
if keys.just_pressed(KeyCode::Digit1) {
let res = resolution.small;
window.resolution.set(res.x, res.y);
}
if keys.just_pressed(KeyCode::Digit2) {
let res = resolution.medium;
window.resolution.set(res.x, res.y);
}
if keys.just_pressed(KeyCode::Digit3) {
let res = resolution.large;
window.resolution.set(res.x, res.y);
}
}
/// This system shows how to respond to a window being resized.
/// Whenever the window is resized, the text will update with the new resolution.
fn on_resize_system(
mut text: Single<&mut Text, With<ResolutionText>>,
mut resize_reader: EventReader<WindowResized>,
) {
for e in resize_reader.read() {
// When resolution is being changed
text.0 = format!("{:.1} x {:.1}", e.width, e.height);
}
}

View File

@@ -0,0 +1,198 @@
//! Illustrates how to change window settings and shows how to affect
//! the mouse pointer in various ways.
#[cfg(feature = "custom_cursor")]
use bevy::winit::cursor::{CustomCursor, CustomCursorImage};
use bevy::{
diagnostic::{FrameCount, FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
prelude::*,
window::{CursorGrabMode, PresentMode, SystemCursorIcon, WindowLevel, WindowTheme},
winit::cursor::CursorIcon,
};
fn main() {
App::new()
.add_plugins((
DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
title: "I am a window!".into(),
name: Some("bevy.app".into()),
resolution: (500., 300.).into(),
present_mode: PresentMode::AutoVsync,
// Tells Wasm to resize the window according to the available canvas
fit_canvas_to_parent: true,
// Tells Wasm not to override default event handling, like F5, Ctrl+R etc.
prevent_default_event_handling: false,
window_theme: Some(WindowTheme::Dark),
enabled_buttons: bevy::window::EnabledButtons {
maximize: false,
..Default::default()
},
// This will spawn an invisible window
// The window will be made visible in the make_visible() system after 3 frames.
// This is useful when you want to avoid the white window that shows up before the GPU is ready to render the app.
visible: false,
..default()
}),
..default()
}),
LogDiagnosticsPlugin::default(),
FrameTimeDiagnosticsPlugin::default(),
))
.add_systems(Startup, init_cursor_icons)
.add_systems(
Update,
(
change_title,
toggle_theme,
toggle_cursor,
toggle_vsync,
toggle_window_controls,
cycle_cursor_icon,
switch_level,
make_visible,
),
)
.run();
}
fn make_visible(mut window: Single<&mut Window>, frames: Res<FrameCount>) {
// The delay may be different for your app or system.
if frames.0 == 3 {
// At this point the gpu is ready to show the app so we can make the window visible.
// Alternatively, you could toggle the visibility in Startup.
// It will work, but it will have one white frame before it starts rendering
window.visible = true;
}
}
/// This system toggles the vsync mode when pressing the button V.
/// You'll see fps increase displayed in the console.
fn toggle_vsync(input: Res<ButtonInput<KeyCode>>, mut window: Single<&mut Window>) {
if input.just_pressed(KeyCode::KeyV) {
window.present_mode = if matches!(window.present_mode, PresentMode::AutoVsync) {
PresentMode::AutoNoVsync
} else {
PresentMode::AutoVsync
};
info!("PRESENT_MODE: {:?}", window.present_mode);
}
}
/// This system switches the window level when pressing the T button
/// You'll notice it won't be covered by other windows, or will be covered by all the other
/// windows depending on the level.
///
/// This feature only works on some platforms. Please check the
/// [documentation](https://docs.rs/bevy/latest/bevy/prelude/struct.Window.html#structfield.window_level)
/// for more details.
fn switch_level(input: Res<ButtonInput<KeyCode>>, mut window: Single<&mut Window>) {
if input.just_pressed(KeyCode::KeyT) {
window.window_level = match window.window_level {
WindowLevel::AlwaysOnBottom => WindowLevel::Normal,
WindowLevel::Normal => WindowLevel::AlwaysOnTop,
WindowLevel::AlwaysOnTop => WindowLevel::AlwaysOnBottom,
};
info!("WINDOW_LEVEL: {:?}", window.window_level);
}
}
/// This system toggles the window controls when pressing buttons 1, 2 and 3
///
/// This feature only works on some platforms. Please check the
/// [documentation](https://docs.rs/bevy/latest/bevy/prelude/struct.Window.html#structfield.enabled_buttons)
/// for more details.
fn toggle_window_controls(input: Res<ButtonInput<KeyCode>>, mut window: Single<&mut Window>) {
let toggle_minimize = input.just_pressed(KeyCode::Digit1);
let toggle_maximize = input.just_pressed(KeyCode::Digit2);
let toggle_close = input.just_pressed(KeyCode::Digit3);
if toggle_minimize || toggle_maximize || toggle_close {
if toggle_minimize {
window.enabled_buttons.minimize = !window.enabled_buttons.minimize;
}
if toggle_maximize {
window.enabled_buttons.maximize = !window.enabled_buttons.maximize;
}
if toggle_close {
window.enabled_buttons.close = !window.enabled_buttons.close;
}
}
}
/// This system will then change the title during execution
fn change_title(mut window: Single<&mut Window>, time: Res<Time>) {
window.title = format!(
"Seconds since startup: {}",
time.elapsed().as_secs_f32().round()
);
}
fn toggle_cursor(mut window: Single<&mut Window>, input: Res<ButtonInput<KeyCode>>) {
if input.just_pressed(KeyCode::Space) {
window.cursor_options.visible = !window.cursor_options.visible;
window.cursor_options.grab_mode = match window.cursor_options.grab_mode {
CursorGrabMode::None => CursorGrabMode::Locked,
CursorGrabMode::Locked | CursorGrabMode::Confined => CursorGrabMode::None,
};
}
}
// This system will toggle the color theme used by the window
fn toggle_theme(mut window: Single<&mut Window>, input: Res<ButtonInput<KeyCode>>) {
if input.just_pressed(KeyCode::KeyF) {
if let Some(current_theme) = window.window_theme {
window.window_theme = match current_theme {
WindowTheme::Light => Some(WindowTheme::Dark),
WindowTheme::Dark => Some(WindowTheme::Light),
};
}
}
}
#[derive(Resource)]
struct CursorIcons(Vec<CursorIcon>);
fn init_cursor_icons(
mut commands: Commands,
#[cfg(feature = "custom_cursor")] asset_server: Res<AssetServer>,
) {
commands.insert_resource(CursorIcons(vec![
SystemCursorIcon::Default.into(),
SystemCursorIcon::Pointer.into(),
SystemCursorIcon::Wait.into(),
SystemCursorIcon::Text.into(),
#[cfg(feature = "custom_cursor")]
CustomCursor::Image(CustomCursorImage {
handle: asset_server.load("branding/icon.png"),
hotspot: (128, 128),
..Default::default()
})
.into(),
]));
}
/// This system cycles the cursor's icon through a small set of icons when clicking
fn cycle_cursor_icon(
mut commands: Commands,
window: Single<Entity, With<Window>>,
input: Res<ButtonInput<MouseButton>>,
mut index: Local<usize>,
cursor_icons: Res<CursorIcons>,
) {
if input.just_pressed(MouseButton::Left) {
*index = (*index + 1) % cursor_icons.0.len();
commands
.entity(*window)
.insert(cursor_icons.0[*index].clone());
} else if input.just_pressed(MouseButton::Right) {
*index = if *index == 0 {
cursor_icons.0.len() - 1
} else {
*index - 1
};
commands
.entity(*window)
.insert(cursor_icons.0[*index].clone());
}
}