use concinnity_core::components::FrameInput;
use concinnity_core::ecs::World;
use concinnity_core::ecs::asset_id::AssetId;
use crate::editor::hook::{EditorHook, panels};
pub(crate) const ID_BASE: u32 = 0x3000_0000;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum PanelKey {
Assets,
Edit,
Preview,
View,
Templates,
Lighting,
Story,
Import,
Health,
Console,
Behavior,
Map,
Variables,
Content,
CharacterShape,
TemplateDetail,
Palette,
Worlds,
}
pub(crate) const PANEL_COUNT: usize = 18;
impl PanelKey {
pub(crate) const ALL: [PanelKey; PANEL_COUNT] = [
PanelKey::Assets,
PanelKey::Edit,
PanelKey::Preview,
PanelKey::View,
PanelKey::Templates,
PanelKey::Lighting,
PanelKey::Story,
PanelKey::Import,
PanelKey::Health,
PanelKey::Console,
PanelKey::Behavior,
PanelKey::Map,
PanelKey::Variables,
PanelKey::Content,
PanelKey::CharacterShape,
PanelKey::TemplateDetail,
PanelKey::Palette,
PanelKey::Worlds,
];
pub(crate) fn index(self) -> usize {
self as usize
}
}
pub(crate) const fn base(key: PanelKey) -> u32 {
ID_BASE
+ match key {
PanelKey::Assets => 0x40,
PanelKey::Edit => 0x200,
PanelKey::Preview => 0x400,
PanelKey::View => 0x500,
PanelKey::Templates => 0x600,
PanelKey::TemplateDetail => 0x700,
PanelKey::Lighting => 0x800,
PanelKey::Story => 0x900,
PanelKey::Import => 0xA00,
PanelKey::Health => 0xB00,
PanelKey::Console => 0x2000,
PanelKey::Behavior => 0x3000,
PanelKey::Variables => 0x4000,
PanelKey::Content => 0x5000,
PanelKey::Palette => 0x8000,
PanelKey::CharacterShape => 0x9000,
PanelKey::Worlds => 0xC000,
PanelKey::Map => 0xF000,
}
}
pub(crate) trait Panel: Sync {
fn key(&self) -> PanelKey;
fn view_row(&self) -> Option<&'static str> {
None
}
fn resizable(&self) -> bool {
false
}
fn is_open(&self, hook: &EditorHook) -> bool;
fn toggle(&self, _hook: &mut EditorHook, _world: &mut World) {}
fn close(&self, hook: &mut EditorHook, world: &mut World);
fn size(&self, hook: &EditorHook) -> [f32; 2];
fn max_size(&self, _hook: &EditorHook) -> [f32; 2] {
[f32::INFINITY, f32::INFINITY]
}
fn default_origin(&self, vp: [f32; 2]) -> [f32; 2];
fn sprite_ids(&self) -> Vec<AssetId>;
fn label_ids(&self) -> Vec<AssetId>;
fn field_ids(&self) -> Vec<(AssetId, &'static str)> {
Vec::new()
}
fn overlay_ids(&self, _hook: &EditorHook) -> Vec<AssetId> {
Vec::new()
}
fn press(
&self,
hook: &mut EditorHook,
world: &mut World,
mx: f32,
my: f32,
o: [f32; 2],
) -> bool;
fn wheel_over(
&self,
_hook: &EditorHook,
_world: &World,
_mx: f32,
_my: f32,
_o: [f32; 2],
) -> bool {
false
}
fn scroll(&self, _hook: &mut EditorHook, _world: &mut World, _delta: f32) {}
fn frame_keys(&self, _hook: &mut EditorHook, _world: &mut World, _input: &FrameInput) {}
fn draw(&self, hook: &EditorHook, world: &mut World, o: [f32; 2], mouse: [f32; 2]);
fn hide(&self, world: &mut World);
}
static PANELS: [&dyn Panel; PANEL_COUNT] = [
&panels::AssetsPanel,
&panels::EditPanel,
&panels::PreviewPanel,
&panels::ViewPanel,
&panels::TemplatesPanel,
&panels::LightingPanel,
&panels::StoryPanel,
&panels::ImportPanel,
&panels::HealthPanel,
&panels::ConsolePanel,
&panels::BehaviorPanel,
&panels::MapPanel,
&panels::VariablesPanel,
&panels::ContentPanel,
&panels::CharacterShapePanel,
&panels::TemplateDetailPanel,
&panels::PalettePanel,
&panels::WorldsPanel,
];
pub(crate) fn panel(key: PanelKey) -> &'static dyn Panel {
PANELS[key.index()]
}
pub(crate) fn all() -> impl Iterator<Item = &'static dyn Panel> {
PANELS.into_iter()
}
pub(crate) fn view_toggles() -> impl Iterator<Item = &'static dyn Panel> {
PANELS.into_iter().filter(|p| p.view_row().is_some())
}
pub(crate) fn view_toggle_count() -> usize {
view_toggles().count()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn keys_match_registry() {
for key in PanelKey::ALL {
assert_eq!(panel(key).key(), key);
}
}
#[test]
fn id_families_are_disjoint() {
let mut seen: std::collections::HashMap<AssetId, String> = std::collections::HashMap::new();
let mut claim = |id: AssetId, who: String| {
if let Some(prev) = seen.insert(id, who.clone()) {
panic!("{who} and {prev} both claim {id:?}");
}
};
for id in crate::editor::hud::all_ids() {
claim(id, "top bar".to_string());
}
for id in crate::editor::viewport::highlight::all_sprite_ids() {
claim(id, "selection highlight".to_string());
}
claim(
crate::editor::viewport::marquee::RECT,
"marquee rect".to_string(),
);
for id in crate::editor::viewport::gizmo::all_sprite_ids() {
claim(id, "gizmo".to_string());
}
for id in crate::editor::viewport::billboards::all_sprite_ids() {
claim(id, "billboards".to_string());
}
for id in crate::editor::viewport::billboards::all_label_ids() {
claim(id, "billboard glyphs".to_string());
}
claim(
crate::editor::viewport::gizmo::MODE_LABEL,
"gizmo mode label".to_string(),
);
claim(
crate::editor::viewport::cursor::CURSOR,
"editor cursor".to_string(),
);
for id in crate::editor::create_menu::all_sprite_ids() {
claim(id, "create menu sprites".to_string());
}
for id in crate::editor::create_menu::all_label_ids() {
claim(id, "create menu labels".to_string());
}
for id in crate::editor::view_menu::all_sprite_ids() {
claim(id, "display menu sprites".to_string());
}
for id in crate::editor::view_menu::all_label_ids() {
claim(id, "display menu labels".to_string());
}
for id in crate::editor::toast_overlay::all_sprite_ids() {
claim(id, "toast sprites".to_string());
}
for id in crate::editor::toast_overlay::all_label_ids() {
claim(id, "toast labels".to_string());
}
for id in crate::editor::modal::all_sprite_ids() {
claim(id, "confirm dialog sprites".to_string());
}
for id in crate::editor::modal::all_label_ids() {
claim(id, "confirm dialog labels".to_string());
}
claim(
crate::editor::worlds::cinematic::FADE,
"start screen shot fade".to_string(),
);
for id in crate::editor::worlds::loading::all_sprite_ids() {
claim(id, "loading cover".to_string());
}
for id in crate::editor::worlds::loading::all_label_ids() {
claim(id, "loading caption".to_string());
}
for key in PanelKey::ALL {
let p = panel(key);
for id in p.sprite_ids() {
claim(id, format!("{key:?} sprites"));
}
for id in p.label_ids() {
claim(id, format!("{key:?} labels"));
}
for (id, _) in p.field_ids() {
claim(id, format!("{key:?} fields"));
}
}
}
#[test]
fn view_toggles_lists_the_toggleable_panels() {
let rows: Vec<&'static str> = view_toggles().map(|p| p.view_row().unwrap()).collect();
assert_eq!(
rows,
[
"Assets",
"Preview",
"Templates",
"Lighting",
"Story",
"Import",
"Health",
"Console",
"Behavior",
"Map",
"Variables",
"Content",
"Character Shape",
"Worlds"
]
);
}
}