Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
4 changes: 4 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,7 @@ 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
54 changes: 54 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,54 @@ 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 (entity, window, window_icon) in changed_windows {
// Identify window
let Some(winit_window) = winit_windows.get_window(entity) else {
continue;
};

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

// Acquire pixel data from the image
let Some(image_data) = image.data.clone() else {
warn!(
?window_icon.handle,
"Image handle has no data, the window will not have our custom icon",
);
continue;
};

// Convert between formats
let icon = match winit::window::Icon::from_rgba(
image_data,
image.texture_descriptor.size.width,
image.texture_descriptor.size.height,
) {
Ok(icon) => icon,
Err(e) => {
error!("Failed to construct window icon: {:?}", e);
continue;
}
};

// Set the window icon
tracing::debug!(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
20 changes: 19 additions & 1 deletion examples/window/window_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ fn main() {
LogDiagnosticsPlugin::default(),
FrameTimeDiagnosticsPlugin::default(),
))
.add_systems(Startup, init_cursor_icons)
.add_systems(Startup, (init_cursor_icons, init_window_icon))
.add_systems(
Update,
(
Expand Down Expand Up @@ -77,6 +77,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 +96,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 +176,22 @@ fn init_cursor_icons(
]));
}

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

let icon_handle = asset_server.load("branding/icon.png");
commands.entity(*window).insert(WindowIcon {
handle: icon_handle,
});
}
}

/// 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