nightshade-editor 0.14.0

Interactive map editor for the Nightshade game engine
use crate::ecs::{EditorMode, EditorWorld};
use nightshade::prelude::*;

#[derive(Default, Clone)]
pub struct StatusHandles {
    pub fps_label: Entity,
    pub entity_count_label: Entity,
    pub mode_label: Entity,
    pub model_label: Entity,
    pub error_label: Entity,
}

pub fn build(tree: &mut UiTreeBuilder) -> StatusHandles {
    let panel = tree.add_docked_panel_bottom("status_bar", "", 24.0);
    ui_panel_set_header_visible(tree.world_mut(), panel, false);
    if let Some(data) = tree.world_mut().ui.get_ui_panel_mut(panel) {
        data.min_size = vec2(0.0, 24.0);
        data.resizable = false;
    }

    let theme = tree
        .world_mut()
        .resources
        .retained_ui
        .theme_state
        .active_theme();
    let font = theme.font_size * 0.85;

    let content = super::panel_content(tree, panel);
    if let Some(node) = tree.world_mut().ui.get_ui_layout_node_mut(content) {
        node.flow_layout = Some(FlowLayout {
            direction: FlowDirection::Horizontal,
            padding: 6.0,
            spacing: 12.0,
            alignment: FlowAlignment::Start,
            cross_alignment: FlowAlignment::Center,
            wrap: false,
        });
    }
    let dim = vec4(0.65, 0.65, 0.7, 1.0);
    let bright = vec4(1.0, 0.6, 0.2, 1.0);
    let red = vec4(1.0, 0.4, 0.4, 1.0);

    let mut fps_label = Entity::default();
    let mut entity_count_label = Entity::default();
    let mut mode_label = Entity::default();
    let mut error_label = Entity::default();
    let mut model_label = Entity::default();

    tree.in_parent(content, |tree| {
        fps_label = tree
            .add_node()
            .size((80.0).px(), (18.0).px())
            .with_text("0 fps", font)
            .text_left()
            .color_raw::<UiBase>(dim)
            .entity();

        entity_count_label = tree
            .add_node()
            .size((120.0).px(), (18.0).px())
            .with_text("0 entities", font)
            .text_left()
            .color_raw::<UiBase>(dim)
            .entity();

        mode_label = tree
            .add_node()
            .size((180.0).px(), (18.0).px())
            .with_text("OBJECT", font)
            .text_left()
            .color_raw::<UiBase>(bright)
            .entity();

        tree.add_node()
            .size((60.0).px(), (18.0).px())
            .with_text(&format!("v{}", env!("CARGO_PKG_VERSION")), font)
            .text_left()
            .color_raw::<UiBase>(bright)
            .entity();

        let spacer = tree.add_node().size((0.0).px(), (18.0).px()).entity();
        if let Some(node) = tree.world_mut().ui.get_ui_layout_node_mut(spacer) {
            node.flex_grow = Some(1.0);
        }

        error_label = tree
            .add_node()
            .size((280.0).px(), (18.0).px())
            .with_text("", font)
            .text_right()
            .color_raw::<UiBase>(red)
            .entity();

        model_label = tree
            .add_node()
            .size((220.0).px(), (18.0).px())
            .with_text("(no model loaded)", font)
            .text_right()
            .color_raw::<UiBase>(dim)
            .entity();
    });

    StatusHandles {
        fps_label,
        entity_count_label,
        mode_label,
        model_label,
        error_label,
    }
}

pub fn update(editor_world: &EditorWorld, world: &mut World) {
    let handles = &editor_world.resources.ui_handles;
    let fps = world.resources.window.timing.frames_per_second;
    ui_set_text(world, handles.status.fps_label, &format!("{:.0} fps", fps));

    let entity_count = world
        .core
        .query_entities(
            nightshade::ecs::world::LOCAL_TRANSFORM | nightshade::ecs::world::GLOBAL_TRANSFORM,
        )
        .count();
    ui_set_text(
        world,
        handles.status.entity_count_label,
        &format!("{} entities", entity_count),
    );

    let mode_text = match editor_world.resources.mode.mode {
        EditorMode::Object => "OBJECT".to_string(),
        EditorMode::Edit { target } => {
            let name = world
                .core
                .get_name(target)
                .map(|name| name.0.clone())
                .unwrap_or_else(|| format!("entity {}", target.id));
            format!("EDIT  {}", name)
        }
    };
    ui_set_text(world, handles.status.mode_label, &mode_text);

    let model_text = editor_world
        .resources
        .loading
        .current_model_name
        .as_deref()
        .unwrap_or("(no model loaded)");
    ui_set_text(world, handles.status.model_label, model_text);

    let error_text = editor_world
        .resources
        .loading
        .last_dropped_error
        .as_deref()
        .unwrap_or("");
    ui_set_text(world, handles.status.error_label, error_text);
}