use concinnity_core::components::FrameInput;
use concinnity_core::ecs::World;
use concinnity_host::thread::asset_id;
use crate::frame_hook::FrameHook;
use crate::editor::hook::tests::fixtures::{click_at, hook, pick_world, set_input};
use crate::editor::hook::{EditorHook, declared_id, entry_type};
use crate::editor::panels::registry::PanelKey;
use crate::editor::hook::tests::fixtures::active;
use crate::editor::sim;
fn right_click_at(world: &mut World, h: &mut EditorHook, pos: [f32; 2]) {
set_input(
world,
FrameInput {
viewport: [1280.0, 720.0],
mouse_x: pos[0],
mouse_y: pos[1],
right_click: true,
..Default::default()
},
);
h.tick(world);
}
fn click_menu_row(
world: &mut World,
h: &mut EditorHook,
pick: &crate::editor::create_menu::MenuItem,
) {
let o = h.create_menu.as_ref().expect("menu open").origin;
let items = h.create_menu_items();
let row = items
.iter()
.position(|i| i == pick)
.unwrap_or_else(|| panic!("{pick:?} not in {items:?}"));
let r = crate::editor::create_menu::row_rect(o, row);
click_at(world, h, [r[0] + 4.0, r[1] + 4.0]);
}
#[test]
fn right_click_creates_a_positioned_type_at_the_surface_point() {
asset_id::reset_interner();
let ground = asset_id::intern("ground");
let mut world = pick_world(
[0.0; 3],
vec![(ground, [-20.0, -1.0, -20.0], [20.0, 0.0, 20.0])],
);
let mut h = hook(vec![serde_json::json!({
"type": "Prop",
"args": { "$id": "ground", "mesh": "demo", "position": [0.0, -0.5, 0.0] }
})]);
right_click_at(&mut world, &mut h, [640.0, 500.0]);
let menu = h.create_menu.as_ref().expect("the right press opens it");
assert!(
menu.pos[1].abs() < 1e-3,
"captured the ground-top landing point: {:?}",
menu.pos
);
let before = h.entries.len();
click_menu_row(
&mut world,
&mut h,
&crate::editor::create_menu::MenuItem::Type("PointLight"),
);
assert!(h.create_menu.is_none(), "picking a row closes the menu");
assert_eq!(h.entries.len(), before + 1);
let created = &h.entries[before];
assert_eq!(entry_type(created), Some("PointLight"));
assert!(
created["args"]["position"][1].as_f64().unwrap().abs() < 1e-3,
"{created}"
);
assert_eq!(declared_id(created), None, "a placed asset is anonymous");
assert_eq!(active(&h).as_deref(), Some("PointLight#0"));
assert!(h.dirty);
h.undo(&mut world);
assert_eq!(h.entries.len(), before, "one undo removes the creation");
}
#[test]
fn prefab_rows_create_a_prop_instance() {
asset_id::reset_interner();
let mut world = pick_world([0.0; 3], vec![]);
let mut h = hook(vec![serde_json::json!({
"type": "Prefab", "args": {"$id": "door_set"}
})]);
right_click_at(&mut world, &mut h, [640.0, 500.0]);
assert!(h.create_menu.is_some());
click_menu_row(
&mut world,
&mut h,
&crate::editor::create_menu::MenuItem::PrefabHeader { open: false },
);
assert!(
h.create_menu.as_ref().unwrap().prefabs_open,
"the header row unfolds the section"
);
click_menu_row(
&mut world,
&mut h,
&crate::editor::create_menu::MenuItem::Prefab("door_set".to_string()),
);
assert!(h.create_menu.is_none());
let created = h.entries.last().unwrap();
assert_eq!(entry_type(created), Some("Prop"));
assert_eq!(created["args"]["prefab"], "door_set");
assert_eq!(declared_id(created), None, "a placed instance is anonymous");
assert_eq!(active(&h).as_deref(), Some("Prop#0"));
}
#[test]
fn the_create_menu_dismisses_on_click_away_and_respects_claimed_regions() {
asset_id::reset_interner();
let mut world = pick_world([0.0; 3], vec![]);
let mut h = hook(Vec::new());
right_click_at(&mut world, &mut h, [640.0, 500.0]);
assert!(h.create_menu.is_some());
let before = h.entries.len();
click_at(&mut world, &mut h, [30.0, 700.0]);
assert!(h.create_menu.is_none(), "a click away dismisses");
assert_eq!(h.entries.len(), before, "and creates nothing");
right_click_at(&mut world, &mut h, [200.0, 10.0]);
assert!(h.create_menu.is_none());
let o = h.origin(PanelKey::Preview, [1280.0, 720.0]);
right_click_at(&mut world, &mut h, [o[0] + 10.0, o[1] + 10.0]);
assert!(h.create_menu.is_none());
h.sim.state = sim::SimState::Playing;
right_click_at(&mut world, &mut h, [640.0, 500.0]);
assert!(h.create_menu.is_none());
}