zim-studio 1.5.0

A Terminal-Based Audio Project Scaffold and Metadata System
Documentation
use ratatui::{
    Frame,
    layout::{Alignment, Constraint, Direction, Layout, Rect},
    style::{Color, Modifier, Style},
    text::{Line, Span},
    widgets::{Block, Borders, Clear, List, ListItem, Paragraph},
};

use super::save_dialog::{SaveDialog, SaveDialogFocus};

pub fn draw_save_dialog(f: &mut Frame, area: Rect, dialog: &SaveDialog) {
    // Create a centered modal
    let modal_width = 60.min(area.width - 4);
    let modal_height = 20.min(area.height - 4);

    let modal_area = Rect {
        x: (area.width - modal_width) / 2,
        y: (area.height - modal_height) / 2,
        width: modal_width,
        height: modal_height,
    };

    // Clear the background
    f.render_widget(Clear, modal_area);

    // Draw the modal border
    let block = Block::default()
        .borders(Borders::ALL)
        .border_style(Style::default().fg(Color::White))
        .title(if dialog.has_selection {
            " Save Selection As "
        } else {
            " Save As "
        })
        .title_style(
            Style::default()
                .fg(Color::Cyan)
                .add_modifier(Modifier::BOLD),
        );
    f.render_widget(block, modal_area);

    let inner_area = modal_area.inner(ratatui::layout::Margin {
        horizontal: 1,
        vertical: 1,
    });

    // Create layout
    let chunks = Layout::default()
        .direction(Direction::Vertical)
        .constraints([
            Constraint::Length(2), // Current path
            Constraint::Min(5),    // Directory list
            Constraint::Length(3), // Filename field
            Constraint::Length(2), // Controls
        ])
        .split(inner_area);

    // Current path
    let path_display = format!("📁 {}", dialog.current_path.display());
    let path_widget = Paragraph::new(path_display)
        .style(Style::default().fg(Color::Blue))
        .block(Block::default().borders(Borders::BOTTOM));
    f.render_widget(path_widget, chunks[0]);

    // Directory list
    let dirs: Vec<ListItem> = dialog
        .directories
        .iter()
        .map(|dir| {
            let prefix = if dir == ".." { "" } else { "📁 " };
            ListItem::new(format!("{prefix}{dir}"))
        })
        .collect();

    let dirs_list = List::new(dirs)
        .block(Block::default().borders(Borders::NONE))
        .highlight_style(if dialog.focus == SaveDialogFocus::DirectoryList {
            Style::default()
                .bg(Color::DarkGray)
                .fg(Color::White)
                .add_modifier(Modifier::BOLD)
        } else {
            Style::default().bg(Color::Gray).fg(Color::Black)
        });

    // Use render_stateful_widget to enable scrolling
    let mut list_state = dialog.list_state;
    f.render_stateful_widget(dirs_list, chunks[1], &mut list_state);

    // Filename field
    let filename_block = Block::default()
        .borders(Borders::ALL)
        .border_style(if dialog.focus == SaveDialogFocus::FilenameField {
            Style::default().fg(Color::Cyan)
        } else {
            Style::default().fg(Color::DarkGray)
        })
        .title(" Filename ");

    let filename_widget = Paragraph::new(dialog.filename.as_str())
        .style(Style::default().fg(Color::White))
        .block(filename_block);
    f.render_widget(filename_widget, chunks[2]);

    // Show cursor in filename field if focused
    if dialog.focus == SaveDialogFocus::FilenameField {
        let cursor_x = chunks[2].x + 1 + dialog.filename.len() as u16;
        let cursor_y = chunks[2].y + 1;
        if cursor_x < chunks[2].x + chunks[2].width - 1 {
            f.set_cursor_position((cursor_x, cursor_y));
        }
    }

    // Controls
    let controls = vec![
        Span::styled("[Tab]", Style::default().fg(Color::Yellow)),
        Span::raw(" switch  "),
        Span::styled("[Enter]", Style::default().fg(Color::Green)),
        Span::raw(" save  "),
        Span::styled("[Esc]", Style::default().fg(Color::Red)),
        Span::raw(" cancel"),
    ];
    let controls_widget = Paragraph::new(Line::from(controls)).alignment(Alignment::Center);
    f.render_widget(controls_widget, chunks[3]);
}