use crate::prelude::*; pub(crate) struct EditorPlugin; impl Plugin for EditorPlugin { fn build(&self, app: &mut App) { app.init_state::() .add_systems(Startup, init_editor) .add_systems( Update, toggle_editor_state.run_if(input_just_pressed(KeyCode::F3)), ) .add_systems( OnTransition { from: EditorState::Open, to: EditorState::Closed, }, toggle_editor_window, ) .add_systems( OnTransition { from: EditorState::Closed, to: EditorState::Open, }, toggle_editor_window, ) .add_systems( Update, handle_window_close.run_if(on_event::()), ); } } /// Tracking the open/closed state of the editor #[derive(States, Debug, Clone, PartialEq, Eq, Hash, Default, Component)] enum EditorState { /// The editor is closed => the editor window is disabled #[default] Closed, /// The editor is open => the editor window is visible Open, } impl std::ops::Not for &EditorState { type Output = EditorState; fn not(self) -> Self::Output { match self { EditorState::Open => EditorState::Closed, EditorState::Closed => EditorState::Open, } } } impl std::convert::From<&EditorState> for bool { fn from(value: &EditorState) -> bool { match value { EditorState::Open => true, EditorState::Closed => false, } } } /// Tag component for editor entities #[derive(Component)] struct EditorTag; /// At startup initialize some editor components fn init_editor(mut commands: Commands) { // Editor window let editor_window = commands .spawn(( EditorTag, Window { visible: false, title: "Editor".into(), ..default() }, )) .id(); // Editor camera let editor_camera = commands .spawn(( EditorTag, Camera3dBundle { camera: Camera { target: RenderTarget::Window(WindowRef::Entity(editor_window)), ..default() }, ..default() }, )) .id(); // Editor UI elements commands .spawn(( EditorTag, TargetCamera(editor_camera), NodeBundle { ..default() }, )) .with_children(|parent| { parent.spawn(( EditorTag, TextBundle::from_section( "Welcome to the editor", TextStyle { color: Color::WHITE, ..default() }, ), )); }); } fn toggle_editor_state( curr_state: Res>, mut next_state: ResMut>, keys: Res>, mut catch: Local, ) { if keys.pressed(KeyCode::F3) && !*catch { *catch = true; match curr_state.get() { EditorState::Open => next_state.set(EditorState::Closed), EditorState::Closed => next_state.set(EditorState::Open), } } else { *catch = false; } } fn toggle_editor_window( state: Res>, mut windows: Query<&mut Window, With>, mut cameras: Query<&mut Camera, With>, ) { let curr = state.get().into(); cameras.iter_mut().for_each(|mut camera| { camera.is_active = curr; }); windows.iter_mut().for_each(|mut window| { window.visible = curr; }) } /// Handles window close requests which are only weird because of the editor /// When the editor window closes, just make it invisible and change the editor state to "Closed". fn handle_window_close( mut events: EventReader, editor_windows: Query>, mut editor_state: ResMut>, ) { events .read() // Filter events down to jus those affecting the editor window .filter_map(|WindowCloseRequested { window }| editor_windows.get(*window).ok()) // For each related close event, send out a "Close the editor" state transition .for_each(|_| { editor_state.set(EditorState::Closed); }); }