-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add basic support for window icons #8130
Conversation
9d7147e
to
9f9f374
Compare
examples/window/window_settings.rs
Outdated
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) { | ||
let icon_handle = asset_server.load("branding/icon.png"); | ||
commands.spawn(WindowIcon(Some(icon_handle))); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might be possible to simplify all of this by adding an icon_path: Option<PathBuf>
or an enum with variants for a path and for bytes for specific image formats to a field on the Window
struct and then adding the two systems above when one of the low-level Bevy plugins runs. But first I thought it would be good to check whether this PR gets the general idea right.
A benefit of having the developer queue up the two systems is that they could potentially change the icon during the running of the game. I'm not sure anyone would ever want that, but this is something that was demonstrated in an earlier PR for this story.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, let's keep this simple here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, let's keep this simple here.
By "simple," are you thinking API-simple (adding a field to Window
), or implementation-simple (sticking with the current approach)?
Thinking you were probably referring to the enum idea, so sticking with the current approach.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I meant that I prefer the current approach :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Once it occurred to me that several moving parts would need to be orchestrated in order to make this work without issues, I took the liberty of making a plugin. I'm not attached to it, though, so no problem if it's dropped.
info!("window icon set"); | ||
commands.entity(entity).remove::<WindowIcon>(); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's two uncorrelated collections here — the WindowIcon
s in flight, and the various windows. In this PR we're just ignoring that complexity. I wasn't sure how much precision would be needed in a first pass.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we probably want to align the window icon to the corresponding window, since we're storing it as a component.
crates/bevy_render/Cargo.toml
Outdated
@@ -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 } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might make sense to re-export winit::window::Icon
from bevy_winit
instead of including winit
here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, I think that's cleaner.
@@ -613,6 +615,23 @@ impl CompressedImageFormats { | |||
} | |||
} | |||
|
|||
// Convert an [`Image`] to `winit::window::Icon`. | |||
impl TryInto<Icon> for Image { | |||
type Error = anyhow::Error; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A previous PR had a purpose-specific error type, which might be good to use here (not sure).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I'd prefer to use a custom error type over anyhow.
} | ||
|
||
info!("window icon set"); | ||
commands.entity(entity).remove::<WindowIcon>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Definitely don't think we should be removing the Window icon component here once it's set.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was worried about a busy loop of always attempting to check whether an icon had been set and then exiting early if it was. I looked into using the typestate pattern, e.g., WindowIcon<Loading>
, WindowIcon<Loaded>
, etc., and then only querying for WindowIcon<Loading>
, but ran into difficulties with that.
Is there a way to remove this code from the hot path once the icon has been set?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Set a run condition on the system itself, checking if any of the WindowIcon
components have been changed. That will stop it from running at all, and will be extremely quick to check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I acquaint myself with Bevy, I'm realizing that the Changed<WindowIcon>
state will only last a tick, until the next &mut WindowIcon
occurs, while the image asset itself will take several ticks to load, by which point the Changed<WindowIcon>
state will have been cleared two or more ticks in the past. This delay between the Changed<T>
and the loaded image asset seems to rule out a simple read-only check of some kind, instead requiring a component-specific state machine to track when things are in place to set the icon.
If the state machine is stored on the component itself, mutating the component to update the state enum will interfere with the Changed<WindowIcon>
check. And polling in the hot path for whether to set the icon seems to be unavoidable unless loading is carried out in a separate app state. Using app state seems to have the drawback of not being generic and instead requiring an app-specific setup.
I'm sure this is all just me misunderstanding how one is supposed to do things in Bevy when an asset needs to be fully loaded. I'll poke around and try to better understand the desired approach in this case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm thinking something similar to the implementation for run_once might work here.
mut query: Query<(Entity, &mut WindowIcon)>, | ||
mut winit_windows: NonSendMut<WinitWindows>, | ||
) { | ||
for (entity, window_icon) in query.iter_mut() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This whole system could probably be simplified significantly.
Basically:
for (id, window) in &mut winit_windows.windows {
if let Ok(WindowIcon(maybe_handle)) = query.get(id) {
window.set_window_icon(maybe_handle);
} else {
window.set_window_icon(None);
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried to stick pretty close to this. One thing I noticed is that it might be more efficient for the outer loop to start with the query and check for associated windows than the other way around. Probably not needed, though.
|
||
/// An icon that can be placed at the top left of the window. | ||
#[derive(Component, Debug)] | ||
pub struct WindowIcon(pub Option<Handle<Image>>); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A simple new(handle: Handle<Image>)
method would go a long way to improving usability here :)
crates/bevy_render/Cargo.toml
Outdated
@@ -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" } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be made into an optional (but on by default) dependency, behind a winit
feature flag.
Okay good! The overall module structure like this is much less bad than I was worried about. I think we can make the underlying system a bit less janky, but once that's done this should work nicely. |
Does it make sense to provide an API for setting the window icon? On Linux, you're supposed to use a .desktop file (which also lets it show up in UI's). On Windows, you're supposed to embed a resource file into the exe. On macOS I'm not sure. |
I like the cross-platform nature of this: it'll make the build process significantly less annoying when releasing games. Although maybe this approach messes up how apps show up in e.g. the launcher too? |
macOS, Android and iOS are also handled by providing a file with the binary (and packaged in the apk or app) |
making |
I think everyone feels weird about it. See this thread for context. |
Looking into why the build is failing for |
you enabled the wayland feature of |
I probably misunderstood this request, in that case. Or, if not, I'm thinking I should add the missing dependency to CI. |
crates/bevy_render/Cargo.toml
Outdated
@@ -27,6 +28,8 @@ wgpu_trace = ["wgpu/trace"] | |||
ci_limits = [] | |||
webgl = ["wgpu/webgl"] | |||
|
|||
bevy_winit = ["bevy_winit/x11", "bevy_winit/wayland"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CI failures comes from this line, none of those features should be enabled here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you! That fixed things.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
687d662
to
617c531
Compare
Is this approach in this PR basically sound? Should I clean it up and move it out of draft? Or should I abandon it and let someone else take a crack at it? I'm fine abandoning it, if people think this isn't quite the right approach and it's going to be hard to get it into shape. |
how is progress looking? |
Hi @Adrian8115. This PR is not active. I should close it. |
Is there a pr that allows us to set the icon of a winow easily? This is a chain of prs that all link to each other but none have been successful |
After this PR, I stopped keeping tabs on Bevvy. I do not know what the current situation is, unfortunately. |
The correct solution imo is to package an icon with your compiled app.
|
Objective
Fixes #1031.
This PR adds an icon to the left of the title in the window taskbar on Windows and X11.
Looking for high-level feedback on whether this implementation is going in the right direction before cleaning up and submitting. A lot of the concepts in Bevy are new to me, and it sounds like the approach that has been suggested is a workaround and might not have a lot of prior art to consult. More context here.
Solution
Open questions
bevy_render
crate? Perhaps this will require adding backbevy_winit = ["bevy_winit/x11", "bevy_winit/wayland"]
and adding thewayland-sys
library to CI. But there might be another way to fix the failing test run.Changelog
Add an icon to the left of the title in the window taskbar on Windows and X11.
Migration Guide
N/A