feat(ui): simplify AppMode minibuffer handling and panel rendering
Refactor AppMode to use MinibufferConfig for all text-entry modes. Update command implementations to use new mode constructors. Introduce PanelContent trait and replace panel structs with content types. Adjust rendering to use Panel::new and minibuffer configuration. Update imports and add MinibufferConfig struct. No functional changes; all behavior preserved. Co-Authored-By: fiddlerwoaroof/git-smart-commit (bartowski/nvidia_Nemotron-Cascade-2-30B-A3B-GGUF)
This commit is contained in:
82
src/ui/panel.rs
Normal file
82
src/ui/panel.rs
Normal file
@ -0,0 +1,82 @@
|
||||
use ratatui::{
|
||||
buffer::Buffer,
|
||||
layout::Rect,
|
||||
style::{Color, Style},
|
||||
widgets::{Block, Borders, Widget},
|
||||
};
|
||||
|
||||
use crate::ui::app::AppMode;
|
||||
|
||||
/// Trait for panel-specific content. Implement this to create a new side panel.
|
||||
pub trait PanelContent {
|
||||
/// Whether the panel should appear active given the current mode.
|
||||
fn is_active(&self, mode: &AppMode) -> bool;
|
||||
/// Color used for the active border AND the selection highlight background.
|
||||
fn active_color(&self) -> Color;
|
||||
/// Block title string (include surrounding spaces for padding).
|
||||
fn title(&self) -> &str;
|
||||
/// Number of renderable rows.
|
||||
fn item_count(&self) -> usize;
|
||||
/// Message shown when `item_count()` returns 0.
|
||||
fn empty_message(&self) -> &str;
|
||||
/// Render a single item at the given row index.
|
||||
/// `inner` is the full inner area of the panel; the item occupies row `index`.
|
||||
fn render_item(&self, index: usize, is_selected: bool, inner: Rect, buf: &mut Buffer);
|
||||
/// Optional footer rendered below the items (e.g. inline hints).
|
||||
fn render_footer(&self, _inner: Rect, _buf: &mut Buffer) {}
|
||||
}
|
||||
|
||||
/// Generic side-panel widget that delegates content rendering to a `PanelContent` impl.
|
||||
pub struct Panel<'a, C: PanelContent> {
|
||||
content: C,
|
||||
mode: &'a AppMode,
|
||||
cursor: usize,
|
||||
}
|
||||
|
||||
impl<'a, C: PanelContent> Panel<'a, C> {
|
||||
pub fn new(content: C, mode: &'a AppMode, cursor: usize) -> Self {
|
||||
Self {
|
||||
content,
|
||||
mode,
|
||||
cursor,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: PanelContent> Widget for Panel<'_, C> {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
let is_active = self.content.is_active(self.mode);
|
||||
let border_style = if is_active {
|
||||
Style::default().fg(self.content.active_color())
|
||||
} else {
|
||||
Style::default().fg(Color::DarkGray)
|
||||
};
|
||||
|
||||
let block = Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.border_style(border_style)
|
||||
.title(self.content.title());
|
||||
let inner = block.inner(area);
|
||||
block.render(area, buf);
|
||||
|
||||
if self.content.item_count() == 0 {
|
||||
buf.set_string(
|
||||
inner.x,
|
||||
inner.y,
|
||||
self.content.empty_message(),
|
||||
Style::default().fg(Color::DarkGray),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
for i in 0..self.content.item_count() {
|
||||
if i as u16 >= inner.height {
|
||||
break;
|
||||
}
|
||||
let is_selected = i == self.cursor && is_active;
|
||||
self.content.render_item(i, is_selected, inner, buf);
|
||||
}
|
||||
|
||||
self.content.render_footer(inner, buf);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user