rmcl 0.3.1

A fully featured Minecraft TUI launcher
// settings panel: shows instance config (memory, java, jvm args, resolution)
// and provides keybinds to open config files in $EDITOR

use ratatui::{
    Frame,
    layout::Rect,
    style::{Modifier, Style},
    text::{Line, Span},
    widgets::{Block, Borders, Paragraph},
};

use crate::config::{SETTINGS, theme::{BORDER_STYLE, THEME}};
use crate::instance::models::InstanceConfig;
use crate::tui::app::FocusedArea;

use super::styled_title;

pub fn render(
    frame: &mut Frame,
    area: Rect,
    focused: FocusedArea,
    instance: Option<&InstanceConfig>,
    _instances_dir: &std::path::Path,
) {
    let theme = THEME.as_ref();
    let color = if focused == FocusedArea::Settings {
        theme.accent()
    } else {
        theme.border()
    };

    let mut block = Block::default()
        .title(styled_title("Settings", true))
        .borders(Borders::ALL)
        .border_type(BORDER_STYLE.to_border_type())
        .border_style(Style::default().fg(color));

    if focused == FocusedArea::Settings {
        let lines = super::popups::keybind_lines_wrapped(
            &[
                ("e", " edit instance"),
                ("g", " edit global"),
                ("d", " desktop"),
            ],
            area.width.saturating_sub(2),
        );
        for line in lines {
            block = block.title_bottom(line);
        }
    }

    let inner = block.inner(area);
    frame.render_widget(block, area);

    let Some(inst) = instance else {
        frame.render_widget(
            Paragraph::new("No instance selected.").style(Style::default().fg(theme.text_dim())),
            inner,
        );
        return;
    };

    let label_style = Style::default().fg(theme.text_dim());
    let value_style = Style::default()
        .fg(theme.text())
        .add_modifier(Modifier::BOLD);
    let dim_style = Style::default().fg(theme.text_dim());

    let memory_min = inst.memory_min.as_deref().unwrap_or(&SETTINGS.defaults.memory_min);
    let memory_max = inst.memory_max.as_deref().unwrap_or(&SETTINGS.defaults.memory_max);
    let java_path = inst
        .java_path
        .as_deref()
        .or_else(|| SETTINGS.paths.effective_java_path())
        .unwrap_or("system");

    let jvm_args = if inst.jvm_args.is_empty() {
        "none".to_string()
    } else {
        inst.jvm_args.join(" ")
    };
    let resolution = inst
        .resolution
        .map(|(w, h)| format!("{}x{}", w, h))
        .unwrap_or_else(|| "default".to_string());

    let lines = vec![
        Line::from(vec![
            Span::styled("Memory  ", label_style),
            Span::styled(format!("{memory_min} - {memory_max}"), value_style),
        ]),
        Line::from(vec![
            Span::styled("Java    ", label_style),
            Span::styled(java_path, value_style),
        ]),
        Line::from(vec![
            Span::styled("JVM     ", label_style),
            Span::styled(jvm_args, dim_style),
        ]),
        Line::from(vec![
            Span::styled("Display ", label_style),
            Span::styled(resolution, value_style),
        ]),
    ];

    frame.render_widget(Paragraph::new(lines), inner);
}

pub enum SettingsAction {
    None,
    EditInstance(std::path::PathBuf),
    EditGlobal(std::path::PathBuf),
    ToggleDesktop,
}

pub fn handle_key(
    key_event: &crossterm::event::KeyEvent,
    instance: Option<&InstanceConfig>,
    instances_dir: &std::path::Path,
) -> SettingsAction {
    match key_event.code {
        crossterm::event::KeyCode::Char('e') => {
            if let Some(inst) = instance {
                let path = instances_dir.join(&inst.name).join("instance.json");
                SettingsAction::EditInstance(path)
            } else {
                SettingsAction::None
            }
        }
        crossterm::event::KeyCode::Char('g') => {
            let path = crate::config::get_config_path().join("config.toml");
            SettingsAction::EditGlobal(path)
        }
        crossterm::event::KeyCode::Char('d') => SettingsAction::ToggleDesktop,
        _ => SettingsAction::None,
    }
}