Skip to content

Commit

Permalink
Add basic support for window icons
Browse files Browse the repository at this point in the history
  • Loading branch information
emwalker committed Mar 19, 2023
1 parent 7b7294b commit 9f9f374
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 0 deletions.
2 changes: 2 additions & 0 deletions crates/bevy_render/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ bevy_render_macros = { path = "macros", version = "0.11.0-dev" }
bevy_time = { path = "../bevy_time", version = "0.11.0-dev" }
bevy_transform = { path = "../bevy_transform", version = "0.11.0-dev" }
bevy_window = { path = "../bevy_window", version = "0.11.0-dev" }
bevy_winit = { path = "../bevy_winit", version = "0.11.0-dev" }
bevy_utils = { path = "../bevy_utils", version = "0.11.0-dev" }
bevy_tasks = { path = "../bevy_tasks", version = "0.11.0-dev" }

Expand Down Expand Up @@ -78,3 +79,4 @@ encase = { version = "0.5", features = ["glam"] }
# For wgpu profiling using tracing. Use `RUST_LOG=info` to also capture the wgpu spans.
profiling = { version = "1", features = ["profile-with-tracing"], optional = true }
async-channel = "1.8"
winit = { version = "0.28", default-features = false }
76 changes: 76 additions & 0 deletions crates/bevy_render/src/texture/icon.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
use crate::texture::Image;
use bevy_asset::{Assets, Handle};
use bevy_ecs::{
prelude::{Component, Entity, NonSendMut, Query, Res},
system::Commands,
};
use bevy_log::{error, info};
use bevy_winit::WinitWindows;
use winit::window::Icon;

/// An icon that can be placed at the top left of the window.
#[derive(Component, Debug)]
pub struct WindowIcon(pub Option<Handle<Image>>);

/// Set or unset the window icon, depending on whether `Some(image_handle)` or `None` is provided.
///
/// # Example
/// ```rust,no_run
/// use bevy_app::{App, Startup, Update};
/// use bevy_asset::AssetServer;
/// use bevy_ecs::prelude::*;
/// use bevy_render::texture::{set_window_icon, WindowIcon};
///
/// fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
/// let icon_handle = asset_server.load("branding/icon.png");
/// commands.spawn(WindowIcon(Some(icon_handle)));
/// }
///
/// fn main() {
/// App::new()
/// .add_systems(Startup, setup)
/// .add_systems(Update, set_window_icon)
/// .run();
/// }
/// ```
///
/// This functionality [is only known to work on Windows and X11](https://docs.rs/winit/latest/winit/window/struct.Window.html#method.set_window_icon).
pub fn set_window_icon(
images: Res<Assets<Image>>,
mut commands: Commands,
mut query: Query<(Entity, &mut WindowIcon)>,
mut winit_windows: NonSendMut<WinitWindows>,
) {
for (entity, window_icon) in query.iter_mut() {
let icon = {
if let Some(image) = &window_icon.0 {
let Some(icon) = images.get(image) else { continue };
let result: Result<Icon, _> = icon.clone().try_into();

match result {
Ok(icon) => Some(icon),
Err(err) => {
error!("failed to set window icon: {}", err);
commands.entity(entity).remove::<WindowIcon>();
continue;
}
}
} else {
None
}
};

if let Some(icon) = &icon {
for (_id, window) in &mut winit_windows.windows {
window.set_window_icon(Some(icon.clone()));
}
} else {
for (_id, window) in &mut winit_windows.windows {
window.set_window_icon(None);
}
}

info!("window icon set");
commands.entity(entity).remove::<WindowIcon>();
}
}
22 changes: 22 additions & 0 deletions crates/bevy_render/src/texture/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ use crate::{
renderer::{RenderDevice, RenderQueue},
texture::BevyDefault,
};
use anyhow::anyhow;
use bevy_asset::HandleUntyped;
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::system::{lifetimeless::SRes, Resource, SystemParamItem};
use bevy_math::Vec2;
use bevy_reflect::{FromReflect, Reflect, TypeUuid};
use winit::window::Icon;

use std::hash::Hash;
use thiserror::Error;
Expand Down Expand Up @@ -613,6 +615,26 @@ impl CompressedImageFormats {
}
}

impl TryInto<winit::window::Icon> for Image {
type Error = anyhow::Error;

fn try_into(self) -> Result<Icon, Self::Error> {
let Ok(icon) = self.try_into_dynamic() else {
return Err(anyhow!("failed to convert Image to DynamicImage"));
};

let width = icon.width();
let height = icon.height();
let data = icon.into_rgba8().into_raw();

let Ok(icon) = winit::window::Icon::from_rgba(data, width, height) else {
return Err(anyhow!("failed to convert to winit::window::Icon"));
};

Ok(icon)
}
}

#[cfg(test)]
mod test {

Expand Down
2 changes: 2 additions & 0 deletions crates/bevy_render/src/texture/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ mod exr_texture_loader;
mod fallback_image;
#[cfg(feature = "hdr")]
mod hdr_texture_loader;
pub mod icon;
#[allow(clippy::module_inception)]
mod image;
mod image_texture_loader;
Expand All @@ -27,6 +28,7 @@ pub use exr_texture_loader::*;
pub use hdr_texture_loader::*;

pub use fallback_image::*;
pub use icon::*;
pub use image_texture_loader::*;
pub use texture_cache::*;

Expand Down
9 changes: 9 additions & 0 deletions examples/window/window_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use bevy::{
diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
prelude::*,
render::texture::{set_window_icon, WindowIcon},
window::{CursorGrabMode, PresentMode, WindowLevel},
};

Expand All @@ -24,9 +25,11 @@ fn main() {
}))
.add_plugin(LogDiagnosticsPlugin::default())
.add_plugin(FrameTimeDiagnosticsPlugin)
.add_systems(Startup, setup)
.add_systems(
Update,
(
set_window_icon,
change_title,
toggle_cursor,
toggle_vsync,
Expand All @@ -37,6 +40,12 @@ fn main() {
.run();
}

/// Add an icon to the window task bar. Only works in Windows and Linux.
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
let icon_handle = asset_server.load("branding/icon.png");
commands.spawn(WindowIcon(Some(icon_handle)));
}

/// 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<Input<KeyCode>>, mut windows: Query<&mut Window>) {
Expand Down

0 comments on commit 9f9f374

Please sign in to comment.