use super::{
PICKER_MODAL, draw_modal_panel_with_hotkeys, picker_list_area, picker_modal_list_area,
};
use crate::tui::{
render::{render_prepared_scroll_view_with_theme, scroll_view_content_width},
state::MissionControlState,
};
use ratatui::{
Frame,
layout::{Rect, Size},
text::Line,
widgets::{Block, Borders, Paragraph},
};
use tui_scrollview::ScrollView;
pub(crate) fn skills_modal_list_area(area: Rect) -> Option<Rect> {
picker_modal_list_area(area, "Skills", 1)
}
pub(crate) fn skill_row_at(
area: Rect,
state: &MissionControlState,
column: u16,
row: u16,
) -> Option<(String, bool)> {
let list_area = skills_modal_list_area(area)?;
if !list_area.contains((column, row).into()) {
return None;
}
let modal = state.modals.skills_modal.as_ref()?;
let document_row = usize::from(row - list_area.y)
.saturating_add(state.scroll_offset(&state.scroll_views.skills_modal));
let mut card_y = 0_usize;
for rows in modal
.rows
.chunk_by(|left, right| left.source == right.source)
{
let first_skill_row = card_y.saturating_add(1);
if document_row >= first_skill_row
&& document_row < first_skill_row.saturating_add(rows.len())
{
let skill = &rows[document_row - first_skill_row];
return Some((skill.name.clone(), !skill.enabled));
}
card_y = card_y.saturating_add(rows.len()).saturating_add(3);
}
None
}
pub(in crate::tui::render) fn draw_skills_modal(
frame: &mut Frame<'_>,
area: Rect,
state: &MissionControlState,
) {
let Some(modal) = &state.modals.skills_modal else {
return;
};
let theme = state.theme;
let Some(content_area) = draw_modal_panel_with_hotkeys(
frame,
area,
"Skills",
PICKER_MODAL,
theme,
"[Tab] scope • [↑/↓] move • [Enter] toggle",
Some("[Esc] close"),
) else {
return;
};
if content_area.height == 0 {
return;
}
frame.render_widget(
Paragraph::new(format!("Scope: {}", modal.scope_label)).style(theme.pane()),
Rect::new(content_area.x, content_area.y, content_area.width, 1),
);
let Some(list_area) = picker_list_area(content_area, 1) else {
return;
};
if modal.rows.is_empty() {
frame.render_widget(
Paragraph::new("No skills discovered").style(theme.pane()),
list_area,
);
return;
}
let content_width = scroll_view_content_width(list_area);
if content_width == 0 {
return;
}
let groups = || {
modal
.rows
.chunk_by(|left, right| left.source == right.source)
};
let content_height = groups()
.map(|rows| rows.len().saturating_add(3))
.sum::<usize>()
.saturating_sub(1)
.max(usize::from(list_area.height));
let mut view = ScrollView::new(Size::new(
content_width,
u16::try_from(content_height).unwrap_or(u16::MAX),
));
let mut y = 0_u16;
let mut row_index = 0_usize;
for rows in groups() {
let card_height = u16::try_from(rows.len().saturating_add(2)).unwrap_or(u16::MAX);
let card_area = Rect::new(0, y, content_width, card_height);
view.render_widget(
Block::default()
.title(Line::from(rows[0].source.as_str()).style(theme.accent(true)))
.borders(Borders::ALL)
.border_style(theme.border(false))
.style(theme.app()),
card_area,
);
for row in rows {
let selected = row_index == modal.selected;
let text = format!("{} {}", if row.enabled { "[x]" } else { "[ ]" }, row.name);
view.render_widget(
Paragraph::new(text).style(if selected {
theme.selection()
} else {
theme.pane()
}),
Rect::new(2, y.saturating_add(1), content_width.saturating_sub(4), 1),
);
y = y.saturating_add(1);
row_index = row_index.saturating_add(1);
}
y = y.saturating_add(3);
}
let mut scroll_state = *state.scroll_views.skills_modal.borrow();
render_prepared_scroll_view_with_theme(frame, list_area, view, &mut scroll_state, None, theme);
}