Stable name colkumn widths

This commit is contained in:
canvrno-oai
2026-04-16 09:50:39 -07:00
parent a65e80c967
commit 0380327292
3 changed files with 68 additions and 75 deletions
@@ -25,11 +25,7 @@ use super::popup_consts::MAX_POPUP_ROWS;
use super::scroll_state::ScrollState;
pub(crate) use super::selection_popup_common::ColumnWidthMode;
use super::selection_popup_common::GenericDisplayRow;
use super::selection_popup_common::measure_rows_height;
use super::selection_popup_common::measure_rows_height_stable_col_widths;
use super::selection_popup_common::measure_rows_height_with_col_width_mode;
use super::selection_popup_common::render_rows;
use super::selection_popup_common::render_rows_stable_col_widths;
use super::selection_popup_common::render_rows_with_col_width_mode;
use super::selection_tabs::SelectionTab;
use super::selection_tabs::render_tab_bar;
@@ -150,6 +146,8 @@ pub(crate) struct SelectionViewParams {
pub is_searchable: bool,
pub search_placeholder: Option<String>,
pub col_width_mode: ColumnWidthMode,
/// Rendered left-column width to use for auto-sized rows.
pub name_column_width: Option<usize>,
pub header: Box<dyn Renderable>,
pub initial_selected_idx: Option<usize>,
@@ -194,6 +192,7 @@ impl Default for SelectionViewParams {
is_searchable: false,
search_placeholder: None,
col_width_mode: ColumnWidthMode::AutoVisible,
name_column_width: None,
header: Box::new(()),
initial_selected_idx: None,
side_content: Box::new(()),
@@ -226,6 +225,7 @@ pub(crate) struct ListSelectionView {
search_query: String,
search_placeholder: Option<String>,
col_width_mode: ColumnWidthMode,
name_column_width: Option<usize>,
filtered_indices: Vec<usize>,
last_selected_actual_idx: Option<usize>,
header: Box<dyn Renderable>,
@@ -291,6 +291,7 @@ impl ListSelectionView {
None
},
col_width_mode: params.col_width_mode,
name_column_width: params.name_column_width,
filtered_indices: Vec::new(),
last_selected_actual_idx: None,
header,
@@ -805,27 +806,14 @@ impl Renderable for ListSelectionView {
// Measure wrapped height for up to MAX_POPUP_ROWS items.
let rows = self.build_rows();
let rows_height = match self.col_width_mode {
ColumnWidthMode::AutoVisible => measure_rows_height(
&rows,
&self.state,
MAX_POPUP_ROWS,
effective_rows_width.saturating_add(1),
),
ColumnWidthMode::AutoAllRows => measure_rows_height_stable_col_widths(
&rows,
&self.state,
MAX_POPUP_ROWS,
effective_rows_width.saturating_add(1),
),
ColumnWidthMode::Fixed => measure_rows_height_with_col_width_mode(
&rows,
&self.state,
MAX_POPUP_ROWS,
effective_rows_width.saturating_add(1),
ColumnWidthMode::Fixed,
),
};
let rows_height = measure_rows_height_with_col_width_mode(
&rows,
&self.state,
MAX_POPUP_ROWS,
effective_rows_width.saturating_add(1),
self.col_width_mode,
self.name_column_width,
);
let header = self.active_header();
let tab_height = tab_bar_height(&self.tabs, self.active_tab_idx.unwrap_or(0), inner_width);
@@ -893,27 +881,14 @@ impl Renderable for ListSelectionView {
let header_height = header.desired_height(inner_width);
let tab_height = tab_bar_height(&self.tabs, self.active_tab_idx.unwrap_or(0), inner_width);
let rows = self.build_rows();
let rows_height = match self.col_width_mode {
ColumnWidthMode::AutoVisible => measure_rows_height(
&rows,
&self.state,
MAX_POPUP_ROWS,
effective_rows_width.saturating_add(1),
),
ColumnWidthMode::AutoAllRows => measure_rows_height_stable_col_widths(
&rows,
&self.state,
MAX_POPUP_ROWS,
effective_rows_width.saturating_add(1),
),
ColumnWidthMode::Fixed => measure_rows_height_with_col_width_mode(
&rows,
&self.state,
MAX_POPUP_ROWS,
effective_rows_width.saturating_add(1),
ColumnWidthMode::Fixed,
),
};
let rows_height = measure_rows_height_with_col_width_mode(
&rows,
&self.state,
MAX_POPUP_ROWS,
effective_rows_width.saturating_add(1),
self.col_width_mode,
self.name_column_width,
);
// Stacked (fallback) side content height — only used when not side-by-side.
let stacked_side_h = if side_w.is_none() {
@@ -984,33 +959,16 @@ impl Renderable for ListSelectionView {
width: effective_rows_width.max(1),
height: list_area.height,
};
match self.col_width_mode {
ColumnWidthMode::AutoVisible => render_rows(
render_area,
buf,
&rows,
&self.state,
render_area.height as usize,
"no matches",
),
ColumnWidthMode::AutoAllRows => render_rows_stable_col_widths(
render_area,
buf,
&rows,
&self.state,
render_area.height as usize,
"no matches",
),
ColumnWidthMode::Fixed => render_rows_with_col_width_mode(
render_area,
buf,
&rows,
&self.state,
render_area.height as usize,
"no matches",
ColumnWidthMode::Fixed,
),
};
render_rows_with_col_width_mode(
render_area,
buf,
&rows,
&self.state,
render_area.height as usize,
"no matches",
self.col_width_mode,
self.name_column_width,
);
}
// -- Side content (preview panel) --
@@ -127,6 +127,7 @@ fn compute_desc_col(
visible_items: usize,
content_width: u16,
col_width_mode: ColumnWidthMode,
name_column_width: Option<usize>,
) -> usize {
if content_width <= 1 {
return 0;
@@ -177,7 +178,10 @@ fn compute_desc_col(
ColumnWidthMode::Fixed => 0,
};
max_name_width.saturating_add(2).min(max_auto_desc_col)
name_column_width
.unwrap_or(max_name_width)
.saturating_add(2)
.min(max_auto_desc_col)
}
}
}
@@ -374,6 +378,7 @@ fn adjust_start_for_wrapped_selection_visibility(
width: u16,
viewport_height: u16,
col_width_mode: ColumnWidthMode,
name_column_width: Option<usize>,
) -> usize {
let mut start_idx = compute_item_window_start(rows_all, state, max_items);
let Some(sel) = state.selected_idx else {
@@ -392,6 +397,7 @@ fn adjust_start_for_wrapped_selection_visibility(
desc_measure_items,
width,
col_width_mode,
name_column_width,
);
if is_selected_visible_in_wrapped_viewport(
rows_all,
@@ -507,6 +513,7 @@ fn render_rows_inner(
max_results: usize,
empty_message: &str,
col_width_mode: ColumnWidthMode,
name_column_width: Option<usize>,
) -> u16 {
if rows_all.is_empty() {
if area.height > 0 {
@@ -532,6 +539,7 @@ fn render_rows_inner(
area.width,
area.height,
col_width_mode,
name_column_width,
);
let desc_col = compute_desc_col(
@@ -540,6 +548,7 @@ fn render_rows_inner(
desc_measure_items,
area.width,
col_width_mode,
name_column_width,
);
// Render items, wrapping descriptions and aligning wrapped lines under the
@@ -604,6 +613,7 @@ pub(crate) fn render_rows(
max_results,
empty_message,
ColumnWidthMode::AutoVisible,
None,
)
}
@@ -632,6 +642,7 @@ pub(crate) fn render_rows_stable_col_widths(
max_results,
empty_message,
ColumnWidthMode::AutoAllRows,
None,
)
}
@@ -649,6 +660,7 @@ pub(crate) fn render_rows_with_col_width_mode(
max_results: usize,
empty_message: &str,
col_width_mode: ColumnWidthMode,
name_column_width: Option<usize>,
) -> u16 {
render_rows_inner(
area,
@@ -658,6 +670,7 @@ pub(crate) fn render_rows_with_col_width_mode(
max_results,
empty_message,
col_width_mode,
name_column_width,
)
}
@@ -704,6 +717,7 @@ pub(crate) fn render_rows_single_line(
visible_items,
area.width,
ColumnWidthMode::AutoVisible,
None,
);
let mut cur_y = area.y;
@@ -767,6 +781,7 @@ pub(crate) fn measure_rows_height(
max_results,
width,
ColumnWidthMode::AutoVisible,
None,
)
}
@@ -785,6 +800,7 @@ pub(crate) fn measure_rows_height_stable_col_widths(
max_results,
width,
ColumnWidthMode::AutoAllRows,
None,
)
}
@@ -797,8 +813,16 @@ pub(crate) fn measure_rows_height_with_col_width_mode(
max_results: usize,
width: u16,
col_width_mode: ColumnWidthMode,
name_column_width: Option<usize>,
) -> u16 {
measure_rows_height_inner(rows_all, state, max_results, width, col_width_mode)
measure_rows_height_inner(
rows_all,
state,
max_results,
width,
col_width_mode,
name_column_width,
)
}
fn measure_rows_height_inner(
@@ -807,6 +831,7 @@ fn measure_rows_height_inner(
max_results: usize,
width: u16,
col_width_mode: ColumnWidthMode,
name_column_width: Option<usize>,
) -> u16 {
if rows_all.is_empty() {
return 1; // placeholder "no matches" line
@@ -833,6 +858,7 @@ fn measure_rows_height_inner(
visible_items,
content_width,
col_width_mode,
name_column_width,
);
let mut total: u16 = 0;
+9
View File
@@ -33,11 +33,13 @@ use ratatui::text::Line;
use ratatui::widgets::Paragraph;
use ratatui::widgets::WidgetRef;
use ratatui::widgets::Wrap;
use unicode_width::UnicodeWidthStr;
const PLUGINS_SELECTION_VIEW_ID: &str = "plugins-selection";
const ALL_PLUGINS_TAB_ID: &str = "all-plugins";
const INSTALLED_PLUGINS_TAB_ID: &str = "installed-plugins";
const OPENAI_CURATED_TAB_ID: &str = "marketplace:openai-curated";
const PLUGIN_MENU_ROW_PREFIX_WIDTH: usize = 2;
const LOADING_ANIMATION_DELAY: Duration = Duration::from_secs(1);
const LOADING_ANIMATION_INTERVAL: Duration = Duration::from_millis(100);
@@ -738,6 +740,12 @@ impl ChatWidget {
.count();
let all_entries = plugin_entries_for_marketplaces(marketplaces.iter().copied());
let name_column_width = all_entries
.iter()
.map(|(_, _, display_name)| {
PLUGIN_MENU_ROW_PREFIX_WIDTH + UnicodeWidthStr::width(display_name.as_str())
})
.max();
let installed_entries = all_entries
.iter()
.filter(|(_, plugin, _)| plugin.installed)
@@ -859,6 +867,7 @@ impl ChatWidget {
is_searchable: true,
search_placeholder: Some("Type to search plugins".to_string()),
col_width_mode: ColumnWidthMode::AutoAllRows,
name_column_width,
..Default::default()
}
}