nightshade 0.13.3

A cross-platform data-oriented game engine.
Documentation
use crate::ecs::ui::types::Rect;
use crate::ecs::world::World;
use crate::render::wgpu::passes::geometry::UiLayer;
use nalgebra_glm::{Vec2, Vec4};

use super::ShellState;

pub fn shell_retained_ui<C>(shell: &mut ShellState<C>, world: &mut World) {
    if !shell.should_render() {
        return;
    }

    let screen_size = world
        .resources
        .window
        .handle
        .as_ref()
        .map(|handle| {
            let size = handle.inner_size();
            Vec2::new(size.width as f32, size.height as f32)
        })
        .unwrap_or(Vec2::new(1920.0, 1080.0));
    let screen_width = screen_size.x;
    let screen_height = screen_size.y;

    let mouse_pos = Vec2::new(
        world.resources.input.mouse.position.x,
        world.resources.input.mouse.position.y,
    );
    let mouse_down = world
        .resources
        .input
        .mouse
        .state
        .contains(crate::ecs::world::resources::MouseState::LEFT_CLICKED);
    let mouse_just_pressed = world
        .resources
        .input
        .mouse
        .state
        .contains(crate::ecs::world::resources::MouseState::LEFT_JUST_PRESSED);

    let min_height = 100.0;
    let max_height = screen_height * 0.9;
    shell.height = shell.height.clamp(min_height, max_height);
    let shell_height = shell.height;
    let current_height = shell_height * shell.animation_progress;

    let resize_handle_height = 8.0;
    let resize_area = Rect::new(
        0.0,
        current_height - resize_handle_height,
        screen_width,
        resize_handle_height * 2.0,
    );
    let mouse_in_resize = resize_area.contains(mouse_pos) && shell.animation_progress > 0.95;

    if mouse_in_resize && mouse_just_pressed && !shell.dragging_resize {
        shell.dragging_resize = true;
        shell.drag_start_y = mouse_pos.y;
        shell.drag_start_height = shell.height;
    }

    if shell.dragging_resize {
        if mouse_down {
            let delta = mouse_pos.y - shell.drag_start_y;
            shell.height = (shell.drag_start_height + delta).clamp(min_height, max_height);
        } else {
            shell.dragging_resize = false;
        }
    }

    let bg_color = Vec4::new(0.06, 0.06, 0.08, 0.95);
    let header_color = Vec4::new(0.39, 0.78, 0.39, 1.0);
    let hint_color = Vec4::new(0.47, 0.47, 0.47, 1.0);
    let command_color = Vec4::new(0.59, 0.78, 1.0, 1.0);
    let output_color = Vec4::new(0.78, 0.78, 0.78, 1.0);
    let border_color = Vec4::new(0.4, 0.4, 0.45, 1.0);
    let transparent = Vec4::new(0.0, 0.0, 0.0, 0.0);

    let panel_clip = Rect::new(0.0, 0.0, screen_width, current_height);

    let ui = &mut world.resources.retained_ui;

    ui.draw_overlay_rect(crate::render::wgpu::passes::geometry::UiRect {
        position: Vec2::new(0.0, 0.0),
        size: Vec2::new(screen_width, current_height),
        color: bg_color,
        corner_radius: 0.0,
        border_width: 0.0,
        border_color: transparent,
        rotation: 0.0,
        clip_rect: Some(panel_clip),
        layer: UiLayer::Tooltips,
        z_index: 0,
    });

    let padding = 10.0;
    let font_size = 14.0;
    let line_height = font_size + 6.0;
    let header_height = 24.0;
    let input_height = 28.0;
    let separator_height = 1.0;

    let header_y = 4.0;
    ui.draw_overlay_text(
        "Console",
        Vec2::new(padding, header_y),
        crate::ecs::text::components::TextProperties {
            font_size,
            color: header_color,
            ..Default::default()
        },
        Some(panel_clip),
        UiLayer::Tooltips,
        1,
    );

    let hint_text = "Alt+C to close";
    let hint_width = hint_text.len() as f32 * font_size * 0.5;
    ui.draw_overlay_text(
        hint_text,
        Vec2::new(screen_width - hint_width - padding, header_y + 2.0),
        crate::ecs::text::components::TextProperties {
            font_size: font_size * 0.85,
            color: hint_color,
            ..Default::default()
        },
        Some(panel_clip),
        UiLayer::Tooltips,
        1,
    );

    let top_separator_y = header_height;
    ui.draw_overlay_rect(crate::render::wgpu::passes::geometry::UiRect {
        position: Vec2::new(0.0, top_separator_y),
        size: Vec2::new(screen_width, separator_height),
        color: border_color,
        corner_radius: 0.0,
        border_width: 0.0,
        border_color: transparent,
        rotation: 0.0,
        clip_rect: Some(panel_clip),
        layer: UiLayer::Tooltips,
        z_index: 1,
    });

    let bottom_separator_y = current_height - input_height - separator_height;
    ui.draw_overlay_rect(crate::render::wgpu::passes::geometry::UiRect {
        position: Vec2::new(0.0, bottom_separator_y),
        size: Vec2::new(screen_width, separator_height),
        color: border_color,
        corner_radius: 0.0,
        border_width: 0.0,
        border_color: transparent,
        rotation: 0.0,
        clip_rect: Some(panel_clip),
        layer: UiLayer::Tooltips,
        z_index: 1,
    });

    let output_top = top_separator_y + separator_height + 2.0;
    let output_bottom = bottom_separator_y - 2.0;
    let output_height = (output_bottom - output_top).max(0.0);
    let output_clip = Rect::new(0.0, output_top, screen_width, output_height);

    let mut total_content_height = 0.0;
    for line in &shell.output {
        let line_count = line.text.lines().count().max(1);
        total_content_height += line_count as f32 * line_height;
    }

    if shell.scroll_to_bottom {
        shell.scroll_offset = (total_content_height - output_height).max(0.0);
        shell.scroll_to_bottom = false;
    }

    shell.scroll_offset = shell
        .scroll_offset
        .clamp(0.0, (total_content_height - output_height).max(0.0));

    if output_height > 0.0 {
        let mut y_pos = output_top - shell.scroll_offset;
        for line in &shell.output {
            let color = if line.is_command {
                command_color
            } else {
                output_color
            };

            for text_line in line.text.lines() {
                if y_pos + line_height > output_top && y_pos < output_bottom {
                    ui.draw_overlay_text(
                        text_line,
                        Vec2::new(padding, y_pos),
                        crate::ecs::text::components::TextProperties {
                            font_size,
                            color,
                            ..Default::default()
                        },
                        Some(output_clip),
                        UiLayer::Tooltips,
                        2,
                    );
                }
                y_pos += line_height;
            }
        }
    }

    let input_y = current_height - input_height + (input_height - font_size) * 0.5;

    ui.draw_overlay_text(
        ">",
        Vec2::new(padding, input_y),
        crate::ecs::text::components::TextProperties {
            font_size,
            color: header_color,
            ..Default::default()
        },
        Some(panel_clip),
        UiLayer::Tooltips,
        2,
    );

    let cursor = if shell.visible && shell.animation_progress > 0.9 {
        "_"
    } else {
        ""
    };
    let input_display = format!("{}{}", shell.input_buffer, cursor);
    ui.draw_overlay_text(
        &input_display,
        Vec2::new(padding + font_size + 4.0, input_y),
        crate::ecs::text::components::TextProperties {
            font_size,
            color: output_color,
            ..Default::default()
        },
        Some(panel_clip),
        UiLayer::Tooltips,
        2,
    );

    ui.draw_overlay_rect(crate::render::wgpu::passes::geometry::UiRect {
        position: Vec2::new(0.0, current_height - 2.0),
        size: Vec2::new(screen_width, 2.0),
        color: border_color,
        corner_radius: 0.0,
        border_width: 0.0,
        border_color: transparent,
        rotation: 0.0,
        clip_rect: Some(panel_clip),
        layer: UiLayer::Tooltips,
        z_index: 3,
    });

    if shell.animation_progress > 0.95 {
        let handle_color = if shell.dragging_resize || mouse_in_resize {
            Vec4::new(0.6, 0.8, 1.0, 1.0)
        } else {
            Vec4::new(0.5, 0.5, 0.55, 0.8)
        };
        let handle_width = 40.0;
        let handle_height_px = 3.0;
        ui.draw_overlay_rect(crate::render::wgpu::passes::geometry::UiRect {
            position: Vec2::new((screen_width - handle_width) * 0.5, current_height + 4.0),
            size: Vec2::new(handle_width, handle_height_px),
            color: handle_color,
            corner_radius: 1.5,
            border_width: 0.0,
            border_color: transparent,
            rotation: 0.0,
            clip_rect: None,
            layer: UiLayer::Tooltips,
            z_index: 10,
        });
    }

    if shell.visible && shell.animation_progress > 0.9 {
        if shell.pending_enter {
            shell.pending_enter = false;
            shell.execute_command(world);
        }

        if shell.pending_up {
            shell.pending_up = false;
            shell.history_up();
        }

        if shell.pending_down {
            shell.pending_down = false;
            shell.history_down();
        }

        if shell.pending_escape {
            shell.pending_escape = false;
            shell.visible = false;
        }
    }
}