use crate::{
app::App,
theme::{self, Theme},
tui::{
components::{
command_bar, footer, leaderboard::LeaderboardOverlay, modal_dialog::ModalDialog,
mode_bar, notifications::NotificationComponent, pickers::Picker, results::Results,
size_warning, title, typing_area,
},
layout::{
AppLayout, LayoutBuilder, ResultsLayout, create_main_layout, create_results_layout,
},
},
};
use anyhow::Result;
use ratatui::{Frame, layout::Rect, style::Style, widgets::Block};
pub fn draw_ui(frame: &mut Frame, app: &mut App) -> Result<()> {
let area = frame.area();
let theme = theme::current_theme();
let bg_block = Block::default().style(Style::default().bg(theme.bg()));
frame.render_widget(bg_block, area);
if LayoutBuilder::is_too_smol(area) {
if area.height >= 2 && area.width >= 1 {
let (warning, warning_width) =
size_warning::create_size_warning_element(&theme, area.height, area.width);
let warning_height = 2;
let clamped_width = warning_width.min(area.width);
let x = ((area.width as i32 - clamped_width as i32).max(0) / 2) as u16;
let y = ((area.height as i32 - warning_height as i32).max(0) / 2) as u16;
let width = clamped_width;
let height = warning_height;
let warning_rect = Rect::new(x, y, width, height);
frame.render_widget(warning, warning_rect);
}
return Ok(());
}
match app.tracker.is_complete() {
true => render_results_screen(frame, app, &theme, create_results_layout(area)),
false => {
let layout = create_main_layout(area);
match app.tracker.is_idle() {
true => render_idle_screen(frame, app, &theme, layout),
false => render_typing_screen(frame, app, &theme, layout),
}
}
}
try_render_overlays(frame, app, &theme, area);
Ok(())
}
fn render_idle_screen(frame: &mut Frame, app: &mut App, theme: &Theme, layout: AppLayout) {
if let Some(rect) = layout.title_area {
let title = title::create_title(app, theme);
frame.render_widget(title, rect);
}
if let Some(rect) = layout.mode_bar_area {
let mode_line = mode_bar::create_mode_line(app, theme, rect.height, rect.width);
frame.render_widget(mode_line, rect);
}
typing_area::render_typing_area(frame, app, theme, &layout);
if layout.show_command_bar {
let commands_area = command_bar::create_command_area(theme);
frame.render_widget(commands_area, layout.command_area);
}
if layout.show_footer {
let footer_element = footer::create_footer_element(theme);
frame.render_widget(footer_element, layout.footer_area);
}
}
fn render_typing_screen(frame: &mut Frame, app: &mut App, theme: &Theme, layout: AppLayout) {
if let Some(rect) = layout.title_area {
let title = title::create_title(app, theme);
frame.render_widget(title, rect);
}
typing_area::render_typing_area(frame, app, theme, &layout);
}
fn render_results_screen(frame: &mut Frame, app: &mut App, theme: &Theme, layout: ResultsLayout) {
Results::render(frame, app, theme, layout);
}
fn try_render_overlays(frame: &mut Frame, app: &mut App, theme: &Theme, area: Rect) {
let leaderboard_is_open = app.leaderboard.is_some();
if let Some(ref modal) = app.modal {
ModalDialog::render(frame, modal, theme, area);
return;
}
if leaderboard_is_open {
LeaderboardOverlay::render(frame, app, theme, area);
return;
}
if !leaderboard_is_open && app.menu.is_open() {
Picker::render(frame, app, theme, area);
}
if !app.config.should_hide_notifications() {
NotificationComponent::render(frame, theme, area);
}
}