use crate::core::geometry::Rect;
use crate::core::event::{Event, EventType};
use crate::core::command::{CM_OK, CM_CANCEL};
use crate::terminal::Terminal;
use super::dialog::Dialog;
use super::input_line::InputLine;
use super::listbox::ListBox;
use super::button::Button;
use super::label::Label;
use super::static_text::StaticText;
use super::{View, ViewId};
use super::help_file::HelpFile;
use std::rc::Rc;
use std::cell::RefCell;
const CMD_TOPIC_SELECTED: u16 = 1002;
pub struct HelpIndex {
dialog: Dialog,
_help_file: Rc<RefCell<HelpFile>>,
#[allow(dead_code)]
all_topics: Vec<(String, String)>, filtered_topics: Vec<(String, String)>,
_search_input_id: ViewId,
_topic_list_id: ViewId,
selected_topic: Option<String>,
}
impl HelpIndex {
pub fn new(bounds: Rect, title: &str, help_file: Rc<RefCell<HelpFile>>) -> Self {
let mut dialog = Dialog::new(bounds, title);
dialog.add(Box::new(StaticText::new(
Rect::new(2, 2, bounds.width() - 4, 3),
"Search for a topic:"
)));
let search_label = Label::new(Rect::new(2, 4, 10, 5), "Search:");
dialog.add(Box::new(search_label));
let search_data = Rc::new(RefCell::new(String::new()));
let search_input = InputLine::new(Rect::new(10, 4, bounds.width() - 4, 5), 100, search_data.clone());
let search_input_id = dialog.add(Box::new(search_input));
let list_label = Label::new(Rect::new(2, 6, 12, 7), "Topics:");
dialog.add(Box::new(list_label));
let topic_list = ListBox::new(
Rect::new(2, 7, bounds.width() - 4, bounds.height() - 6),
CMD_TOPIC_SELECTED
);
let topic_list_id = dialog.add(Box::new(topic_list));
dialog.add(Box::new(Button::new(
Rect::new(bounds.width() - 24, bounds.height() - 4, bounds.width() - 14, bounds.height() - 2),
"View",
CM_OK,
true
)));
dialog.add(Box::new(Button::new(
Rect::new(bounds.width() - 12, bounds.height() - 4, bounds.width() - 2, bounds.height() - 2),
"Close",
CM_CANCEL,
false
)));
let help = help_file.borrow();
let topic_ids = help.get_topic_ids();
let mut all_topics = Vec::new();
for id in topic_ids {
if let Some(topic) = help.get_topic(&id) {
all_topics.push((id.clone(), topic.title.clone()));
}
}
drop(help);
all_topics.sort_by(|a, b| a.1.cmp(&b.1));
let filtered_topics = all_topics.clone();
let mut index = Self {
dialog,
_help_file: help_file,
all_topics,
filtered_topics,
_search_input_id: search_input_id,
_topic_list_id: topic_list_id,
selected_topic: None,
};
index.update_topic_list();
index
}
fn update_topic_list(&mut self) {
let _topic_titles: Vec<String> = self.filtered_topics
.iter()
.map(|(_, title)| title.clone())
.collect();
}
#[allow(dead_code)]
fn filter_topics(&mut self, search_text: &str) {
if search_text.is_empty() {
self.filtered_topics = self.all_topics.clone();
} else {
let search_lower = search_text.to_lowercase();
self.filtered_topics = self.all_topics
.iter()
.filter(|(id, title)| {
title.to_lowercase().contains(&search_lower) ||
id.to_lowercase().contains(&search_lower)
})
.cloned()
.collect();
}
self.update_topic_list();
}
pub fn execute(&mut self, app: &mut crate::app::Application) -> Option<String> {
let result = self.dialog.execute(app);
if result == CM_OK && !self.filtered_topics.is_empty() {
Some(self.filtered_topics[0].0.clone())
} else {
None
}
}
pub fn get_selected_topic(&self) -> Option<String> {
self.selected_topic.clone()
}
}
impl View for HelpIndex {
fn bounds(&self) -> Rect {
self.dialog.bounds()
}
fn set_bounds(&mut self, bounds: Rect) {
self.dialog.set_bounds(bounds);
}
fn draw(&mut self, terminal: &mut Terminal) {
self.dialog.draw(terminal);
}
fn handle_event(&mut self, event: &mut Event) {
if event.what == EventType::Command && event.command == CMD_TOPIC_SELECTED {
*event = Event::command(CM_OK);
}
self.dialog.handle_event(event);
}
fn can_focus(&self) -> bool {
true
}
fn state(&self) -> crate::core::state::StateFlags {
self.dialog.state()
}
fn set_state(&mut self, state: crate::core::state::StateFlags) {
self.dialog.set_state(state);
}
fn get_palette(&self) -> Option<crate::core::palette::Palette> {
self.dialog.get_palette()
}
}
pub struct HelpIndexBuilder {
bounds: Option<Rect>,
title: Option<String>,
help_file: Option<Rc<RefCell<HelpFile>>>,
}
impl HelpIndexBuilder {
pub fn new() -> Self {
Self { bounds: None, title: None, help_file: None }
}
#[must_use]
pub fn bounds(mut self, bounds: Rect) -> Self {
self.bounds = Some(bounds);
self
}
#[must_use]
pub fn title(mut self, title: impl Into<String>) -> Self {
self.title = Some(title.into());
self
}
#[must_use]
pub fn help_file(mut self, help_file: Rc<RefCell<HelpFile>>) -> Self {
self.help_file = Some(help_file);
self
}
pub fn build(self) -> HelpIndex {
let bounds = self.bounds.expect("HelpIndex bounds must be set");
let title = self.title.expect("HelpIndex title must be set");
let help_file = self.help_file.expect("HelpIndex help_file must be set");
HelpIndex::new(bounds, &title, help_file)
}
pub fn build_boxed(self) -> Box<HelpIndex> {
Box::new(self.build())
}
}
impl Default for HelpIndexBuilder {
fn default() -> Self {
Self::new()
}
}