use ratatui::prelude::Rect;
use crate::state::{AppState, Focus};
use crate::theme::KeyChord;
use std::fmt::Write;
pub fn calculate_footer_height(app: &AppState, bottom_container: Rect) -> u16 {
let baseline_lines: u16 = 2;
let km = &app.keymap;
let footer_w: u16 = bottom_container.width.saturating_sub(2);
let nm_rows: u16 = if matches!(app.focus, Focus::Search) && app.search_normal_mode {
let toggle_label = km
.search_normal_toggle
.first()
.map_or_else(|| "Esc".to_string(), KeyChord::label);
let insert_label = km
.search_normal_insert
.first()
.map_or_else(|| "i".to_string(), KeyChord::label);
let left_label = km
.search_normal_select_left
.first()
.map_or_else(|| "h".to_string(), KeyChord::label);
let right_label = km
.search_normal_select_right
.first()
.map_or_else(|| "l".to_string(), KeyChord::label);
let delete_label = km
.search_normal_delete
.first()
.map_or_else(|| "d".to_string(), KeyChord::label);
let clear_label = km
.search_normal_clear
.first()
.map_or_else(|| "Shift+Del".to_string(), KeyChord::label);
let line1 = format!(
"Normal Mode (Focused Search Window): [{toggle_label}/{insert_label}] Insert Mode, [j / k] move, [Ctrl+d / Ctrl+u] page, [{left_label} / {right_label}] Select text, [{delete_label}] Delete text, [{clear_label}] Clear input"
);
let mut line2 = String::new();
if !km.config_menu_toggle.is_empty()
|| !km.options_menu_toggle.is_empty()
|| !km.panels_menu_toggle.is_empty()
|| (!app.installed_only_mode
&& (!km.search_normal_import.is_empty() || !km.search_normal_export.is_empty()))
{
if !km.config_menu_toggle.is_empty()
|| !km.options_menu_toggle.is_empty()
|| !km.panels_menu_toggle.is_empty()
{
line2.push_str(" • Open Menus: ");
if let Some(k) = km.config_menu_toggle.first() {
let _ = write!(line2, "[{}] Config", k.label());
}
if let Some(k) = km.options_menu_toggle.first() {
if !line2.ends_with("menus: ") {
line2.push_str(", ");
}
let _ = write!(line2, "[{}] Options", k.label());
}
if let Some(k) = km.panels_menu_toggle.first() {
if !line2.ends_with("menus: ") {
line2.push_str(", ");
}
let _ = write!(line2, "[{}] Panels", k.label());
}
}
if !app.installed_only_mode
&& (!km.search_normal_import.is_empty() || !km.search_normal_export.is_empty())
{
line2.push_str(" • ");
if let Some(k) = km.search_normal_import.first() {
let _ = write!(line2, "[{}] Import", k.label());
if let Some(k2) = km.search_normal_export.first() {
let _ = write!(line2, ", [{}] Export", k2.label());
}
} else if let Some(k) = km.search_normal_export.first() {
let _ = write!(line2, "[{}] Export", k.label());
}
}
}
let w = if footer_w == 0 { 1 } else { footer_w };
let rows1 = (u16::try_from(line1.len()).unwrap_or(u16::MAX).div_ceil(w)).max(1);
let rows2 = if line2.is_empty() {
0
} else {
(u16::try_from(line2.len()).unwrap_or(u16::MAX).div_ceil(w)).max(1)
};
rows1 + rows2
} else {
0
};
let base_help_h: u16 = if app.show_keybinds_footer {
baseline_lines
} else {
0
};
base_help_h.saturating_add(nm_rows).saturating_add(2)
}
pub fn calculate_layout_areas(
app: &AppState,
bottom_container: Rect,
footer_height: u16,
) -> (Rect, Rect, Option<Rect>, Option<Rect>, bool) {
use ratatui::layout::{Constraint, Direction, Layout};
const MIN_PACKAGE_INFO_H: u16 = 3;
let show_keybinds =
app.show_keybinds_footer && bottom_container.height >= MIN_PACKAGE_INFO_H + footer_height;
let help_h: u16 = if show_keybinds { footer_height } else { 0 };
let content_container = Rect {
x: bottom_container.x,
y: bottom_container.y,
width: bottom_container.width,
height: bottom_container.height.saturating_sub(help_h),
};
let (details_area, pkgb_area_opt, comments_area_opt) =
match (app.pkgb_visible, app.comments_visible) {
(true, true) => {
let split = Layout::default()
.direction(Direction::Horizontal)
.constraints([
Constraint::Percentage(50),
Constraint::Percentage(25),
Constraint::Percentage(25),
])
.split(content_container);
(split[0], Some(split[1]), Some(split[2]))
}
(true, false) => {
let split = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
.split(content_container);
(split[0], Some(split[1]), None)
}
(false, true) => {
let split = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
.split(content_container);
(split[0], None, Some(split[1]))
}
(false, false) => {
(content_container, None, None)
}
};
(
content_container,
details_area,
pkgb_area_opt,
comments_area_opt,
show_keybinds,
)
}