use super::view::{View, write_line_to_terminal};
use crate::core::command::{CM_HISTORY_SELECTED, CM_RECORD_HISTORY, CM_SHOW_HISTORY};
use crate::core::draw::DrawBuffer;
use crate::core::event::{Event, EventType, MB_LEFT_BUTTON};
use crate::core::geometry::{Point, Rect};
use crate::core::history::HistoryManager;
use crate::core::state::StateFlags;
use crate::terminal::Terminal;
use std::cell::RefCell;
use std::rc::Rc;
pub struct History {
bounds: Rect,
history_id: u16,
state: StateFlags,
link: Rc<RefCell<String>>,
pub selected_item: Option<String>, palette_chain: Option<crate::core::palette_chain::PaletteChainNode>,
}
impl History {
pub fn new(pos: Point, history_id: u16, link: Rc<RefCell<String>>) -> Self {
Self {
bounds: Rect::new(pos.x, pos.y, pos.x + 2, pos.y + 1),
history_id,
state: 0,
link,
selected_item: None,
palette_chain: None,
}
}
pub fn has_items(&self) -> bool {
HistoryManager::has_history(self.history_id)
}
pub fn history_id(&self) -> u16 {
self.history_id
}
}
impl View for History {
fn bounds(&self) -> Rect {
self.bounds
}
fn set_bounds(&mut self, bounds: Rect) {
self.bounds = bounds;
}
fn draw(&mut self, terminal: &mut Terminal) {
let mut buf = DrawBuffer::new(2);
let arrow = if self.has_items() { "▼" } else { " " };
use crate::core::palette::colors::{BUTTON_NORMAL, BUTTON_SELECTED};
let color = if self.is_focused() {
BUTTON_SELECTED
} else {
BUTTON_NORMAL
};
buf.move_str(0, arrow, color);
write_line_to_terminal(terminal, self.bounds.a.x, self.bounds.a.y, &buf);
}
fn handle_event(&mut self, event: &mut Event) {
match event.what {
EventType::MouseDown => {
if self.bounds.contains(event.mouse.pos)
&& event.mouse.buttons & MB_LEFT_BUTTON != 0
{
if self.has_items() {
event.what = EventType::Command;
event.command = CM_SHOW_HISTORY;
event.info = self.history_id;
} else {
event.clear();
}
}
}
EventType::Broadcast => match event.command {
CM_RECORD_HISTORY => {
let text = self.link.borrow().clone();
if !text.is_empty() {
HistoryManager::add(self.history_id, text);
}
}
CM_HISTORY_SELECTED if event.info == self.history_id => {
if let Some(item) = HistoryManager::get_list(self.history_id).first() {
self.selected_item = Some(item.clone());
*self.link.borrow_mut() = item.clone();
}
}
_ => {}
},
_ => {}
}
}
fn can_focus(&self) -> bool {
false }
fn state(&self) -> StateFlags {
self.state
}
fn set_state(&mut self, state: StateFlags) {
self.state = state;
}
fn set_palette_chain(&mut self, node: Option<crate::core::palette_chain::PaletteChainNode>) {
self.palette_chain = node;
}
fn get_palette_chain(&self) -> Option<&crate::core::palette_chain::PaletteChainNode> {
self.palette_chain.as_ref()
}
fn get_palette(&self) -> Option<crate::core::palette::Palette> {
use crate::core::palette::{Palette, palettes};
Some(Palette::from_slice(palettes::CP_HISTORY))
}
}
pub struct HistoryBuilder {
pos: Option<Point>,
history_id: Option<u16>,
link: Option<Rc<RefCell<String>>>,
}
impl HistoryBuilder {
pub fn new() -> Self {
Self {
pos: None,
history_id: None,
link: None,
}
}
#[must_use]
pub fn link(mut self, link: Rc<RefCell<String>>) -> Self {
self.link = Some(link);
self
}
#[must_use]
pub fn pos(mut self, pos: Point) -> Self {
self.pos = Some(pos);
self
}
#[must_use]
pub fn history_id(mut self, history_id: u16) -> Self {
self.history_id = Some(history_id);
self
}
pub fn build(self) -> History {
let pos = self.pos.expect("History pos must be set");
let history_id = self.history_id.expect("History history_id must be set");
let link = self.link.expect("History link must be set");
History::new(pos, history_id, link)
}
pub fn build_boxed(self) -> Box<History> {
Box::new(self.build())
}
}
impl Default for HistoryBuilder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn link(text: &str) -> Rc<RefCell<String>> {
Rc::new(RefCell::new(text.to_string()))
}
#[test]
fn test_history_button_creation() {
let _guard = crate::core::history::test_lock();
HistoryManager::clear_all();
let button = History::new(Point::new(20, 5), 1, link(""));
assert!(!button.has_items());
assert_eq!(button.bounds.width(), 2);
}
#[test]
fn test_history_button_with_items() {
let _guard = crate::core::history::test_lock();
HistoryManager::clear_all();
HistoryManager::add(2, "test".to_string());
let button = History::new(Point::new(20, 5), 2, link(""));
assert!(button.has_items());
}
#[test]
fn test_record_history_broadcast_records_linked_data() {
let _guard = crate::core::history::test_lock();
HistoryManager::clear_all();
let data = link("hello world");
let mut button = History::new(Point::new(20, 5), 3, Rc::clone(&data));
let mut event = Event::broadcast(CM_RECORD_HISTORY);
button.handle_event(&mut event);
assert_eq!(HistoryManager::get_list(3), vec!["hello world".to_string()]);
assert_eq!(event.what, EventType::Broadcast);
data.borrow_mut().clear();
let mut event = Event::broadcast(CM_RECORD_HISTORY);
button.handle_event(&mut event);
assert_eq!(HistoryManager::count(3), 1);
}
#[test]
fn test_click_converts_to_show_history_command() {
let _guard = crate::core::history::test_lock();
HistoryManager::clear_all();
HistoryManager::add(4, "entry".to_string());
let mut button = History::new(Point::new(20, 5), 4, link(""));
let mut event = Event::mouse(
EventType::MouseDown,
Point::new(20, 5),
MB_LEFT_BUTTON,
false,
);
button.handle_event(&mut event);
assert_eq!(event.what, EventType::Command);
assert_eq!(event.command, CM_SHOW_HISTORY);
assert_eq!(event.info, 4);
assert_eq!(event.mouse.pos, Point::new(20, 5));
}
#[test]
fn test_click_with_empty_history_is_consumed() {
let _guard = crate::core::history::test_lock();
HistoryManager::clear_all();
let mut button = History::new(Point::new(20, 5), 5, link(""));
let mut event = Event::mouse(
EventType::MouseDown,
Point::new(21, 5),
MB_LEFT_BUTTON,
false,
);
button.handle_event(&mut event);
assert_eq!(event.what, EventType::Nothing);
}
#[test]
fn test_history_selected_broadcast_updates_link() {
let _guard = crate::core::history::test_lock();
HistoryManager::clear_all();
HistoryManager::add(6, "older".to_string());
HistoryManager::add(6, "picked".to_string());
let data = link("current");
let mut button = History::new(Point::new(20, 5), 6, Rc::clone(&data));
let mut event = Event::broadcast_with_info(CM_HISTORY_SELECTED, 6);
button.handle_event(&mut event);
assert_eq!(*data.borrow(), "picked");
*data.borrow_mut() = "unchanged".to_string();
let mut event = Event::broadcast_with_info(CM_HISTORY_SELECTED, 7);
button.handle_event(&mut event);
assert_eq!(*data.borrow(), "unchanged");
}
}