-
-
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
Windows as Entities
#4947
Windows as Entities
#4947
Conversation
crates/bevy_winit/src/lib.rs
Outdated
CoreStage::PostUpdate, | ||
SystemSet::new() | ||
.label(ModifiesWindows) | ||
.with_system(update_title) | ||
.with_system(update_window_mode) | ||
.with_system(update_decorations) | ||
.with_system(update_scale_factor) | ||
.with_system(update_resizable) | ||
.with_system(update_position) | ||
.with_system(update_minimized) | ||
.with_system(update_maximized) | ||
.with_system(update_resolution) | ||
.with_system(update_cursor_icon) | ||
.with_system(update_cursor_lock_mode) | ||
.with_system(update_cursor_visibility) | ||
.with_system(update_cursor_position) | ||
.with_system(update_resize_contraints) | ||
.with_system(update_present_mode) | ||
.with_system(destroy_windows), // TODO: This should probably go last? |
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.
Splitting this into multiple systems seemed like the natural thing to do once the commands are their own events. Having many small systems instead of a big one should make the plugin more approachable to new users wanting to contribute so I hope this does not cause a lot of timing or race-condition issues
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.
We should scan for and resolve system order ambiguities before merging this. That said, I very much appreciate this design.
|
||
/// A collection of [`Window`]s with unique [`WindowId`]s. | ||
#[derive(Debug, Default)] | ||
pub struct Windows { |
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.
All places that previously used Windows
should now instead do a Query<Entity, With<Window>>
#[derive(Debug)] | ||
pub struct SetTitleCommand { | ||
pub entity: Entity, | ||
pub title: String, | ||
} | ||
|
||
impl Command for SetTitleCommand { | ||
fn write(self, world: &mut World) { | ||
if let Some(_) = world.get::<Window>(self.entity) { | ||
let mut event = world.resource_mut::<Events<SetTitleCommand>>(); | ||
event.send(self); | ||
} else { | ||
panic!("Trying to enact window commands on an entity without a window-component"); | ||
} | ||
} | ||
} |
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.
All window commands now look like this: They are a Command
that sends itself as an event that the window backend will pick up
// TODO: Can this be solved in any other way? | ||
/// Returns true if this commands has the entity present | ||
pub fn has_entity(&self, entity: Entity) -> bool { | ||
return self.entities.contains(entity); | ||
} |
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 is needed to be able to not grant WindowCommands
on an entity that does not exist, see: https://github.com/bevyengine/bevy/pull/4947/files#diff-9e0af223dcea6a0265d880b70764a4362c0c84438fb28f5f558a369cda91021cR27
f9b3c3e
to
b64d741
Compare
// if not we'll regress on this | ||
let window_descriptor = app | ||
.world | ||
.get_resource::<WindowDescriptor>() | ||
.map(|descriptor| (*descriptor).clone()) | ||
.unwrap_or_default(); | ||
let mut create_window_event = app.world.resource_mut::<Events<CreateWindow>>(); | ||
create_window_event.send(CreateWindow { | ||
id: WindowId::primary(), | ||
descriptor: window_descriptor, | ||
}); | ||
|
||
let window_id = app.world.spawn().id(); | ||
|
||
let mut system_state: SystemState<(Commands, ResMut<PrimaryWindow>)> = SystemState::new(&mut app.world); | ||
let (mut commands, mut primary_window) = system_state.get_mut(&mut app.world); | ||
primary_window.window = Some(window_id); | ||
// create_primary_window(commands, primary_window); | ||
|
||
|
||
let command = CreateWindowCommand { entity: window_id, descriptor: window_descriptor }; | ||
// Apply the command directly on the world | ||
// I wonder if this causes timing issue: this will trigger a CreateWindowCommand event, but will bevy_winit exist in time to listen to the event? | ||
command.write(&mut app.world); |
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.
TODO: Replace with systemState api
@Weibye I think you can just close this, since you can reopen it with just a click if needed. |
Fair point! |
This is going to be both large and controversial, so I would very much appreciate feedback and input on this initial direction so that this turns out the best it can be.
I will update the PR description with more details as I progress.
Current Status: Check todo list at the bottom.
Objective
Entities
as that would allow users to operate on windows the same as they would already be doing withcommands,
systems
andcomponents
Solution
High Level Overview
Windows in Bevy has now become entities with components. The user can query these components to read information about the windows. The user can use change detection to check if various components has changed. These components are 'readony'-ish for the user.
To mutate the windows, the user will now use
WindowCommands
and applyCommand
s to theEntity
with the window. The window backend is responsible handling these commands and updating the data on the components accordingly.bevy_window
changesWindow_id
WindowId
has been replaced byEntity
throughout the entire projectWindow
Window
has been split into multiple components:Window
(marker)WindowCursor
WindowCursorPosition
WindowHandle
WindowPresentation
WindowModeComponent
WindowPosition
WindowResolution
WindowTitle
WindowCanvas
WindowDecoraded
(marker)WindowCurrentlyFocused
(marker)WindowResizable
(marker)WindowTransparent
(marker)WindowMinimized
(marker)WindowMaximized
(marker)bevy_winit
) to create, update and remove these componentsupdate_x_from_backend()
or similar and should only be used by the window backend.Windows
Windows
has been removed completelyWindow
marker componentQuery<Entity, With<Window>>
WindowCommandsExtension
WindowCommandsExtensions
so that we cancommands.window(entity)
to getWindowCommands
ÈntityCommands
workWindowCommand
enum
impl Command
command.write()
for the window backend to handlePrimaryWindow
PrimaryWindow
is a new resource that thebevy_window
plugin is responsible for creating when setting up the initial window.Entity
of the window that is considered the primary windowbevy_winit
changesbevy_render
changesbevy_text
bevy_ui
Internal changes:
Res<Windows>
-> Querywindow.
Changelog
bevy_render
Migration Guide
WindowId
->Entity
window: Res<Window>
->windows: Query<&RelevantWindowComponent, With<Window>>
windows: Res<Windows>
->windows: Query<&RelevantWindowComponent, With<Window>>
Remaining Tasks
bevy_window,
bevy_winit
andbevy_render
when it comes to window handle creation.