// 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, ) { 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 = 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); }