use crate::render::Cell;
use crate::style::Color;
use crate::widget::theme::MAX_DROPDOWN_VISIBLE;
use crate::widget::traits::{OverlayEntry, RenderContext};
pub struct DropdownLayout {
pub overlay_y: u16,
pub height: u16,
}
pub fn calculate_dropdown_layout(ctx: &RenderContext, dropdown_height: u16) -> DropdownLayout {
let (_, abs_y) = ctx.absolute_position();
let buf_height = ctx.buffer.height();
let space_below = buf_height.saturating_sub(abs_y + 1);
let overlay_y = if space_below >= dropdown_height {
abs_y + 1
} else {
abs_y.saturating_sub(dropdown_height)
};
DropdownLayout {
overlay_y,
height: dropdown_height,
}
}
pub fn dropdown_height(item_count: usize, max_visible: Option<usize>) -> u16 {
if item_count == 0 {
return 1; }
let cap = max_visible
.map(|mv| mv.min(MAX_DROPDOWN_VISIBLE as usize))
.unwrap_or(MAX_DROPDOWN_VISIBLE as usize);
(item_count.min(cap) as u16).max(1)
}
pub struct DropdownOption<'a> {
pub label: &'a str,
pub is_highlighted: bool,
pub is_disabled: bool,
pub match_indices: std::collections::HashSet<usize>,
pub indicator: char,
}
pub struct DropdownColors {
pub fg: Option<Color>,
pub bg: Option<Color>,
pub selected_fg: Option<Color>,
pub selected_bg: Option<Color>,
pub highlight_fg: Option<Color>,
pub disabled_fg: Option<Color>,
}
pub fn render_status_row(
entry: &mut OverlayEntry,
text: &str,
width: u16,
fg: Option<Color>,
bg: Option<Color>,
text_fg: Option<Color>,
) {
let text_width = width.saturating_sub(2) as usize;
for x in 0..width {
let mut cell = Cell::new(' ');
cell.fg = fg;
cell.bg = bg;
entry.push(x, 0, cell);
}
let truncated = crate::utils::truncate_to_width(text, text_width);
let mut cx: u16 = 1;
for ch in truncated.chars() {
let mut cell = Cell::new(ch);
cell.fg = text_fg;
cell.bg = bg;
entry.push(cx, 0, cell);
cx += crate::utils::char_width(ch) as u16;
}
}
pub fn render_options(
entry: &mut OverlayEntry,
options: &[DropdownOption<'_>],
width: u16,
colors: &DropdownColors,
) {
let text_width = width.saturating_sub(2) as usize;
for (row, opt) in options.iter().enumerate() {
let y = row as u16;
let (fg, bg) = if opt.is_highlighted {
(colors.selected_fg, colors.selected_bg)
} else {
(colors.fg, colors.bg)
};
let fg = if opt.is_disabled {
colors.disabled_fg
} else {
fg
};
for x in 0..width {
let mut cell = Cell::new(' ');
cell.fg = fg;
cell.bg = bg;
entry.push(x, y, cell);
}
let mut cell = Cell::new(opt.indicator);
cell.fg = fg;
cell.bg = bg;
entry.push(0, y, cell);
let truncated = crate::utils::truncate_to_width(opt.label, text_width.saturating_sub(1));
let mut cx: u16 = 2;
for (j, ch) in truncated.chars().enumerate() {
let mut cell = Cell::new(ch);
cell.bg = bg;
if opt.is_disabled {
cell.fg = colors.disabled_fg;
} else if opt.match_indices.contains(&j) {
cell.fg = colors.highlight_fg;
} else {
cell.fg = fg;
}
entry.push(cx, y, cell);
cx += crate::utils::char_width(ch) as u16;
}
}
}
pub fn queue_or_inline_overlay(ctx: &mut RenderContext, entry: OverlayEntry) {
if ctx.has_overlay_support() {
ctx.queue_overlay(entry);
} else {
for oc in &entry.cells {
ctx.set(oc.x, oc.y.saturating_add(1), oc.cell);
}
}
}