// src/components/common/find_file_palette.rs use crate::config::colors::themes::Theme; use crate::modes::general::command_navigation::NavigationState; // Corrected path use ratatui::{ layout::{Constraint, Direction, Layout, Rect}, style::Style, widgets::{Block, List, ListItem, Paragraph}, Frame, }; use unicode_width::UnicodeWidthStr; const PALETTE_MAX_VISIBLE_OPTIONS: usize = 15; const PADDING_CHAR: &str = " "; pub fn render_find_file_palette( f: &mut Frame, area: Rect, theme: &Theme, navigation_state: &NavigationState, ) { let palette_display_input = navigation_state.get_display_input(); // Use the new method let num_total_filtered = navigation_state.filtered_options.len(); let current_selected_list_idx = navigation_state.selected_index; let mut display_start_offset = 0; if num_total_filtered > PALETTE_MAX_VISIBLE_OPTIONS { if let Some(sel_idx) = current_selected_list_idx { if sel_idx >= display_start_offset + PALETTE_MAX_VISIBLE_OPTIONS { display_start_offset = sel_idx - PALETTE_MAX_VISIBLE_OPTIONS + 1; } else if sel_idx < display_start_offset { display_start_offset = sel_idx; } display_start_offset = display_start_offset .min(num_total_filtered.saturating_sub(PALETTE_MAX_VISIBLE_OPTIONS)); } } display_start_offset = display_start_offset.max(0); let display_end_offset = (display_start_offset + PALETTE_MAX_VISIBLE_OPTIONS) .min(num_total_filtered); // navigation_state.filtered_options is Vec<(usize, String)> // We only need the String part for display. let visible_options_slice: Vec<&String> = if num_total_filtered > 0 { navigation_state.filtered_options [display_start_offset..display_end_offset] .iter() .map(|(_, opt_str)| opt_str) .collect() } else { Vec::new() }; let chunks = Layout::default() .direction(Direction::Vertical) .constraints([ Constraint::Length(1), // For palette input line Constraint::Min(0), // For options list, take remaining space ]) .split(area); // Ensure list_area height does not exceed PALETTE_MAX_VISIBLE_OPTIONS let list_area_height = std::cmp::min(chunks[1].height, PALETTE_MAX_VISIBLE_OPTIONS as u16); let final_list_area = Rect::new(chunks[1].x, chunks[1].y, chunks[1].width, list_area_height); let input_area = chunks[0]; // let list_area = chunks[1]; // Use final_list_area let prompt_prefix = match navigation_state.navigation_type { crate::modes::general::command_navigation::NavigationType::FindFile => "Find File: ", crate::modes::general::command_navigation::NavigationType::TableTree => "Table Path: ", }; let base_prompt_text = format!("{}{}", prompt_prefix, palette_display_input); let prompt_text_width = UnicodeWidthStr::width(base_prompt_text.as_str()); let input_area_width = input_area.width as usize; let input_padding_needed = input_area_width.saturating_sub(prompt_text_width); let padded_prompt_text = if input_padding_needed > 0 { format!( "{}{}", base_prompt_text, PADDING_CHAR.repeat(input_padding_needed) ) } else { base_prompt_text }; let input_paragraph = Paragraph::new(padded_prompt_text) .style(Style::default().fg(theme.accent).bg(theme.bg)); f.render_widget(input_paragraph, input_area); let mut display_list_items: Vec = Vec::with_capacity(PALETTE_MAX_VISIBLE_OPTIONS); for (idx_in_visible_slice, opt_str) in visible_options_slice.iter().enumerate() { // The selected_index in navigation_state is relative to the full filtered_options list. // We need to check if the current item (from the visible slice) corresponds to the selected_index. let original_filtered_idx = display_start_offset + idx_in_visible_slice; let is_selected = current_selected_list_idx == Some(original_filtered_idx); let style = if is_selected { Style::default().fg(theme.bg).bg(theme.accent) } else { Style::default().fg(theme.fg).bg(theme.bg) }; let opt_width = opt_str.width() as u16; let list_item_width = final_list_area.width; let padding_amount = list_item_width.saturating_sub(opt_width); let padded_opt_str = format!( "{}{}", opt_str, PADDING_CHAR.repeat(padding_amount as usize) ); display_list_items.push(ListItem::new(padded_opt_str).style(style)); } // Fill remaining lines in the list area to maintain fixed height appearance let num_rendered_options = display_list_items.len(); if num_rendered_options < PALETTE_MAX_VISIBLE_OPTIONS && (final_list_area.height as usize) > num_rendered_options { for _ in num_rendered_options..(final_list_area.height as usize) { let empty_padded_str = PADDING_CHAR.repeat(final_list_area.width as usize); display_list_items.push( ListItem::new(empty_padded_str) .style(Style::default().fg(theme.bg).bg(theme.bg)), ); } } let options_list_widget = List::new(display_list_items) .block(Block::default().style(Style::default().bg(theme.bg))); f.render_widget(options_list_widget, final_list_area); }