nightshade-editor 0.14.2

Interactive map editor for the Nightshade game engine
use nightshade::prelude::*;

pub fn ui_capturing(world: &World) -> bool {
    let pointer = world
        .resources
        .retained_ui
        .interaction
        .hovered_entity
        .or(world.resources.retained_ui.interaction.active_entity);
    if let Some(entity) = pointer
        && pointer_is_inside_panel(world, entity)
    {
        return true;
    }

    let panel_being_manipulated = world
        .ui
        .query_entities(nightshade::ecs::world::UI_PANEL)
        .any(|entity| {
            world
                .ui
                .get_ui_panel(entity)
                .is_some_and(|panel| panel.resize_edge.is_some() || panel.drag_offset.is_some())
        });
    if panel_being_manipulated {
        return true;
    }

    world
        .ui
        .query_entities(nightshade::ecs::world::UI_TILE_CONTAINER)
        .any(|entity| {
            world.ui.get_ui_tile_container(entity).is_some_and(|data| {
                data.dragging_tab.is_some()
                    || data.pending_tab_drag.is_some()
                    || data.dragging_splitter.is_some()
            })
        })
}

fn pointer_is_inside_panel(world: &World, entity: Entity) -> bool {
    let mut current = entity;
    loop {
        if world.ui.get_ui_panel(current).is_some() {
            return true;
        }
        match world.core.get_parent(current).and_then(|parent| parent.0) {
            Some(parent) => current = parent,
            None => return false,
        }
    }
}

pub fn mouse_in_active_viewport(world: &World) -> bool {
    world
        .resources
        .window
        .active_viewport_rect
        .is_some_and(|rect| {
            rect.contains(nightshade::ecs::input::access::mouse_for_active(world).position)
        })
}

#[derive(Default)]
pub struct ShortcutState {
    pub save_was_pressed: bool,
    pub undo_was_pressed: bool,
    pub redo_was_pressed: bool,
    pub delete_was_pressed: bool,
    pub duplicate_was_pressed: bool,
    pub palette_was_pressed: bool,
    pub snap_floor_was_pressed: bool,
    pub stamp_decal_was_pressed: bool,
}

pub fn poll_shortcuts(
    state: &mut ShortcutState,
    world: &World,
    actions: &mut Vec<crate::systems::retained_ui::Action>,
) {
    let keyboard = &world.resources.input.keyboard;
    let ctrl = keyboard.is_key_pressed(KeyCode::ControlLeft)
        || keyboard.is_key_pressed(KeyCode::ControlRight);
    let shift =
        keyboard.is_key_pressed(KeyCode::ShiftLeft) || keyboard.is_key_pressed(KeyCode::ShiftRight);

    let save_pressed = ctrl && !shift && keyboard.is_key_pressed(KeyCode::KeyS);
    if save_pressed && !state.save_was_pressed {
        actions.push(crate::systems::retained_ui::Action::SaveProject);
    }
    state.save_was_pressed = save_pressed;

    let undo_pressed = ctrl && !shift && keyboard.is_key_pressed(KeyCode::KeyZ);
    if undo_pressed && !state.undo_was_pressed {
        actions.push(crate::systems::retained_ui::Action::Undo);
    }
    state.undo_was_pressed = undo_pressed;

    let redo_pressed = ctrl
        && (keyboard.is_key_pressed(KeyCode::KeyY)
            || (shift && keyboard.is_key_pressed(KeyCode::KeyZ)));
    if redo_pressed && !state.redo_was_pressed {
        actions.push(crate::systems::retained_ui::Action::Redo);
    }
    state.redo_was_pressed = redo_pressed;

    let text_input_active = world
        .resources
        .retained_ui
        .interaction
        .focused_entity
        .is_some_and(|entity| world.ui.get_ui_text_input(entity).is_some());

    let delete_pressed = !text_input_active
        && (keyboard.is_key_pressed(KeyCode::Delete)
            || keyboard.is_key_pressed(KeyCode::Backspace)
            || keyboard.is_key_pressed(KeyCode::KeyX));
    if delete_pressed && !state.delete_was_pressed {
        actions.push(crate::systems::retained_ui::Action::DeleteSelectedEntity);
    }
    state.delete_was_pressed = delete_pressed;

    let duplicate_pressed = keyboard.is_key_pressed(KeyCode::KeyD)
        && ((ctrl && !shift) || (!text_input_active && !ctrl && shift));
    if duplicate_pressed && !state.duplicate_was_pressed {
        actions.push(crate::systems::retained_ui::Action::DuplicateSelectedEntity);
    }
    state.duplicate_was_pressed = duplicate_pressed;

    let palette_pressed = ctrl
        && (keyboard.is_key_pressed(KeyCode::KeyK)
            || (shift && keyboard.is_key_pressed(KeyCode::KeyP)));
    if palette_pressed && !state.palette_was_pressed {
        actions.push(crate::systems::retained_ui::Action::OpenCommandPalette);
    }
    state.palette_was_pressed = palette_pressed;

    let snap_floor_pressed = !text_input_active && keyboard.is_key_pressed(KeyCode::End);
    if snap_floor_pressed && !state.snap_floor_was_pressed {
        actions.push(crate::systems::retained_ui::Action::SnapSelectionToFloor);
    }
    state.snap_floor_was_pressed = snap_floor_pressed;

    let stamp_decal_pressed =
        !text_input_active && !ctrl && shift && keyboard.is_key_pressed(KeyCode::KeyT);
    if stamp_decal_pressed && !state.stamp_decal_was_pressed {
        actions.push(crate::systems::retained_ui::Action::StampDecalAtCursor);
    }
    state.stamp_decal_was_pressed = stamp_decal_pressed;
}