You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

164 lines
4.5 KiB
Rust

use crate::prelude::*;
pub(crate) struct EditorPlugin;
impl Plugin for EditorPlugin {
fn build(&self, app: &mut App) {
app.init_state::<EditorState>()
.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::<WindowCloseRequested>()),
);
}
}
/// 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<State<EditorState>>,
mut next_state: ResMut<NextState<EditorState>>,
keys: Res<ButtonInput<KeyCode>>,
mut catch: Local<bool>,
) {
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<State<EditorState>>,
mut windows: Query<&mut Window, With<EditorTag>>,
mut cameras: Query<&mut Camera, With<EditorTag>>,
) {
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<WindowCloseRequested>,
editor_windows: Query<Entity, With<EditorTag>>,
mut editor_state: ResMut<NextState<EditorState>>,
) {
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);
});
}