use bevy::prelude::*;
use bevy_gearbox::{StateMachine};
use bevy_egui::egui;
use crate::editor_state::{EditorState, NodeAction, NodeActionTriggered, NodeContextMenuRequested, TransitionContextMenuRequested, DeleteNode, SetInitialStateRequested, DeleteTransitionByEdge, SaveStateMachine, CloseMachineRequested};
use crate::components::{NodeType, LeafNode};
use crate::{StateMachinePersistentData, StateMachineTransientData};
use crate::node_kind::{AddChildClicked, MakeParallelClicked, MakeParentClicked, MakeLeafClicked};
pub fn handle_context_menu_request(
node_context_menu_requested: On<NodeContextMenuRequested>,
mut editor_state: ResMut<EditorState>,
) {
editor_state.background_context_menu_position = None;
editor_state.transition_context_menu = None;
editor_state.transition_context_menu_position = None;
editor_state.show_machine_selection_menu = false;
editor_state.context_menu_entity = Some(node_context_menu_requested.entity);
editor_state.context_menu_position = Some(node_context_menu_requested.position);
editor_state.suppress_background_context_menu_once = true;
}
pub fn handle_transition_context_menu_request(
transition_context_menu_requested: On<TransitionContextMenuRequested>,
mut editor_state: ResMut<EditorState>,
) {
editor_state.background_context_menu_position = None;
editor_state.context_menu_entity = None;
editor_state.context_menu_position = None;
editor_state.show_machine_selection_menu = false;
editor_state.transition_context_menu = Some((transition_context_menu_requested.source_entity, transition_context_menu_requested.target_entity, transition_context_menu_requested.event_type.clone(), transition_context_menu_requested.edge_entity));
editor_state.transition_context_menu_position = Some(transition_context_menu_requested.position);
editor_state.suppress_background_context_menu_once = true;
}
pub fn handle_node_action(
node_action_triggered: On<NodeActionTriggered>,
mut commands: Commands,
mut editor_state: ResMut<EditorState>,
mut q_sm: Query<(&mut StateMachinePersistentData, &mut StateMachineTransientData), With<StateMachine>>,
q_child_of: Query<&bevy_gearbox::StateChildOf>,
q_name: Query<&Name>,
) {
let selected_machine = q_child_of.root_ancestor(node_action_triggered.entity);
let Ok((mut persistent_data, mut transient_data)) = q_sm.get_mut(selected_machine) else {
return;
};
match node_action_triggered.action {
NodeAction::Inspect => {
editor_state.inspected_entity = Some(node_action_triggered.entity);
}
NodeAction::AddChild => {
let child_entity = commands.spawn((
bevy_gearbox::StateChildOf(node_action_triggered.entity),
Name::new("New State"),
)).id();
if let Some(parent_node) = persistent_data.nodes.get(&node_action_triggered.entity) {
let parent_pos = match parent_node {
NodeType::Leaf(leaf_node) => leaf_node.entity_node.position,
NodeType::Parent(parent_node) => parent_node.entity_node.position,
};
let child_pos = parent_pos + egui::Vec2::new(50.0, 50.0);
let leaf_node = LeafNode::new(child_pos);
persistent_data.nodes.insert(child_entity, NodeType::Leaf(leaf_node));
}
let parent_entity = node_action_triggered.entity;
if let Some(&nk_root) = transient_data.node_kind_roots.get(&parent_entity) {
commands.trigger(AddChildClicked::new(nk_root));
commands.trigger(crate::node_kind::ChildAdded::new(nk_root));
}
}
NodeAction::Rename => {
let entity_name = q_name.get(node_action_triggered.entity).unwrap().to_string();
transient_data.text_editing.start_editing(node_action_triggered.entity, &entity_name);
}
NodeAction::MakeParallel => {
let state_entity = node_action_triggered.entity;
if let Some(&nk_root) = transient_data.node_kind_roots.get(&state_entity) {
commands.trigger(MakeParallelClicked::new(nk_root));
}
}
NodeAction::MakeParent => {
let state_entity = node_action_triggered.entity;
if let Some(&nk_root) = transient_data.node_kind_roots.get(&state_entity) {
commands.trigger(MakeParentClicked::new(nk_root));
}
}
NodeAction::MakeLeaf => {
let state_entity = node_action_triggered.entity;
if let Some(&nk_root) = transient_data.node_kind_roots.get(&state_entity) {
commands.trigger(MakeLeafClicked::new(nk_root));
}
}
NodeAction::SetAsInitialState => {
let child_entity = node_action_triggered.entity;
commands.trigger(SetInitialStateRequested { child_entity });
}
NodeAction::ResetRegion => {
commands.trigger(bevy_gearbox::ResetRegion::new(selected_machine));
}
NodeAction::Delete => {
commands.trigger(DeleteNode {
entity: node_action_triggered.entity,
});
}
}
}
pub fn render_context_menu(
ctx: &egui::Context,
editor_state: &mut EditorState,
commands: &mut Commands,
all_entities: &Query<(Entity, Option<&Name>, Option<&bevy_gearbox::InitialState>)>,
q_child_of: &Query<&bevy_gearbox::StateChildOf>,
q_parallel: &Query<&bevy_gearbox::Parallel>,
) {
if let (Some(entity), Some(position)) = (editor_state.context_menu_entity, editor_state.context_menu_position) {
let menu_id = egui::Id::new("context_menu").with(entity);
let mut last_menu_rect: Option<egui::Rect> = None;
egui::Area::new(menu_id)
.fixed_pos(position)
.order(egui::Order::Foreground)
.show(ctx, |ui| {
egui::Frame::popup(ui.style())
.show(ui, |ui| {
ui.set_min_width(120.0);
if ui.button("Inspect").clicked() {
commands.trigger(NodeActionTriggered {
entity,
action: NodeAction::Inspect,
});
editor_state.context_menu_entity = None;
editor_state.context_menu_position = None;
ui.close();
}
if ui.button("Rename").clicked() {
commands.trigger(NodeActionTriggered {
entity,
action: NodeAction::Rename,
});
editor_state.context_menu_entity = None;
editor_state.context_menu_position = None;
ui.close();
}
let is_parent = all_entities.get(entity).ok().and_then(|(_,_,init)| init.map(|_|())).is_some();
let is_parallel = q_parallel.get(entity).is_ok();
let is_leaf = !is_parent && !is_parallel;
let is_root = editor_state.open_machines.iter().any(|m| m.entity == entity);
if is_leaf {
if ui.button("Make Parallel").clicked() {
commands.trigger(NodeActionTriggered { entity, action: NodeAction::MakeParallel });
editor_state.context_menu_entity = None;
editor_state.context_menu_position = None;
ui.close();
}
if ui.button("Make Parent").clicked() {
commands.trigger(NodeActionTriggered { entity, action: NodeAction::MakeParent });
editor_state.context_menu_entity = None;
editor_state.context_menu_position = None;
ui.close();
}
}
if is_root {
if ui.button("💾 Save Machine").clicked() {
commands.trigger(SaveStateMachine { entity });
editor_state.context_menu_entity = None;
editor_state.context_menu_position = None;
ui.close();
}
if ui.button("✕ Close Machine").clicked() {
commands.trigger(CloseMachineRequested { entity });
editor_state.context_menu_entity = None;
editor_state.context_menu_position = None;
ui.close();
}
ui.separator();
if ui.button("↺ Reset Machine").clicked() {
commands.trigger(NodeActionTriggered { entity, action: NodeAction::ResetRegion });
editor_state.context_menu_entity = None;
editor_state.context_menu_position = None;
ui.close();
}
}
if is_parent {
if !is_root {
if ui.button("↺ Reset Region").clicked() {
commands.trigger(NodeActionTriggered { entity, action: NodeAction::ResetRegion });
editor_state.context_menu_entity = None;
editor_state.context_menu_position = None;
ui.close();
}
}
if ui.button("Make Parallel").clicked() {
commands.trigger(NodeActionTriggered { entity, action: NodeAction::MakeParallel });
editor_state.context_menu_entity = None;
editor_state.context_menu_position = None;
ui.close();
}
if ui.button("Make Leaf").clicked() {
commands.trigger(NodeActionTriggered { entity, action: NodeAction::MakeLeaf });
editor_state.context_menu_entity = None;
editor_state.context_menu_position = None;
ui.close();
}
if ui.button("Add child").clicked() {
commands.trigger(NodeActionTriggered { entity, action: NodeAction::AddChild });
editor_state.context_menu_entity = None;
editor_state.context_menu_position = None;
ui.close();
}
}
if is_parallel {
if !is_root {
if ui.button("↺ Reset Region").clicked() {
commands.trigger(NodeActionTriggered { entity, action: NodeAction::ResetRegion });
editor_state.context_menu_entity = None;
editor_state.context_menu_position = None;
ui.close();
}
}
if ui.button("Make Leaf").clicked() {
commands.trigger(NodeActionTriggered { entity, action: NodeAction::MakeLeaf });
editor_state.context_menu_entity = None;
editor_state.context_menu_position = None;
ui.close();
}
if ui.button("Make Parent").clicked() {
commands.trigger(NodeActionTriggered { entity, action: NodeAction::MakeParent });
editor_state.context_menu_entity = None;
editor_state.context_menu_position = None;
ui.close();
}
if ui.button("Add child").clicked() {
commands.trigger(NodeActionTriggered { entity, action: NodeAction::AddChild });
editor_state.context_menu_entity = None;
editor_state.context_menu_position = None;
ui.close();
}
}
if let Ok(child_of) = q_child_of.get(entity) {
let parent_has_initial = all_entities
.get(child_of.0)
.ok()
.and_then(|(_,_,init)| init.map(|_| ()))
.is_some();
if parent_has_initial {
if ui.button("Set as Initial State").clicked() {
commands.trigger(NodeActionTriggered { entity, action: NodeAction::SetAsInitialState });
editor_state.context_menu_entity = None;
editor_state.context_menu_position = None;
ui.close();
}
}
}
if ui.button("🗑 Delete Node").clicked() {
commands.trigger(NodeActionTriggered {
entity,
action: NodeAction::Delete,
});
editor_state.context_menu_entity = None;
editor_state.context_menu_position = None;
ui.close();
}
last_menu_rect = Some(ui.min_rect());
});
});
if let Some(menu_rect) = last_menu_rect {
if ctx.input(|i| i.pointer.any_click()) {
let pointer_pos = ctx.input(|i| i.pointer.hover_pos().unwrap_or_default());
if !menu_rect.contains(pointer_pos) {
editor_state.context_menu_entity = None;
editor_state.context_menu_position = None;
}
}
}
}
if let (Some((source, target, _, edge_entity)), Some(position)) = (
editor_state.transition_context_menu.clone(),
editor_state.transition_context_menu_position
) {
let menu_id = egui::Id::new("transition_context_menu").with((source, target));
let mut last_menu_rect: Option<egui::Rect> = None;
egui::Area::new(menu_id)
.fixed_pos(position)
.order(egui::Order::Foreground)
.show(ctx, |ui| {
egui::Frame::popup(ui.style())
.show(ui, |ui| {
ui.set_min_width(120.0);
if ui.button("Inspect").clicked() {
editor_state.inspected_entity = Some(edge_entity);
editor_state.transition_context_menu = None;
editor_state.transition_context_menu_position = None;
ui.close();
}
if ui.button("🗑 Delete Transition").clicked() {
commands.trigger(DeleteTransitionByEdge { edge_entity });
editor_state.transition_context_menu = None;
editor_state.transition_context_menu_position = None;
ui.close();
}
last_menu_rect = Some(ui.min_rect());
});
});
if let Some(menu_rect) = last_menu_rect {
if ctx.input(|i| i.pointer.any_click()) {
let pointer_pos = ctx.input(|i| i.pointer.hover_pos().unwrap_or_default());
if !menu_rect.contains(pointer_pos) {
editor_state.transition_context_menu = None;
editor_state.transition_context_menu_position = None;
}
}
}
}
}