Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ default = [
"bevy_window",
"bevy_winit",
"custom_cursor",
"custom_window_icon",
"default_font",
"hdr",
"ktx2",
Expand Down Expand Up @@ -563,6 +564,9 @@ reflect_auto_register_static = ["bevy_internal/reflect_auto_register_static"]
# Enable winit custom cursor support
custom_cursor = ["bevy_internal/custom_cursor"]

# Enable winit custom cursor support
custom_window_icon = ["bevy_internal/custom_window_icon"]

# Experimental support for nodes that are ignored for UI layouting
ghost_nodes = ["bevy_internal/ghost_nodes"]

Expand Down Expand Up @@ -3772,6 +3776,7 @@ wasm = false
name = "window_settings"
path = "examples/window/window_settings.rs"
doc-scrape-examples = true
required-features = ["bevy_window", "bevy_winit"]

[package.metadata.example.window_settings]
name = "Window Settings"
Expand Down
3 changes: 3 additions & 0 deletions crates/bevy_internal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,9 @@ reflect_documentation = ["bevy_reflect/documentation"]
# Enable custom cursor support
custom_cursor = ["bevy_window/custom_cursor", "bevy_winit/custom_cursor"]

# Enable custom icon support
custom_window_icon = ["bevy_window/custom_window_icon", "bevy_winit/custom_window_icon"]

# Experimental support for nodes that are ignored for UI layouting
ghost_nodes = ["bevy_ui/ghost_nodes"]

Expand Down
3 changes: 3 additions & 0 deletions crates/bevy_window/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ serialize = ["serde", "bevy_ecs/serialize", "bevy_input/serialize"]
# Enable custom cursor support
custom_cursor = ["bevy_image", "bevy_asset"]

# Enable custom icon support
custom_window_icon = ["bevy_image", "bevy_asset"]

# Platform Compatibility

## Allows access to the `std` crate. Enabling this feature will prevent compilation
Expand Down
24 changes: 24 additions & 0 deletions crates/bevy_window/src/window.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
#[cfg(feature = "std")]
use alloc::format;
use alloc::{borrow::ToOwned, string::String};
#[cfg(feature = "custom_window_icon")]
use bevy_asset::Handle;
#[cfg(feature = "custom_window_icon")]
use bevy_image::Image;
use core::num::NonZero;

use bevy_ecs::{
Expand Down Expand Up @@ -776,6 +780,26 @@ impl Default for CursorOptions {
}
}


/// Icon data for a [`Window`].
#[derive(Component, Debug, Clone, Default)]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Component, Debug, Default, Clone)
)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
all(feature = "serialize", feature = "bevy_reflect"),
reflect(Serialize, Deserialize)
)]
#[cfg(feature = "custom_window_icon")]
pub struct WindowIcon {
/// Handle to the asset to be read into the window icon.
pub handle: Handle<Image>,
}


/// Defines where a [`Window`] should be placed on the screen.
#[derive(Default, Debug, Clone, Copy, PartialEq)]
#[cfg_attr(
Expand Down
10 changes: 8 additions & 2 deletions crates/bevy_winit/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ custom_cursor = [
"bytemuck",
]

custom_window_icon = [
"bevy_window/custom_window_icon",
"bevy_image",
"bevy_asset",
]

[dependencies]
# bevy
bevy_a11y = { path = "../bevy_a11y", version = "0.18.0-dev" }
Expand All @@ -52,9 +58,9 @@ bevy_platform = { path = "../bevy_platform", version = "0.18.0-dev", default-fea
] }

# bevy optional
## used by custom_cursor
## used by custom_cursor and custom_window_icon
bevy_asset = { path = "../bevy_asset", version = "0.18.0-dev", optional = true }
## used by custom_cursor
## used by custom_cursor and custom_window_icon
bevy_image = { path = "../bevy_image", version = "0.18.0-dev", optional = true }
## used by custom_cursor
wgpu-types = { version = "26", optional = true }
Expand Down
5 changes: 5 additions & 0 deletions crates/bevy_winit/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ use crate::{
winit_monitors::WinitMonitors,
};

#[cfg(feature = "custom_window_icon")]
use system::changed_window_icon;

pub mod accessibility;
mod converters;
mod cursor;
Expand Down Expand Up @@ -142,6 +145,8 @@ impl<T: Message> Plugin for WinitPlugin<T> {
// so we don't need to care about its ordering relative to `changed_windows`
changed_windows.ambiguous_with(exit_on_all_closed),
changed_cursor_options,
#[cfg(feature = "custom_window_icon")]
changed_window_icon,
despawn_windows,
check_keyboard_focus_lost,
)
Expand Down
84 changes: 84 additions & 0 deletions crates/bevy_winit/src/system.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use std::collections::HashMap;

#[cfg(feature = "custom_window_icon")]
use bevy_asset::Assets;
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{
change_detection::DetectChangesMut,
Expand All @@ -10,7 +12,11 @@ use bevy_ecs::{
query::QueryFilter,
system::{Local, NonSendMarker, Query, SystemParamItem},
};
#[cfg(feature = "custom_window_icon")]
use bevy_image::Image;
use bevy_input::keyboard::{Key, KeyCode, KeyboardFocusLost, KeyboardInput};
#[cfg(feature = "custom_window_icon")]
use bevy_window::WindowIcon;
use bevy_window::{
ClosingWindow, CursorOptions, Monitor, PrimaryMonitor, RawHandleWrapper, VideoMode, Window,
WindowClosed, WindowClosing, WindowCreated, WindowEvent, WindowFocused, WindowMode,
Expand Down Expand Up @@ -618,6 +624,84 @@ pub(crate) fn changed_cursor_options(
});
}

#[cfg(feature = "custom_window_icon")]
pub(crate) fn changed_window_icon(
changed_windows: Query<(Entity, &Window, &WindowIcon), Changed<WindowIcon>>,
assets: Res<Assets<Image>>,
_non_send_marker: NonSendMarker,
) {
WINIT_WINDOWS.with_borrow(|winit_windows| {
for (window_entity, window, window_icon) in changed_windows {
// Identify window
let Some(winit_window) = winit_windows.get_window(window_entity) else {
continue;
};

// Fetch the image asset
let Some(image) = assets.get(&window_icon.handle) else {
warn!(
?window_entity,
?window,
?window_icon,
"Could not set window icon for window: image asset not found"
);
continue;
};

// Convert to rgba image
let rgba_image = match image.clone().try_into_dynamic() {
Ok(dynamic_image) => {
// winit icon expects 32bpp RGBA data
dynamic_image.into_rgba8()
}
Err(error) => {
error!(
?window_entity,
?window,
?window_icon,
?image,
?error,
"Could not set window icon for window: failed to convert image to RGBA",
);
continue;
}
};

// Convert to winit image
let width = rgba_image.width();
let height = rgba_image.height();
let icon = match winit::window::Icon::from_rgba(
rgba_image.into_raw(),
width,
height,
) {
Ok(icon) => icon,
Err(error) => {
error!(
?window_entity,
?window,
?window_icon,
?image,
?error,
"Could not set window icon for window: failed to construct winit window icon from RGBA buffer",
);
continue;
}
};

// Set the window icon
tracing::debug!(
?window_entity,
?window.title,
?window_icon.handle,
image_size = ?image.size(),
"Setting window icon"
);
winit_window.set_window_icon(Some(icon));
}
});
}

/// This keeps track of which keys are pressed on each window.
/// When a window is unfocused, this is used to send key release events for all the currently held keys.
#[derive(Default, Component)]
Expand Down
1 change: 1 addition & 0 deletions docs/cargo_features.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ The default feature set enables most of the expected features of a game engine,
|bevy_window|Windowing layer|
|bevy_winit|winit window and input backend|
|custom_cursor|Enable winit custom cursor support|
|custom_window_icon|Enable winit custom cursor support|
|debug|Enable collecting debug information about systems and components to help with diagnostics|
|default_font|Include a default font, containing only ASCII characters, at the cost of a 20kB binary size increase|
|hdr|HDR image format support|
Expand Down
36 changes: 35 additions & 1 deletion examples/window/window_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,14 @@ fn main() {
LogDiagnosticsPlugin::default(),
FrameTimeDiagnosticsPlugin::default(),
))
.add_systems(Startup, init_cursor_icons)
.add_systems(
Startup,
(
init_cursor_icons,
#[cfg(feature = "custom_window_icon")]
init_window_icon
)
)
.add_systems(
Update,
(
Expand All @@ -53,6 +60,8 @@ fn main() {
cycle_cursor_icon,
switch_level,
make_visible,
#[cfg(all(feature = "bevy_asset", feature = "bevy_log"))]
log_asset_messages,
),
)
.run();
Expand All @@ -77,6 +86,7 @@ fn toggle_vsync(input: Res<ButtonInput<KeyCode>>, mut window: Single<&mut Window
} else {
PresentMode::AutoVsync
};
#[cfg(feature = "bevy_log")]
info!("PRESENT_MODE: {:?}", window.present_mode);
}
}
Expand All @@ -95,6 +105,7 @@ fn switch_level(input: Res<ButtonInput<KeyCode>>, mut window: Single<&mut Window
WindowLevel::Normal => WindowLevel::AlwaysOnTop,
WindowLevel::AlwaysOnTop => WindowLevel::AlwaysOnBottom,
};
#[cfg(feature = "bevy_log")]
info!("WINDOW_LEVEL: {:?}", window.window_level);
}
}
Expand Down Expand Up @@ -174,6 +185,29 @@ fn init_cursor_icons(
]));
}

#[cfg(feature = "custom_window_icon")]
fn init_window_icon(
mut commands: Commands,
window: Single<Entity, With<Window>>,
asset_server: Res<AssetServer>,
) {
use bevy::window::WindowIcon;

let icon_handle = asset_server.load("branding/icon.png");
#[cfg(feature = "bevy_log")]
info!("icon_handle: {:?}", icon_handle);
commands.entity(*window).insert(WindowIcon {
handle: icon_handle,
});
}

#[cfg(all(feature = "bevy_asset", feature = "bevy_log"))]
fn log_asset_messages(mut asset_messages: MessageReader<AssetEvent<Image>>) {
for msg in asset_messages.read() {
info!(?msg);
}
}

/// This system cycles the cursor's icon through a small set of icons when clicking
fn cycle_cursor_icon(
mut commands: Commands,
Expand Down
Loading