use concinnity_cook::authoring::registry::RegisteredType;
use concinnity_core::components::FrameInput;
use concinnity_core::ecs::World;
use crate::editor::asset_handle::AssetHandle;
use crate::editor::create_menu;
use crate::editor::hook::drag;
use crate::editor::hook::{EditorHook, names_of_types};
use crate::editor::hud;
use crate::editor::panels::registry::{self, PanelKey};
use crate::editor::widget::{self, point_in};
pub(in crate::editor::hook) struct CreateMenu {
pub(in crate::editor::hook) origin: [f32; 2],
pub(in crate::editor::hook) pos: [f32; 3],
pub(in crate::editor::hook) prefabs_open: bool,
}
impl EditorHook {
pub(in crate::editor::hook) fn create_menu_items(&self) -> Vec<create_menu::MenuItem> {
let prefabs = names_of_types(&self.entries, &["Prefab"]);
let open = self.create_menu.as_ref().is_some_and(|m| m.prefabs_open);
create_menu::items(&prefabs, open)
}
pub(in crate::editor::hook) fn open_create_menu(
&mut self,
input: &FrameInput,
vp: [f32; 2],
world: &World,
) {
let (mx, my) = (input.mouse_x, input.mouse_y);
if self.sim.playing() || my <= hud::BAR_H || self.over_open_panel(mx, my, vp) {
self.create_menu = None;
return;
}
let Some(pos) = self.drop_point(world, vp, [mx, my], input.ctrl) else {
return;
};
let rows = create_menu::items(&names_of_types(&self.entries, &["Prefab"]), false).len();
if rows == 0 {
return;
}
self.create_menu = Some(CreateMenu {
origin: create_menu::origin([mx, my], rows, vp),
pos,
prefabs_open: false,
});
}
pub(super) fn over_open_panel(&self, mx: f32, my: f32, vp: [f32; 2]) -> bool {
PanelKey::ALL.iter().any(|&key| {
registry::panel(key).is_open(self)
&& point_in(
mx,
my,
widget::outer_rect(self.origin(key, vp), self.effective_size(key)),
)
})
}
pub(in crate::editor::hook) fn route_create_menu_click(
&mut self,
input: &FrameInput,
vp: [f32; 2],
) -> bool {
let Some(menu) = &self.create_menu else {
return false;
};
let (o, pos) = (menu.origin, menu.pos);
let items = self.create_menu_items();
let (mx, my) = (input.mouse_x, input.mouse_y);
match create_menu::hit_row(mx, my, o, items.len()) {
Some(row) => match &items[row] {
create_menu::MenuItem::Type(ty) => {
self.create_type_at(ty, pos);
self.create_menu = None;
}
create_menu::MenuItem::PrefabHeader { open } => {
let unfold = !open;
if let Some(m) = &mut self.create_menu {
m.prefabs_open = unfold;
}
let rows = self.create_menu_items().len();
if let Some(m) = &mut self.create_menu {
m.origin = create_menu::origin(m.origin, rows, vp);
}
}
create_menu::MenuItem::Prefab(name) => {
self.create_prefab_at(&name.clone(), pos);
self.create_menu = None;
}
},
None if create_menu::over(mx, my, o, items.len()) => {}
None => self.create_menu = None,
}
true
}
fn create_type_at(&mut self, ty: &str, pos: [f32; 3]) {
let arg = create_menu::position_key(ty);
let args = serde_json::json!({ arg: pos.map(drag::gizmo::round3) });
let key = self
.entries
.push(serde_json::json!({ "type": ty, "args": args }));
self.mark_changed();
self.selection.replace(AssetHandle::Entry(key));
}
fn create_prefab_at(&mut self, prefab: &str, pos: [f32; 3]) {
let Some(args) = drag::content::placement_args(
RegisteredType::Prefab,
prefab,
pos.map(drag::gizmo::round3),
) else {
return;
};
let key = self
.entries
.push(serde_json::json!({ "type": "Prop", "args": args }));
self.mark_changed();
self.selection.replace(AssetHandle::Entry(key));
}
pub(in crate::editor::hook) fn drive_create_menu_draw(
&self,
world: &mut World,
shown: bool,
mouse: [f32; 2],
) {
match (&self.create_menu, shown) {
(Some(menu), true) => {
let items = self.create_menu_items();
create_menu::place(world, menu.origin, &items, mouse);
}
_ => create_menu::hide(world),
}
}
}