-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Windows as Entities
#4947
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
Conversation
| 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
Entitiesas that would allow users to operate on windows the same as they would already be doing withcommands,systemsandcomponentsSolution
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
WindowCommandsand applyCommands to theEntitywith the window. The window backend is responsible handling these commands and updating the data on the components accordingly.bevy_windowchangesWindow_idWindowIdhas been replaced byEntitythroughout the entire projectWindowWindowhas been split into multiple components:Window(marker)WindowCursorWindowCursorPositionWindowHandleWindowPresentationWindowModeComponentWindowPositionWindowResolutionWindowTitleWindowCanvasWindowDecoraded(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.WindowsWindowshas been removed completelyWindowmarker componentQuery<Entity, With<Window>>WindowCommandsExtensionWindowCommandsExtensionsso that we cancommands.window(entity)to getWindowCommandsÈntityCommandsworkWindowCommandenumimpl Commandcommand.write()for the window backend to handlePrimaryWindowPrimaryWindowis a new resource that thebevy_windowplugin is responsible for creating when setting up the initial window.Entityof the window that is considered the primary windowbevy_winitchangesbevy_renderchangesbevy_textbevy_uiInternal changes:
Res<Windows>-> Querywindow.Changelog
bevy_renderMigration Guide
WindowId->Entitywindow: Res<Window>->windows: Query<&RelevantWindowComponent, With<Window>>windows: Res<Windows>->windows: Query<&RelevantWindowComponent, With<Window>>Remaining Tasks
bevy_window,bevy_winitandbevy_renderwhen it comes to window handle creation.