use concinnity_core::components::FrameInput;
use concinnity_core::components::InputKey;
use concinnity_core::components::KeyEvent;
use concinnity_core::components::KeyMods;
use concinnity_core::components::KeyPress;
use concinnity_core::components::TextInput;
use concinnity_core::ecs::World;
use crate::editor::hook::EditorHook;
use crate::editor::palette;
use crate::editor::palette::{Category, PaletteAction};
use crate::editor::panels::registry::PanelKey;
use crate::editor::widget;
fn hook(entries: Vec<serde_json::Value>) -> EditorHook {
EditorHook::new("unused.jsonl".to_string(), entries)
}
fn palette_world() -> World {
let mut world = World::new();
for id in palette::panel::ids().field_ids() {
world.push_identified(id, TextInput::default());
}
world
}
fn chord(key: InputKey, ctrl: bool, cmd: bool) -> FrameInput {
FrameInput {
key_events: vec![KeyEvent::Press(KeyPress::new(
key,
KeyMods {
ctrl,
cmd,
..KeyMods::NONE
},
))],
ctrl,
cmd,
..Default::default()
}
}
#[test]
fn either_platform_modifier_opens_the_palette() {
for (ctrl, cmd) in [(true, false), (false, true)] {
let mut h = hook(Vec::new());
let mut world = palette_world();
h.drive_palette_toggle(&chord(InputKey::K, ctrl, cmd), &mut world);
assert!(
h.open[PanelKey::Palette],
"ctrl={ctrl} cmd={cmd} did not open it"
);
h.drive_palette_toggle(&chord(InputKey::K, ctrl, cmd), &mut world);
assert!(!h.open[PanelKey::Palette]);
}
}
#[test]
fn an_unmodified_k_leaves_the_palette_closed() {
let mut h = hook(Vec::new());
let mut world = palette_world();
h.drive_palette_toggle(&chord(InputKey::K, false, false), &mut world);
assert!(!h.open[PanelKey::Palette]);
}
#[test]
fn opening_blurs_the_query_for_one_frame() {
let mut h = hook(Vec::new());
let mut world = palette_world();
h.drive_palette_toggle(&chord(InputKey::K, true, false), &mut world);
assert!(h.palette.blur);
assert!(!h.make_palette_view([0.0, 0.0]).focus, "blurred this frame");
}
#[test]
fn typing_reranks_and_rehomes_the_highlight() {
let mut h = hook(Vec::new());
let mut world = palette_world();
h.drive_palette_toggle(&chord(InputKey::K, true, false), &mut world);
let opened = h.palette.matches.len();
assert!(opened > 0, "the launch list is not empty");
h.palette.pick = 3;
widget::seed_field(&mut world, palette::panel::INPUT, "cook");
h.sample_palette_query(&world);
assert_eq!(
h.palette.pick, 0,
"a narrowed list starts at its best answer"
);
assert!(h.palette.matches.len() < opened);
let first = &h.palette.items[h.palette.matches[0]];
assert_eq!(first.label, "/cook");
}
#[test]
fn the_arrows_walk_the_matches_and_stop_at_the_ends() {
let mut h = hook(Vec::new());
let mut world = palette_world();
h.drive_palette_toggle(&chord(InputKey::K, true, false), &mut world);
h.palette_keys(&mut world, &chord(InputKey::Down, false, false));
assert_eq!(h.palette.pick, 1);
h.palette_keys(&mut world, &chord(InputKey::Up, false, false));
assert_eq!(h.palette.pick, 0);
h.palette_keys(&mut world, &chord(InputKey::Up, false, false));
assert_eq!(h.palette.pick, 0, "stops at the top");
}
#[test]
fn committing_a_panel_row_opens_it() {
let mut h = hook(Vec::new());
let mut world = palette_world();
h.drive_palette_toggle(&chord(InputKey::K, true, false), &mut world);
let at = h
.palette
.matches
.iter()
.position(|&i| h.palette.items[i].action == PaletteAction::OpenPanel(PanelKey::Variables))
.expect("the Variables panel is a palette row");
h.palette.pick = at;
h.palette_keys(&mut world, &chord(InputKey::Enter, false, false));
assert!(!h.open[PanelKey::Palette], "committing closes the palette");
assert!(h.open[PanelKey::Variables], "the panel opened");
assert_eq!(
h.panel_order.last().copied(),
Some(PanelKey::Variables),
"and it is frontmost"
);
}
#[test]
fn committing_an_argument_command_seeds_command_mode() {
let mut h = hook(Vec::new());
let mut world = palette_world();
h.drive_palette_toggle(&chord(InputKey::K, true, false), &mut world);
let at = h
.palette
.matches
.iter()
.position(|&i| h.palette.items[i].label == "/add")
.expect("/add is a palette row");
h.palette.pick = at;
h.palette_keys(&mut world, &chord(InputKey::Enter, false, false));
assert!(
h.open[PanelKey::Palette],
"the palette stays up for the arguments"
);
assert_eq!(widget::field_text(&world, palette::panel::INPUT), "/add ");
}
#[test]
fn a_commit_is_remembered_for_the_next_launch_list() {
let mut h = hook(Vec::new());
let mut world = palette_world();
h.drive_palette_toggle(&chord(InputKey::K, true, false), &mut world);
let at = h
.palette
.matches
.iter()
.position(|&i| h.palette.items[i].action == PaletteAction::OpenPanel(PanelKey::Variables))
.expect("the Variables panel is a palette row");
let label = h.palette.items[h.palette.matches[at]].label.clone();
h.palette.pick = at;
h.palette_keys(&mut world, &chord(InputKey::Enter, false, false));
h.drive_palette_toggle(&chord(InputKey::K, true, false), &mut world);
let first = &h.palette.items[h.palette.matches[0]];
assert_eq!(first.label, label, "the last commit leads the list");
}
#[test]
fn a_press_outside_dismisses_without_reaching_the_world() {
let mut h = hook(Vec::new());
let mut world = palette_world();
let vp = [1280.0, 720.0];
h.drive_palette_toggle(&chord(InputKey::K, true, false), &mut world);
let o = h.origin(PanelKey::Palette, vp);
let inside = FrameInput {
mouse_x: o[0] + 20.0,
mouse_y: o[1] + 10.0,
..Default::default()
};
assert!(
!h.route_palette_dismiss(&inside, vp),
"a press on the palette is left to its own hit test"
);
assert!(h.open[PanelKey::Palette]);
let outside = FrameInput {
mouse_x: o[0] - 40.0,
mouse_y: o[1] + 10.0,
..Default::default()
};
assert!(
h.route_palette_dismiss(&outside, vp),
"the press is claimed"
);
assert!(!h.open[PanelKey::Palette]);
}
#[test]
fn world_assets_become_rows_routed_by_type() {
let entries = vec![
serde_json::json!({"type": "Behavior", "args": {"$id": "greeter", "on": "tick", "do": []}}),
];
let mut h = hook(entries);
let mut world = palette_world();
h.drive_palette_toggle(&chord(InputKey::K, true, false), &mut world);
let row = h
.palette
.items
.iter()
.find(|it| it.label == "greeter")
.expect("the authored behavior is a palette row");
assert_eq!(row.category, Category::Asset);
assert_eq!(row.action, PaletteAction::OpenAsset("greeter".to_string()));
}