Files
komp_ac/client/src/components/common/autocomplete.rs
2025-04-12 17:34:17 +02:00

87 lines
3.2 KiB
Rust

// src/components/common/autocomplete.rs
use crate::config::colors::themes::Theme;
use ratatui::{
layout::Rect,
style::{Color, Modifier, Style},
widgets::{Block, List, ListItem, ListState},
Frame,
};
/// Renders an opaque dropdown list for autocomplete suggestions.
pub fn render_autocomplete_dropdown(
f: &mut Frame,
input_rect: Rect,
frame_area: Rect,
theme: &Theme,
suggestions: &[String],
selected_index: Option<usize>,
) {
if suggestions.is_empty() {
return;
}
// --- Calculate Dropdown Size & Position ---
let max_suggestion_width = suggestions.iter().map(|s| s.len()).max().unwrap_or(0) as u16;
// Ensure dropdown is at least as wide as the input field, or the longest suggestion, min 10
let dropdown_width = max_suggestion_width.max(input_rect.width).max(10);
let dropdown_height = (suggestions.len() as u16).min(5); // Max 5 suggestions visible
let mut dropdown_area = Rect {
x: input_rect.x, // Align horizontally with input
y: input_rect.y + 1, // Position directly below input
width: dropdown_width,
height: dropdown_height,
};
// --- Clamping Logic (prevent rendering off-screen) ---
// Clamp vertically (if it goes below the frame)
if dropdown_area.bottom() > frame_area.height {
dropdown_area.y = input_rect.y.saturating_sub(dropdown_height); // Try rendering above
}
// Clamp horizontally (if it goes past the right edge)
if dropdown_area.right() > frame_area.width {
dropdown_area.x = frame_area.width.saturating_sub(dropdown_width);
}
// Ensure x is not negative (if clamping pushes it left)
dropdown_area.x = dropdown_area.x.max(0);
// Ensure y is not negative (if clamping pushes it up)
dropdown_area.y = dropdown_area.y.max(0);
// --- End Clamping ---
// Render a solid background block first to ensure opacity
let background_block = Block::default().style(Style::default().bg(Color::DarkGray));
f.render_widget(background_block, dropdown_area);
// Create list items, ensuring each has a defined background
let items: Vec<ListItem> = suggestions
.iter()
.enumerate()
.map(|(i, s)| {
let is_selected = selected_index == Some(i);
ListItem::new(s.as_str()).style(if is_selected {
// Style for selected item (highlight background)
Style::default()
.fg(theme.bg) // Text color on highlight
.bg(theme.highlight) // Highlight background
.add_modifier(Modifier::BOLD)
} else {
// Style for non-selected items (matching background block)
Style::default()
.fg(theme.fg) // Text color on gray
.bg(Color::DarkGray) // Explicit gray background
})
})
.collect();
// Create the list widget (without its own block)
let list = List::new(items);
// State for managing selection highlight (still needed for logic)
let mut list_state = ListState::default();
list_state.select(selected_index);
// Render the list statefully *over* the background block
f.render_stateful_widget(list, dropdown_area, &mut list_state);
}