suggestions in the dropdown menu now works amazingly well

This commit is contained in:
filipriec
2025-06-15 23:11:27 +02:00
parent 0e69df8282
commit 512e7fb9e7
7 changed files with 541 additions and 118 deletions

View File

@@ -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);
}