1use std::cell::Ref;
16use std::cell::RefCell;
17use std::cell::RefMut;
18use std::collections::HashMap;
19use std::rc::Rc;
20
21use common::logutil::get_last_log_to_display;
22use common::logutil::CPMsgRecord;
23use cursive::event::Event;
24use cursive::event::EventResult;
25use cursive::event::EventTrigger;
26use cursive::utils::markup::StyledString;
27use cursive::view::Nameable;
28use cursive::view::Scrollable;
29use cursive::view::View;
30use cursive::view::ViewWrapper;
31use cursive::views::LinearLayout;
32use cursive::views::NamedView;
33use cursive::views::OnEventView;
34use cursive::views::Panel;
35use cursive::views::ResizedView;
36use cursive::views::ScrollView;
37use cursive::views::SelectView;
38use cursive::views::ViewRef;
39use cursive::Cursive;
40
41use crate::command_palette::CommandPalette;
42use crate::controllers::Controllers;
43use crate::tab_view::TabView;
44
45pub struct ColumnTitles {
46 pub titles: Vec<String>,
47 pub pinned_titles: usize, }
49
50pub trait StateCommon {
56 type ModelType;
57 type TagType: ToString;
58 type KeyType: Clone;
59
60 fn get_filter_info(&self) -> &Option<(Self::TagType, String)>;
62
63 fn get_tag_from_tab_idx(&self, tab: &str, idx: usize) -> Self::TagType;
65
66 fn is_filter_supported_from_tab_idx(&self, _tab: &str, _idx: usize) -> bool {
68 false
69 }
70 fn set_filter_from_tab_idx(
73 &mut self,
74 _tab: &str,
75 _idx: usize,
76 _filter: Option<String>,
77 ) -> bool {
78 false
79 }
80
81 fn set_sort_tag(&mut self, _tag: Self::TagType, _reverse: &mut bool) -> bool {
84 false
85 }
86 fn set_sort_string(&mut self, _selection: &str, _reverse: &mut bool) -> bool {
87 false
88 }
89 fn set_sort_tag_from_tab_idx(&mut self, _tab: &str, _idx: usize, _reverse: &mut bool) -> bool {
90 false
91 }
92
93 fn get_model(&self) -> Ref<Self::ModelType>;
94 fn get_model_mut(&self) -> RefMut<Self::ModelType>;
95 fn new(model: Rc<RefCell<Self::ModelType>>) -> Self;
96}
97
98pub trait ViewBridge {
100 type StateType: Default + StateCommon;
101 fn get_view_name() -> &'static str;
104
105 fn get_titles(&self) -> ColumnTitles;
107
108 fn get_rows(
115 &mut self,
116 state: &Self::StateType,
117 offset: Option<usize>,
118 ) -> Vec<(
119 StyledString,
120 <<Self as ViewBridge>::StateType as StateCommon>::KeyType,
121 )>;
122
123 fn on_select_update_state(
126 _state: &mut Self::StateType,
127 _selected_key: Option<&<<Self as ViewBridge>::StateType as StateCommon>::KeyType>,
128 ) {
129 }
130
131 fn on_select_update_cmd_palette(
134 _state: &Self::StateType,
135 _selected_key: &<<Self as ViewBridge>::StateType as StateCommon>::KeyType,
136 _current_tab: &str,
137 _selected_column: usize,
138 ) -> String {
139 "".to_owned()
140 }
141}
142
143pub struct StatsView<V: 'static + ViewBridge> {
176 tab_titles_map: HashMap<String, ColumnTitles>,
177 tab_view_map: HashMap<String, V>,
178 detailed_view: OnEventView<Panel<LinearLayout>>,
179 pub state: Rc<RefCell<V::StateType>>,
180 pub reverse_sort: bool,
181 pub event_controllers: Rc<RefCell<HashMap<Event, Controllers>>>,
182}
183
184impl<V: 'static + ViewBridge> ViewWrapper for StatsView<V> {
185 cursive::wrap_impl!(self.detailed_view: OnEventView<Panel<LinearLayout>>);
186
187 fn wrap_on_event(&mut self, ch: Event) -> EventResult {
191 if ch == Event::Refresh {
193 return EventResult::Ignored;
194 }
195
196 let cmd_mode = self.get_cmd_palette().is_cmd_mode();
198 if cmd_mode {
199 return self.get_cmd_palette().on_event(ch);
200 }
201
202 let controller = self
203 .event_controllers
204 .borrow()
205 .get(&ch)
206 .unwrap_or(&Controllers::Unknown)
207 .clone();
208
209 if controller == Controllers::Unknown {
211 self.with_view_mut(|v| v.on_event(ch))
212 .unwrap_or(EventResult::Ignored)
213 } else {
214 controller.handle(self, &[]);
215 EventResult::with_cb(move |c| controller.callback::<V>(c, &[]))
216 }
217 }
218}
219
220impl<V: 'static + ViewBridge> StatsView<V> {
221 #[allow(unused)]
222 pub fn new(
223 name: &'static str,
224 tabs: Vec<String>,
225 tab_view_map: HashMap<String, V>,
226 select_view: SelectView<<V::StateType as StateCommon>::KeyType>,
227 state: V::StateType,
228 event_controllers: Rc<RefCell<HashMap<Event, Controllers>>>,
229 cmd_controllers: Rc<RefCell<HashMap<&'static str, Controllers>>>,
230 ) -> Self {
231 let mut tab_titles_map = HashMap::new();
232 for (tab, bridge) in &tab_view_map {
233 let mut titles = bridge.get_titles();
234 tab_titles_map.insert(tab.into(), titles);
235 }
236
237 let default_tab = tabs[0].clone();
238 let tab_titles = tab_titles_map
239 .get(&default_tab)
240 .expect("Failed to query default tab");
241
242 let select_view_with_cb =
243 select_view.on_select(|c, selected_key: &<V::StateType as StateCommon>::KeyType| {
244 c.call_on_name(V::get_view_name(), |view: &mut StatsView<V>| {
245 V::on_select_update_state(&mut view.state.borrow_mut(), Some(selected_key));
246 let mut cmd_palette = view.get_cmd_palette();
247 let cur_tab = view.get_tab_view().get_cur_selected().to_string();
248 let selected_column = view.get_title_view().current_selected;
249 cmd_palette.set_info(V::on_select_update_cmd_palette(
250 &view.state.borrow(),
251 selected_key,
252 &cur_tab,
253 selected_column,
254 ));
255 });
256 });
257
258 let detailed_view = OnEventView::new(Panel::new(
259 LinearLayout::vertical()
260 .child(
261 TabView::new(tabs, " ", 0 )
262 .expect("Fail to construct tab")
263 .with_name(format!("{}_tab", &name)),
264 )
265 .child(
266 LinearLayout::vertical()
267 .child(
268 TabView::new(
269 tab_titles.titles.clone(),
270 " ",
271 tab_titles.pinned_titles, )
273 .expect("Fail to construct title")
274 .with_name(format!("{}_title", &name)),
275 )
276 .child(ResizedView::with_full_screen(
277 select_view_with_cb
278 .with_name(format!("{}_detail", &name))
279 .scrollable(),
280 ))
281 .scrollable()
282 .scroll_x(true)
283 .scroll_y(false),
284 )
285 .child(
286 CommandPalette::new::<V>(name, "<root>", cmd_controllers)
287 .with_name(format!("{}_cmd_palette", &name)),
288 ),
289 ));
290
291 Self {
292 tab_titles_map,
293 tab_view_map,
294 detailed_view,
295 state: Rc::new(RefCell::new(state)),
296 reverse_sort: true,
297 event_controllers,
298 }
299 }
300
301 pub fn update_title(&mut self) {
303 let cur_tab = self.get_tab_view().get_cur_selected().to_string();
304 let mut title_view = self.get_title_view();
305 let tabs = self
306 .tab_titles_map
307 .get(&cur_tab)
308 .unwrap_or_else(|| panic!("Fail to query title from tab {}", cur_tab));
309 title_view.tabs.clone_from(&tabs.titles);
310 title_view.fixed_tabs = tabs.pinned_titles;
311 title_view.current_selected = 0;
312 title_view.current_offset_idx = 0;
313 title_view.cur_offset = 0;
314 title_view.total_length = title_view.tabs.iter().fold(0, |acc, x| acc + x.len() + 1);
315 title_view.cur_length = title_view.tabs[0].len();
316 }
317
318 pub fn on_event<F, E>(mut self, trigger: E, cb: F) -> Self
320 where
321 E: Into<EventTrigger>,
322 F: 'static + Fn(&mut Cursive),
323 {
324 self.detailed_view.set_on_event(trigger, cb);
325 self
326 }
327
328 pub fn get_tab_view(&mut self) -> ViewRef<TabView> {
330 let tab_panel: &mut NamedView<TabView> = self
331 .detailed_view .get_inner_mut() .get_inner_mut() .get_child_mut(0) .expect("Fail to get tab panel, StatsView may not properly init")
336 .downcast_mut()
337 .expect("Fail to downcast to panel, StatsView may not properly init");
338
339 tab_panel.get_mut()
340 }
341
342 fn get_scroll_view(&mut self) -> &mut ScrollView<LinearLayout> {
344 self.detailed_view .get_inner_mut() .get_inner_mut() .get_child_mut(1) .expect("Fail to get stats scrollable, StatsView may not properly init")
349 .downcast_mut()
350 .expect("Fail to downcast to stats scrollable, StatsView may not properly init")
351 }
352
353 pub fn get_title_view(&mut self) -> ViewRef<TabView> {
355 let scroll_view = self.get_scroll_view();
356
357 let title_named: &mut NamedView<TabView> = scroll_view
358 .get_inner_mut() .get_child_mut(0) .expect("Fail to get title, StatsView may not properly init")
361 .downcast_mut()
362 .expect("Fail to downcast to title, StatsView may not properly init");
363
364 title_named.get_mut()
365 }
366
367 pub fn get_list_scroll_view(
369 &mut self,
370 ) -> &mut ScrollView<NamedView<SelectView<<V::StateType as StateCommon>::KeyType>>> {
371 let scroll_view = self.get_scroll_view();
372
373 #[allow(clippy::type_complexity)]
374 let select_named: &mut ResizedView<
375 ScrollView<NamedView<SelectView<<V::StateType as StateCommon>::KeyType>>>,
376 > = scroll_view
377 .get_inner_mut() .get_child_mut(1) .expect("Fail to get title, StatsView may not properly init")
380 .downcast_mut()
381 .expect("Fail to downcast to title, StatsView may not properly init");
382
383 select_named.get_inner_mut()
384 }
385
386 pub fn get_detail_view(
388 &mut self,
389 ) -> ViewRef<SelectView<<V::StateType as StateCommon>::KeyType>> {
390 self.get_list_scroll_view().get_inner_mut().get_mut()
391 }
392
393 pub fn get_cmd_palette(&mut self) -> ViewRef<CommandPalette> {
395 let cmd_palette: &mut NamedView<CommandPalette> = self
396 .detailed_view .get_inner_mut() .get_inner_mut() .get_child_mut(2) .expect("Fail to get cmd palette, StatsView may not properly init")
401 .downcast_mut()
402 .expect("Fail to downcast to cmd palette, StatsView may not properly init");
403
404 cmd_palette.get_mut()
405 }
406
407 pub fn get_screen_width(&mut self) -> usize {
409 self.get_scroll_view().content_viewport().width()
410 }
411
412 pub fn set_horizontal_offset(&mut self, x: usize) {
413 let screen_width = self.get_screen_width();
414 if screen_width < x {
415 self.get_title_view()
416 .scroll_to_offset(x - screen_width, screen_width);
417 } else {
418 self.get_title_view().scroll_to_offset(0, screen_width);
419 }
420 }
421
422 pub fn refresh(&mut self, c: &mut Cursive) {
426 {
427 let cur_tab = self.get_tab_view().get_cur_selected().to_string();
428 let mut select_view = self.get_detail_view();
429
430 let pos = select_view.selected_id().unwrap_or(0);
431 select_view.clear();
432
433 let horizontal_offset = self.get_title_view().current_offset_idx;
434
435 let tab_detail = self
436 .tab_view_map
437 .get_mut(&cur_tab)
438 .unwrap_or_else(|| panic!("Fail to query data from tab {}", cur_tab));
439 select_view.add_all(tab_detail.get_rows(&self.state.borrow(), Some(horizontal_offset)));
440
441 select_view.select_down(pos)(c);
445
446 let mut cmd_palette = self.get_cmd_palette();
447 if let Some(msg) = get_last_log_to_display() {
448 cmd_palette.set_alert(msg);
449 }
450
451 let selection = select_view.selection().map(|rc| rc.as_ref().clone());
452 let selected_column = self.get_title_view().current_selected;
453 V::on_select_update_state(&mut self.state.borrow_mut(), selection.as_ref());
454 if let (false, Some(selection)) = (cmd_palette.is_alerting(), selection) {
457 let info_msg = V::on_select_update_cmd_palette(
458 &self.state.borrow(),
459 &selection,
460 &cur_tab,
461 selected_column,
462 );
463 cmd_palette.set_info(info_msg);
464 }
465 }
466 self.get_list_scroll_view().scroll_to_important_area();
467 }
468
469 pub fn feed_data(mut self, c: &mut Cursive) -> Self {
471 self.refresh(c);
472 self
473 }
474
475 pub fn get_view(c: &mut Cursive) -> ViewRef<Self> {
477 c.find_name::<Self>(V::get_view_name())
478 .expect("Fail to find view with name")
479 }
480
481 pub fn refresh_myself(c: &mut Cursive) {
484 Self::get_view(c).refresh(c)
485 }
486
487 pub fn set_alert(&mut self, msg: &str) {
488 self.get_cmd_palette()
489 .set_alert(CPMsgRecord::construct_msg(slog::Level::Warning, msg));
490 }
491
492 pub fn cp_warn(c: &mut Cursive, msg: &str) {
494 Self::get_view(c).set_alert(msg);
495 }
496
497 pub fn cp_filter(c: &mut Cursive, filter_info: Option<(String, String)>) {
500 Self::get_view(c).get_cmd_palette().set_filter(filter_info);
501 }
502}