pub mod clip_preview;
pub mod event_tracks;
pub mod fsm;
pub mod graph;
pub mod ragdoll;
pub mod saving;
pub mod window;
use std::{any::Any, cmp::Ordering, collections::VecDeque, fmt::Display};
use bevy::{
ecs::{
resource::Resource,
system::{Commands, In, IntoSystem, ResMut, SystemInput},
world::World,
},
log::error,
};
use event_tracks::{EventTrackAction, handle_event_track_action};
use fsm::{FsmAction, handle_fsm_action};
use graph::{GraphAction, handle_graph_action};
use saving::{SaveAction, handle_save_action};
use window::WindowAction;
use crate::ui::native_views::EditorViewUiState;
use super::{UiState, core::ViewAction, windows::WindowId};
#[derive(Resource, Default)]
pub struct PendingActions {
pub actions: PushQueue<EditorAction>,
}
#[derive(Default)]
pub struct ActionContext {
actions: PushQueue<EditorAction>,
}
pub struct PushQueue<T>(VecDeque<T>);
impl<T> Default for PushQueue<T> {
fn default() -> Self {
Self(VecDeque::new())
}
}
impl<T> PushQueue<T> {
pub fn push(&mut self, item: T) {
self.0.push_back(item);
}
}
pub enum EditorAction {
View(ViewAction),
Save(SaveAction),
EventTrack(EventTrackAction),
Graph(GraphAction),
Fsm(FsmAction),
Dynamic(Box<dyn DynamicAction>),
}
pub fn handle_editor_action_queue(world: &mut World, actions: impl Iterator<Item = EditorAction>) {
let mut ctx = ActionContext::default();
for action in actions {
handle_editor_action(world, action, &mut ctx);
}
while let Some(action) = ctx.actions.0.pop_front() {
handle_editor_action(world, action, &mut ctx);
}
}
pub fn handle_editor_action(world: &mut World, action: EditorAction, ctx: &mut ActionContext) {
match action {
EditorAction::View(action) => {
if let Err(err) = world.run_system_cached_with(handle_view_action, action) {
error!("Failed to apply view action: {:?}", err);
}
}
EditorAction::Save(action) => handle_save_action(world, action),
EditorAction::EventTrack(action) => handle_event_track_action(world, action),
EditorAction::Graph(action) => handle_graph_action(world, action),
EditorAction::Fsm(action) => handle_fsm_action(world, action),
EditorAction::Dynamic(action) => action.handle(world, ctx),
}
}
fn handle_view_action(
In(view_action): In<ViewAction>,
mut ui_state: ResMut<UiState>,
mut commands: Commands,
) {
match view_action {
ViewAction::Close(index) => {
let view_state = ui_state.views.remove(index);
commands.entity(view_state.entity).despawn();
if let Some(idx) = ui_state.active_view {
match idx.cmp(&index) {
Ordering::Less => {}
Ordering::Equal => {
ui_state.active_view = None;
}
Ordering::Greater => {
ui_state.active_view = Some(idx - 1);
}
}
}
}
ViewAction::Select(index) => {
ui_state.active_view = Some(index);
}
ViewAction::New(name) => {
commands.queue(|world: &mut World| {
let view_state = EditorViewUiState::empty(world, name);
let mut ui_state = world.resource_mut::<UiState>();
ui_state.new_native_view(view_state);
});
}
}
}
pub fn process_actions_system(world: &mut World) {
world.try_resource_scope::<PendingActions, ()>(|world, mut actions| {
handle_editor_action_queue(world, actions.actions.0.drain(..));
});
}
impl PushQueue<EditorAction> {
pub fn window(&mut self, window: WindowId, event: impl Any + Send + Sync) {
self.push(EditorAction::Dynamic(Box::new(WindowAction {
target: window,
action: Box::new(event),
})))
}
pub fn dynamic(&mut self, action: impl DynamicAction) {
self.push(EditorAction::Dynamic(Box::new(action)));
}
}
pub fn run_handler<I, O, M, S>(
world: &mut World,
msg: impl Display + 'static,
) -> impl FnOnce(S, I::Inner<'_>) + '_
where
I: SystemInput + 'static,
O: 'static,
S: IntoSystem<I, O, M> + 'static,
{
move |closure, input| {
let _ = world
.run_system_cached_with(closure, input)
.inspect_err(|err| error!("{}: {}", msg, err));
}
}
pub trait DynamicAction: Send + Sync + 'static {
fn handle(self: Box<Self>, world: &mut World, ctx: &mut ActionContext);
}