use std::io::Write;
use crossterm::{
cursor, queue,
style::Color,
terminal::{Clear, ClearType},
};
use crate::app_state::AppState;
use crate::ui::constants::{ANIMATION_SPEED, BLOCK_SIZE_DIVISOR, BLOCK_SIZE_MAX, SCREEN_MARGIN};
use crate::ui::text::{display_width, print_colored_text, truncate_to_width};
pub fn print_loading_indicator<W: Write>(
stdout: &mut W,
cols: u16,
rows: u16,
frame_counter: u64,
startup_status_lines: &[String],
) {
let message = "Loading...";
let x = (cols.saturating_sub(message.len() as u16)) / 2;
let y = rows / 2;
queue!(stdout, cursor::MoveTo(x, y)).unwrap();
print_colored_text(stdout, message, Color::Yellow, None, None);
let bar_width = 40.min(cols as usize - SCREEN_MARGIN); let bar_x = (cols.saturating_sub(bar_width as u16)) / 2;
let bar_y = y + 2;
let position = ((frame_counter / ANIMATION_SPEED) % (bar_width as u64 * 2)) as usize;
let block_size = BLOCK_SIZE_MAX.min(bar_width / BLOCK_SIZE_DIVISOR); let actual_pos = if position < bar_width {
position
} else {
bar_width * 2 - position - 1
};
let block_start = actual_pos.min(bar_width.saturating_sub(block_size));
let block_end = (block_start + block_size).min(bar_width);
queue!(stdout, cursor::MoveTo(bar_x, bar_y)).unwrap();
for i in 0..bar_width {
if i >= block_start && i < block_end {
print_colored_text(stdout, "━", Color::Cyan, None, None);
} else {
print_colored_text(stdout, "─", Color::DarkGrey, None, None);
}
}
if !startup_status_lines.is_empty() {
let status_start_y = bar_y + 2;
let max_lines = ((rows - status_start_y) - 1).min(10) as usize; let lines_to_show = startup_status_lines.len().min(max_lines);
let start_idx = startup_status_lines.len().saturating_sub(lines_to_show);
let status_x = bar_x + 3;
for (i, status_line) in startup_status_lines[start_idx..].iter().enumerate() {
let status_y = status_start_y + i as u16;
queue!(stdout, cursor::MoveTo(status_x, status_y)).unwrap();
let color = if status_line.contains("✓") {
Color::DarkGreen
} else {
Color::DarkGrey
};
print_colored_text(stdout, status_line, color, None, None);
queue!(stdout, Clear(ClearType::UntilNewLine)).unwrap();
}
}
}
pub fn print_function_keys<W: Write>(
stdout: &mut W,
cols: u16,
rows: u16,
state: &AppState,
is_remote: bool,
) {
queue!(stdout, cursor::MoveTo(0, rows - 1)).unwrap();
let sort_indicator = match state.sort_criteria {
crate::app_state::SortCriteria::Default => "Sort:Default",
crate::app_state::SortCriteria::Pid => "Sort:PID",
crate::app_state::SortCriteria::User => "Sort:User",
crate::app_state::SortCriteria::Priority => "Sort:Priority",
crate::app_state::SortCriteria::Nice => "Sort:Nice",
crate::app_state::SortCriteria::VirtualMemory => "Sort:VIRT",
crate::app_state::SortCriteria::ResidentMemory => "Sort:RES",
crate::app_state::SortCriteria::State => "Sort:State",
crate::app_state::SortCriteria::CpuPercent => "Sort:CPU%",
crate::app_state::SortCriteria::MemoryPercent => "Sort:MEM%",
crate::app_state::SortCriteria::GpuPercent => "Sort:GPU%",
crate::app_state::SortCriteria::GpuMemoryUsage => "Sort:GPU-Mem",
crate::app_state::SortCriteria::CpuTime => "Sort:Time",
crate::app_state::SortCriteria::Command => "Sort:Command",
crate::app_state::SortCriteria::Utilization => "Sort:Util",
crate::app_state::SortCriteria::GpuMemory => "Sort:GPU-Mem",
crate::app_state::SortCriteria::Power => "Sort:Power",
crate::app_state::SortCriteria::Temperature => "Sort:Temp",
};
let filter_indicator = if state.gpu_filter_enabled {
"Filter:GPU"
} else {
""
};
let function_keys = if is_remote {
format!(
"h:Help q:Exit c:CPU Cores ←→:Tabs ↑↓:Scroll PgUp/PgDn:Page d:Default u:Util g:GPU-Mem [{sort_indicator}]"
)
} else {
if state.gpu_filter_enabled {
format!("h:Help q:Exit c:CPU Cores f:Filter ←→:Scroll ↑↓:Scroll p:PID m:Memory g:GPU-Mem [{sort_indicator}] [{filter_indicator}]")
} else {
format!("h:Help q:Exit c:CPU Cores f:Filter ←→:Scroll ↑↓:Scroll p:PID m:Memory g:GPU-Mem [{sort_indicator}]")
}
};
let truncated_keys = if display_width(&function_keys) > cols as usize {
truncate_to_width(&function_keys, cols as usize).into_owned()
} else {
function_keys
};
let notification_msg = state.notifications.get_current_message().unwrap_or("");
let notification_len = display_width(notification_msg);
let available_space = if notification_len > 0 {
cols.saturating_sub(notification_len as u16 + 1) } else {
cols
} as usize;
let final_function_keys = if display_width(&truncated_keys) > available_space {
truncate_to_width(&truncated_keys, available_space)
} else {
std::borrow::Cow::Borrowed(truncated_keys.as_str())
};
print_colored_text(stdout, &final_function_keys, Color::DarkGreen, None, None);
if notification_len > 0 {
print_colored_text(stdout, " ", Color::White, None, None);
let notification_color =
if notification_msg.contains("Error") || notification_msg.contains("Failed") {
Color::Red
} else if notification_msg.contains("Warning") {
Color::Yellow
} else {
Color::Cyan
};
print_colored_text(stdout, notification_msg, notification_color, None, None);
}
let used_space = display_width(&final_function_keys)
+ if notification_len > 0 {
notification_len + 1
} else {
0
};
let remaining_space = cols as usize - used_space.min(cols as usize);
if remaining_space > 0 {
print_colored_text(
stdout,
&" ".repeat(remaining_space),
Color::White,
None,
None,
);
}
}