use std::cell::Ref;
use std::cell::RefCell;
use std::cell::RefMut;
use std::collections::HashMap;
use std::rc::Rc;
use common::logutil::get_last_log_to_display;
use common::logutil::CPMsgRecord;
use cursive::event::Event;
use cursive::event::EventResult;
use cursive::event::EventTrigger;
use cursive::utils::markup::StyledString;
use cursive::view::Nameable;
use cursive::view::Scrollable;
use cursive::view::View;
use cursive::view::ViewWrapper;
use cursive::views::LinearLayout;
use cursive::views::NamedView;
use cursive::views::OnEventView;
use cursive::views::Panel;
use cursive::views::ResizedView;
use cursive::views::ScrollView;
use cursive::views::SelectView;
use cursive::views::ViewRef;
use cursive::Cursive;
use crate::command_palette::CommandPalette;
use crate::controllers::Controllers;
use crate::tab_view::TabView;
pub struct ColumnTitles {
pub titles: Vec<String>,
pub pinned_titles: usize, }
pub trait StateCommon {
type ModelType;
type TagType: ToString;
type KeyType: Clone;
fn get_filter_info(&self) -> &Option<(Self::TagType, String)>;
fn get_tag_from_tab_idx(&self, tab: &str, idx: usize) -> Self::TagType;
fn is_filter_supported_from_tab_idx(&self, _tab: &str, _idx: usize) -> bool {
false
}
fn set_filter_from_tab_idx(
&mut self,
_tab: &str,
_idx: usize,
_filter: Option<String>,
) -> bool {
false
}
fn set_sort_tag(&mut self, _tag: Self::TagType, _reverse: &mut bool) -> bool {
false
}
fn set_sort_string(&mut self, _selection: &str, _reverse: &mut bool) -> bool {
false
}
fn set_sort_tag_from_tab_idx(&mut self, _tab: &str, _idx: usize, _reverse: &mut bool) -> bool {
false
}
fn get_model(&self) -> Ref<Self::ModelType>;
fn get_model_mut(&self) -> RefMut<Self::ModelType>;
fn new(model: Rc<RefCell<Self::ModelType>>) -> Self;
}
pub trait ViewBridge {
type StateType: Default + StateCommon;
fn get_view_name() -> &'static str;
fn get_titles(&self) -> ColumnTitles;
fn get_rows(
&mut self,
state: &Self::StateType,
offset: Option<usize>,
) -> Vec<(
StyledString,
<<Self as ViewBridge>::StateType as StateCommon>::KeyType,
)>;
fn on_select_update_state(
_state: &mut Self::StateType,
_selected_key: Option<&<<Self as ViewBridge>::StateType as StateCommon>::KeyType>,
) {
}
fn on_select_update_cmd_palette(
_state: &Self::StateType,
_selected_key: &<<Self as ViewBridge>::StateType as StateCommon>::KeyType,
_current_tab: &str,
_selected_column: usize,
) -> String {
"".to_owned()
}
}
pub struct StatsView<V: 'static + ViewBridge> {
tab_titles_map: HashMap<String, ColumnTitles>,
tab_view_map: HashMap<String, V>,
detailed_view: OnEventView<Panel<LinearLayout>>,
pub state: Rc<RefCell<V::StateType>>,
pub reverse_sort: bool,
pub event_controllers: Rc<RefCell<HashMap<Event, Controllers>>>,
}
impl<V: 'static + ViewBridge> ViewWrapper for StatsView<V> {
cursive::wrap_impl!(self.detailed_view: OnEventView<Panel<LinearLayout>>);
fn wrap_on_event(&mut self, ch: Event) -> EventResult {
if ch == Event::Refresh {
return EventResult::Ignored;
}
let cmd_mode = self.get_cmd_palette().is_cmd_mode();
if cmd_mode {
return self.get_cmd_palette().on_event(ch);
}
let controller = self
.event_controllers
.borrow()
.get(&ch)
.unwrap_or(&Controllers::Unknown)
.clone();
if controller == Controllers::Unknown {
self.with_view_mut(|v| v.on_event(ch))
.unwrap_or(EventResult::Ignored)
} else {
controller.handle(self, &[]);
EventResult::with_cb(move |c| controller.callback::<V>(c, &[]))
}
}
}
impl<V: 'static + ViewBridge> StatsView<V> {
#[allow(unused)]
pub fn new(
name: &'static str,
tabs: Vec<String>,
tab_view_map: HashMap<String, V>,
select_view: SelectView<<V::StateType as StateCommon>::KeyType>,
state: V::StateType,
event_controllers: Rc<RefCell<HashMap<Event, Controllers>>>,
cmd_controllers: Rc<RefCell<HashMap<&'static str, Controllers>>>,
) -> Self {
let mut tab_titles_map = HashMap::new();
for (tab, bridge) in &tab_view_map {
let mut titles = bridge.get_titles();
tab_titles_map.insert(tab.into(), titles);
}
let default_tab = tabs[0].clone();
let tab_titles = tab_titles_map
.get(&default_tab)
.expect("Failed to query default tab");
let select_view_with_cb =
select_view.on_select(|c, selected_key: &<V::StateType as StateCommon>::KeyType| {
c.call_on_name(V::get_view_name(), |view: &mut StatsView<V>| {
V::on_select_update_state(&mut view.state.borrow_mut(), Some(selected_key));
let mut cmd_palette = view.get_cmd_palette();
let cur_tab = view.get_tab_view().get_cur_selected().to_string();
let selected_column = view.get_title_view().current_selected;
cmd_palette.set_info(V::on_select_update_cmd_palette(
&view.state.borrow(),
selected_key,
&cur_tab,
selected_column,
));
});
});
let detailed_view = OnEventView::new(Panel::new(
LinearLayout::vertical()
.child(
TabView::new(tabs, " ", 0 )
.expect("Fail to construct tab")
.with_name(format!("{}_tab", &name)),
)
.child(
LinearLayout::vertical()
.child(
TabView::new(
tab_titles.titles.clone(),
" ",
tab_titles.pinned_titles,
)
.expect("Fail to construct title")
.with_name(format!("{}_title", &name)),
)
.child(ResizedView::with_full_screen(
select_view_with_cb
.with_name(format!("{}_detail", &name))
.scrollable(),
))
.scrollable()
.scroll_x(true)
.scroll_y(false),
)
.child(
CommandPalette::new::<V>(name, "<root>", cmd_controllers)
.with_name(format!("{}_cmd_palette", &name)),
),
));
Self {
tab_titles_map,
tab_view_map,
detailed_view,
state: Rc::new(RefCell::new(state)),
reverse_sort: true,
event_controllers,
}
}
pub fn update_title(&mut self) {
let cur_tab = self.get_tab_view().get_cur_selected().to_string();
let mut title_view = self.get_title_view();
let tabs = self
.tab_titles_map
.get(&cur_tab)
.unwrap_or_else(|| panic!("Fail to query title from tab {}", cur_tab));
title_view.tabs.clone_from(&tabs.titles);
title_view.fixed_tabs = tabs.pinned_titles;
title_view.current_selected = 0;
title_view.current_offset_idx = 0;
title_view.cur_offset = 0;
title_view.total_length = title_view.tabs.iter().fold(0, |acc, x| acc + x.len() + 1);
title_view.cur_length = title_view.tabs[0].len();
}
pub fn on_event<F, E>(mut self, trigger: E, cb: F) -> Self
where
E: Into<EventTrigger>,
F: 'static + Fn(&mut Cursive),
{
self.detailed_view.set_on_event(trigger, cb);
self
}
pub fn get_tab_view(&mut self) -> ViewRef<TabView> {
let tab_panel: &mut NamedView<TabView> = self
.detailed_view .get_inner_mut() .get_inner_mut() .get_child_mut(0) .expect("Fail to get tab panel, StatsView may not properly init")
.downcast_mut()
.expect("Fail to downcast to panel, StatsView may not properly init");
tab_panel.get_mut()
}
fn get_scroll_view(&mut self) -> &mut ScrollView<LinearLayout> {
self.detailed_view .get_inner_mut() .get_inner_mut() .get_child_mut(1) .expect("Fail to get stats scrollable, StatsView may not properly init")
.downcast_mut()
.expect("Fail to downcast to stats scrollable, StatsView may not properly init")
}
pub fn get_title_view(&mut self) -> ViewRef<TabView> {
let scroll_view = self.get_scroll_view();
let title_named: &mut NamedView<TabView> = scroll_view
.get_inner_mut() .get_child_mut(0) .expect("Fail to get title, StatsView may not properly init")
.downcast_mut()
.expect("Fail to downcast to title, StatsView may not properly init");
title_named.get_mut()
}
pub fn get_list_scroll_view(
&mut self,
) -> &mut ScrollView<NamedView<SelectView<<V::StateType as StateCommon>::KeyType>>> {
let scroll_view = self.get_scroll_view();
#[allow(clippy::type_complexity)]
let select_named: &mut ResizedView<
ScrollView<NamedView<SelectView<<V::StateType as StateCommon>::KeyType>>>,
> = scroll_view
.get_inner_mut() .get_child_mut(1) .expect("Fail to get title, StatsView may not properly init")
.downcast_mut()
.expect("Fail to downcast to title, StatsView may not properly init");
select_named.get_inner_mut()
}
pub fn get_detail_view(
&mut self,
) -> ViewRef<SelectView<<V::StateType as StateCommon>::KeyType>> {
self.get_list_scroll_view().get_inner_mut().get_mut()
}
pub fn get_cmd_palette(&mut self) -> ViewRef<CommandPalette> {
let cmd_palette: &mut NamedView<CommandPalette> = self
.detailed_view .get_inner_mut() .get_inner_mut() .get_child_mut(2) .expect("Fail to get cmd palette, StatsView may not properly init")
.downcast_mut()
.expect("Fail to downcast to cmd palette, StatsView may not properly init");
cmd_palette.get_mut()
}
pub fn get_screen_width(&mut self) -> usize {
self.get_scroll_view().content_viewport().width()
}
pub fn set_horizontal_offset(&mut self, x: usize) {
let screen_width = self.get_screen_width();
if screen_width < x {
self.get_title_view()
.scroll_to_offset(x - screen_width, screen_width);
} else {
self.get_title_view().scroll_to_offset(0, screen_width);
}
}
pub fn refresh(&mut self, c: &mut Cursive) {
{
let cur_tab = self.get_tab_view().get_cur_selected().to_string();
let mut select_view = self.get_detail_view();
let pos = select_view.selected_id().unwrap_or(0);
select_view.clear();
let horizontal_offset = self.get_title_view().current_offset_idx;
let tab_detail = self
.tab_view_map
.get_mut(&cur_tab)
.unwrap_or_else(|| panic!("Fail to query data from tab {}", cur_tab));
select_view.add_all(tab_detail.get_rows(&self.state.borrow(), Some(horizontal_offset)));
select_view.select_down(pos)(c);
let mut cmd_palette = self.get_cmd_palette();
if let Some(msg) = get_last_log_to_display() {
cmd_palette.set_alert(msg);
}
let selection = select_view.selection().map(|rc| rc.as_ref().clone());
let selected_column = self.get_title_view().current_selected;
V::on_select_update_state(&mut self.state.borrow_mut(), selection.as_ref());
if let (false, Some(selection)) = (cmd_palette.is_alerting(), selection) {
let info_msg = V::on_select_update_cmd_palette(
&self.state.borrow(),
&selection,
&cur_tab,
selected_column,
);
cmd_palette.set_info(info_msg);
}
}
self.get_list_scroll_view().scroll_to_important_area();
}
pub fn feed_data(mut self, c: &mut Cursive) -> Self {
self.refresh(c);
self
}
pub fn get_view(c: &mut Cursive) -> ViewRef<Self> {
c.find_name::<Self>(V::get_view_name())
.expect("Fail to find view with name")
}
pub fn refresh_myself(c: &mut Cursive) {
Self::get_view(c).refresh(c)
}
pub fn set_alert(&mut self, msg: &str) {
self.get_cmd_palette()
.set_alert(CPMsgRecord::construct_msg(slog::Level::Warning, msg));
}
pub fn cp_warn(c: &mut Cursive, msg: &str) {
Self::get_view(c).set_alert(msg);
}
pub fn cp_filter(c: &mut Cursive, filter_info: Option<(String, String)>) {
Self::get_view(c).get_cmd_palette().set_filter(filter_info);
}
}