suggestions in the dropdown menu now works amazingly well
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
// src/components/common/autocomplete.rs
|
||||
|
||||
use common::proto::multieko2::search::search_response::Hit;
|
||||
use serde::Deserialize;
|
||||
// Keep all existing imports
|
||||
use crate::config::colors::themes::Theme;
|
||||
use ratatui::{
|
||||
layout::Rect,
|
||||
@@ -9,6 +12,11 @@ use ratatui::{
|
||||
};
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
// Helper struct for parsing the JSON inside a Hit
|
||||
#[derive(Deserialize)]
|
||||
struct SuggestionContent {
|
||||
name: String,
|
||||
}
|
||||
/// Renders an opaque dropdown list for autocomplete suggestions.
|
||||
pub fn render_autocomplete_dropdown(
|
||||
f: &mut Frame,
|
||||
@@ -88,3 +96,84 @@ pub fn render_autocomplete_dropdown(
|
||||
f.render_stateful_widget(list, dropdown_area, &mut profile_list_state);
|
||||
}
|
||||
|
||||
// --- NEW FUNCTION FOR RICH SUGGESTIONS ---
|
||||
/// Renders an opaque dropdown list for rich `Hit`-based suggestions.
|
||||
pub fn render_rich_autocomplete_dropdown(
|
||||
f: &mut Frame,
|
||||
input_rect: Rect,
|
||||
frame_area: Rect,
|
||||
theme: &Theme,
|
||||
suggestions: &[Hit], // <-- Accepts &[Hit]
|
||||
selected_index: Option<usize>,
|
||||
) {
|
||||
if suggestions.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
// --- Get display names from Hits, with a fallback for parsing errors ---
|
||||
let display_names: Vec<String> = suggestions
|
||||
.iter()
|
||||
.map(|hit| {
|
||||
serde_json::from_str::<SuggestionContent>(&hit.content_json)
|
||||
.map(|content| content.name)
|
||||
.unwrap_or_else(|_| format!("ID: {}", hit.id)) // Fallback display
|
||||
})
|
||||
.collect();
|
||||
|
||||
// --- Calculate Dropdown Size & Position ---
|
||||
let max_suggestion_width =
|
||||
display_names.iter().map(|s| s.width()).max().unwrap_or(0) as u16;
|
||||
let horizontal_padding: u16 = 2;
|
||||
let dropdown_width = (max_suggestion_width + horizontal_padding).max(10);
|
||||
let dropdown_height = (suggestions.len() as u16).min(5);
|
||||
|
||||
let mut dropdown_area = Rect {
|
||||
x: input_rect.x,
|
||||
y: input_rect.y + 1,
|
||||
width: dropdown_width,
|
||||
height: dropdown_height,
|
||||
};
|
||||
|
||||
// --- Clamping Logic (prevent rendering off-screen) ---
|
||||
if dropdown_area.bottom() > frame_area.height {
|
||||
dropdown_area.y = input_rect.y.saturating_sub(dropdown_height);
|
||||
}
|
||||
if dropdown_area.right() > frame_area.width {
|
||||
dropdown_area.x = frame_area.width.saturating_sub(dropdown_width);
|
||||
}
|
||||
dropdown_area.x = dropdown_area.x.max(0);
|
||||
dropdown_area.y = dropdown_area.y.max(0);
|
||||
|
||||
// --- Rendering Logic ---
|
||||
let background_block =
|
||||
Block::default().style(Style::default().bg(Color::DarkGray));
|
||||
f.render_widget(background_block, dropdown_area);
|
||||
|
||||
let items: Vec<ListItem> = display_names
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, s)| {
|
||||
let is_selected = selected_index == Some(i);
|
||||
let s_width = s.width() as u16;
|
||||
let padding_needed = dropdown_width.saturating_sub(s_width);
|
||||
let padded_s =
|
||||
format!("{}{}", s, " ".repeat(padding_needed as usize));
|
||||
|
||||
ListItem::new(padded_s).style(if is_selected {
|
||||
Style::default()
|
||||
.fg(theme.bg)
|
||||
.bg(theme.highlight)
|
||||
.add_modifier(Modifier::BOLD)
|
||||
} else {
|
||||
Style::default().fg(theme.fg).bg(Color::DarkGray)
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
let list = List::new(items);
|
||||
let mut list_state = ListState::default();
|
||||
list_state.select(selected_index);
|
||||
|
||||
f.render_stateful_widget(list, dropdown_area, &mut list_state);
|
||||
}
|
||||
|
||||
|
||||
@@ -78,16 +78,34 @@ pub fn render_form(
|
||||
|
||||
// --- NEW: RENDER AUTOCOMPLETE ---
|
||||
if form_state.autocomplete_active {
|
||||
if let Some(suggestions) = form_state.get_suggestions() {
|
||||
if let Some(active_rect) = active_field_rect {
|
||||
if !suggestions.is_empty() {
|
||||
// Use the Rect of the active field that render_canvas found for us.
|
||||
if let Some(active_rect) = active_field_rect {
|
||||
let selected_index = form_state.get_selected_suggestion_index();
|
||||
|
||||
// THE DECIDER LOGIC:
|
||||
// 1. Check for rich suggestions first.
|
||||
if let Some(rich_suggestions) = form_state.get_rich_suggestions() {
|
||||
if !rich_suggestions.is_empty() {
|
||||
autocomplete::render_rich_autocomplete_dropdown(
|
||||
f,
|
||||
active_rect,
|
||||
f.area(), // Use f.area() for clamping, not f.size()
|
||||
theme,
|
||||
rich_suggestions,
|
||||
selected_index,
|
||||
);
|
||||
}
|
||||
}
|
||||
// 2. Fallback to simple suggestions if rich ones aren't available.
|
||||
else if let Some(simple_suggestions) = form_state.get_suggestions() {
|
||||
if !simple_suggestions.is_empty() {
|
||||
autocomplete::render_autocomplete_dropdown(
|
||||
f,
|
||||
active_rect,
|
||||
f.area(),
|
||||
theme,
|
||||
suggestions,
|
||||
form_state.get_selected_suggestion_index(),
|
||||
simple_suggestions,
|
||||
selected_index,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user