Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
36d81c1
Implement custom_window_icon feature
TeamDman Sep 28, 2025
1dcd3ac
Automatically convert to RGBA for window icon, improved logging fidelity
TeamDman Sep 28, 2025
8cf8576
Ensure systems in window_settings.rs example are only installed when …
TeamDman Sep 28, 2025
b988ef0
Run `cargo run -p build-templated-pages -- update features`
TeamDman Sep 28, 2025
80780fd
Apply suggestions from code review
TeamDman Sep 29, 2025
fa87d64
Remove `serialize` impl for WindowIcon since Handle isn't compatible
TeamDman Sep 29, 2025
8d4b1de
Initialize window icon on creation to fix problem problem with Change…
TeamDman Sep 30, 2025
0c17984
Remove experiment code that disables window icon setting at creation
TeamDman Sep 30, 2025
ee71bfe
Merge branch 'main' into feat/window-icon-v3
TeamDman Sep 30, 2025
3c2cdca
extract helper fn to winit_window_icon.rs
TeamDman Sep 30, 2025
e48fa11
formatting
TeamDman Sep 30, 2025
139a31e
Fix race conditions when updating winit window icon.
TeamDman Sep 30, 2025
16a74f8
Merge upstream main
TeamDman Dec 6, 2025
ee86d09
Fix formatting
TeamDman Dec 6, 2025
3761d7e
Remove double cfg guard for custom_window_icon feature
TeamDman Dec 6, 2025
5859575
Add platform specific icon loading mechanism
TeamDman Dec 6, 2025
a3e3f4e
Fix toml formatting for window-icon integration test
TeamDman Dec 6, 2025
0004603
Remove unneeded dead_code attr
TeamDman Dec 6, 2025
3a0c820
Remove guard on PlatformSpecific window icon variant since WindowIcon…
TeamDman Dec 6, 2025
d31f213
Fix expect failing depending on platform
TeamDman Dec 6, 2025
0c09e09
fix allow attr formatting
TeamDman Dec 6, 2025
0375809
Fix doc comments
TeamDman Dec 6, 2025
b81bcae
Fix window icon integration test build failing on non-windows platforms
TeamDman Dec 6, 2025
5103f2e
Remove unused import on non-windows platforms
TeamDman Dec 6, 2025
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 @@ -187,6 +187,7 @@ default_app = [
"bevy_state",
"bevy_window",
"custom_cursor",
"custom_window_icon",
"reflect_auto_register",
]

Expand Down Expand Up @@ -673,6 +674,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 window icon 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 @@ -3907,6 +3911,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
6 changes: 6 additions & 0 deletions crates/bevy_internal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,12 @@ reflect_documentation = ["bevy_reflect/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 window 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
12 changes: 12 additions & 0 deletions crates/bevy_window/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ impl Default for WindowPlugin {
WindowPlugin {
primary_window: Some(Window::default()),
primary_cursor_options: Some(CursorOptions::default()),
#[cfg(feature = "custom_window_icon")]
primary_window_icon: Some(WindowIcon::default()),
exit_condition: ExitCondition::OnAllClosed,
close_when_requested: true,
}
Expand All @@ -72,6 +74,12 @@ pub struct WindowPlugin {
/// [`exit_on_all_closed`].
pub primary_window: Option<Window>,

/// Default [`WindowIcon`] component for the primary window entity.
///
/// Has no effect if [`WindowPlugin::primary_window`] is `None`.
#[cfg(feature = "custom_window_icon")]
pub primary_window_icon: Option<WindowIcon>,

/// Settings for the cursor on the primary window.
///
/// Defaults to `Some(CursorOptions::default())`.
Expand Down Expand Up @@ -133,6 +141,10 @@ impl Plugin for WindowPlugin {
if let Some(primary_cursor_options) = &self.primary_cursor_options {
entity_commands.insert(primary_cursor_options.clone());
}
#[cfg(feature = "custom_window_icon")]
if let Some(primary_window_icon) = &self.primary_window_icon {
entity_commands.insert(primary_window_icon.clone());
}
}

match self.exit_condition {
Expand Down
53 changes: 53 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 @@ -161,6 +165,7 @@ impl ContainsEntity for NormalizedWindowRef {
reflect(Serialize, Deserialize)
)]
#[require(CursorOptions)]
#[cfg_attr(feature = "custom_window_icon", require(WindowIcon))]
pub struct Window {
/// What presentation mode to give the window.
pub present_mode: PresentMode,
Expand Down Expand Up @@ -776,6 +781,54 @@ 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(feature = "custom_window_icon")]
pub enum WindowIcon {
/// Handle to the asset to be read into the window icon.
Image(Handle<Image>),
/// The icon will not be intentionally set by Bevy, which may result in no icon being set.
#[default]
PlatformDefault,
/// Use an icon loaded using a platform-specific mechanism.
PlatformSpecific(WindowIconSource),
}

/// Icon source for usage with [`IconExtWindows`](https://docs.rs/winit/latest/winit/platform/windows/trait.IconExtWindows.html) from `winit` for the Windows platform.
/// See the [embed_resource](https://docs.rs/embed-resource/latest/embed_resource/) crate for an example on how to embed resources in a Windows executable.
/// The Bevy source code has an example under `tests-integration/window-icon`.
///
/// At this time, Bevy does not provide automatic discovery of the application icon, so you will have to specify here the identifier used in the `.rc` file manually.
#[derive(Debug, Clone)]
#[cfg(feature = "custom_window_icon")]
#[cfg(target_os = "windows")]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, Clone))]
pub enum WindowIconSource {
/// A path to be passed as an argument to [`LoadImageW`](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-loadimagew) with `hinst` set to `0`
Path {
/// The path to the icon file.
path: std::path::PathBuf,
/// Size that should be used when loading the icon. If `None`, the system's default icon size will be used. If the specified size is not present, Windows may scale another available size.
size: Option<(u32, u32)>,
},
/// A numeric resource identifier to be passed as an argument to [`LoadImageW`](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-loadimagew) with [`hinst` of the current module](https://docs.rs/crate/winit/latest/source/src/platform_impl/windows/util.rs#147-160).
ResourceOrdinal(u16),
/// A path to a resource name to be passed as an argument to [`LoadImageW`](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-loadimagew) with [`hinst` of the current module](https://docs.rs/crate/winit/latest/source/src/platform_impl/windows/util.rs#147-160).
ResourceName(String),
}

/// Icon source for platforms other than Windows.
#[derive(Debug, Clone)]
#[cfg(feature = "custom_window_icon")]
#[cfg(not(target_os = "windows"))]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Debug, Clone))]
pub enum WindowIconSource {}

/// 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
35 changes: 35 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::{
state::winit_runner,
};

#[cfg(feature = "custom_window_icon")]
use system::{changed_window_icon, changed_window_icon_asset, set_winit_window_icon};

pub mod accessibility;
mod converters;
mod cursor;
Expand All @@ -52,6 +55,8 @@ mod winit_config;
mod winit_monitors;
mod winit_windows;

mod winit_window_icon;

thread_local! {
/// Temporary storage of WinitWindows data to replace usage of `!Send` resources. This will be replaced with proper
/// storage of `!Send` data after issue #17667 is complete.
Expand Down Expand Up @@ -142,6 +147,12 @@ 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,
#[cfg(feature = "custom_window_icon")]
changed_window_icon_asset,
#[cfg(feature = "custom_window_icon")]
set_winit_window_icon,
despawn_windows,
check_keyboard_focus_lost,
)
Expand Down Expand Up @@ -203,6 +214,28 @@ impl AppSendEvent for Vec<WindowEvent> {
}

/// The parameters of the [`create_windows`] system.
#[cfg(not(feature = "custom_window_icon"))]
pub type CreateWindowParams<'w, 's, F = ()> = (
Commands<'w, 's>,
Query<
'w,
's,
(
Entity,
&'static mut Window,
&'static CursorOptions,
Option<&'static RawHandleWrapperHolder>,
),
F,
>,
MessageWriter<'w, WindowCreated>,
ResMut<'w, WinitActionRequestHandlers>,
Res<'w, AccessibilityRequested>,
Res<'w, WinitMonitors>,
);

/// The parameters of the [`create_windows`] system.
#[cfg(feature = "custom_window_icon")]
pub type CreateWindowParams<'w, 's, F = ()> = (
Commands<'w, 's>,
Query<
Expand All @@ -212,6 +245,7 @@ pub type CreateWindowParams<'w, 's, F = ()> = (
Entity,
&'static mut Window,
&'static CursorOptions,
Option<&'static bevy_window::WindowIcon>,
Option<&'static RawHandleWrapperHolder>,
),
F,
Expand All @@ -220,6 +254,7 @@ pub type CreateWindowParams<'w, 's, F = ()> = (
ResMut<'w, WinitActionRequestHandlers>,
Res<'w, AccessibilityRequested>,
Res<'w, WinitMonitors>,
Res<'w, bevy_asset::Assets<bevy_image::Image>>, // qualified path used to avoid unused import warnings
);

/// The parameters of the [`create_monitors`] system.
Expand Down
Loading