use std::time::Duration;
use std::time::Instant;
use tui_pane::ResolvedPaneLayout;
use super::data::PaneDataStore;
use super::pane_impls::CpuPane;
use super::pane_impls::GitPane;
use super::pane_impls::LangPane;
use super::pane_impls::OutputPane;
use super::pane_impls::PackagePane;
use super::pane_impls::ProjectListPane;
use super::pane_impls::TargetsPane;
use crate::config::CpuConfig;
use crate::tui::app::HoveredPaneRow;
use crate::tui::running_targets::ProjectTargetSlice;
use crate::tui::running_targets::RunningTargetsPoller;
const RUNNING_TARGETS_POLL_INTERVAL: Duration = Duration::from_secs(1);
pub struct Panes {
pub package: PackagePane,
pub lang: LangPane,
pub cpu: CpuPane,
pub git: GitPane,
pub output: OutputPane,
pub targets: TargetsPane,
pub project_list: ProjectListPane,
pub pane_data: PaneDataStore,
pub tiled_layout: ResolvedPaneLayout<super::PaneId>,
hovered_row: Option<HoveredPaneRow>,
pub running_targets: RunningTargetsPoller,
top_row_height_cache: TopRowHeightCache,
}
#[derive(Default)]
struct TopRowHeightCache {
key: Option<(u64, u16, u16)>,
value: u16,
}
impl Panes {
pub fn new(cpu_cfg: &CpuConfig) -> Self {
Self {
package: PackagePane::new(),
lang: LangPane::new(),
cpu: CpuPane::new(cpu_cfg),
git: GitPane::new(),
output: OutputPane::new(),
targets: TargetsPane::new(),
project_list: ProjectListPane::new(),
pane_data: PaneDataStore::new(),
tiled_layout: ResolvedPaneLayout::default(),
hovered_row: None,
running_targets: RunningTargetsPoller::new(RUNNING_TARGETS_POLL_INTERVAL),
top_row_height_cache: TopRowHeightCache::default(),
}
}
pub fn cached_top_row_height(&self, key: (u64, u16, u16)) -> Option<u16> {
(self.top_row_height_cache.key == Some(key)).then_some(self.top_row_height_cache.value)
}
pub const fn store_top_row_height(&mut self, key: (u64, u16, u16), value: u16) {
self.top_row_height_cache.key = Some(key);
self.top_row_height_cache.value = value;
}
pub const fn hovered_row(&self) -> Option<HoveredPaneRow> { self.hovered_row }
pub fn set_detail_data(
&mut self,
stamp: super::data::DetailCacheKey,
package: super::PackageData,
git: super::GitData,
targets: super::TargetsData,
) {
self.package.set_content(package);
self.git.set_content(git);
self.targets.set_content(targets);
self.pane_data.set_detail_stamp(Some(stamp));
}
pub fn clear_detail_data(&mut self, stamp: Option<super::data::DetailCacheKey>) {
self.package.clear_content();
self.git.clear_content();
self.targets.clear_content();
self.pane_data.set_detail_stamp(stamp);
}
pub const fn set_hover(&mut self, hovered: Option<HoveredPaneRow>) {
self.hovered_row = hovered;
}
pub fn clear_for_tree_change(&self) { self.clear_worktree_summary_cache(); }
pub fn clear_worktree_summary_cache(&self) { self.git.clear_worktree_summary_cache(); }
pub fn cpu_tick(&mut self) { self.cpu.tick(); }
pub fn running_targets_tick(&mut self, now: Instant, projects: &[ProjectTargetSlice<'_>]) {
self.running_targets.tick(now, projects);
}
pub fn reset_cpu(&mut self, cfg: &CpuConfig) { self.cpu.reset(cfg); }
pub fn install_cpu_placeholder(&mut self) { self.cpu.install_placeholder(); }
}
#[cfg(test)]
mod detail_set_tests {
use super::Panes;
use crate::config::CpuConfig;
use crate::tui::app::VisibleRow;
use crate::tui::panes::GitData;
use crate::tui::panes::PackageData;
use crate::tui::panes::TargetsData;
use crate::tui::panes::data::DetailCacheKey;
fn fresh() -> Panes { Panes::new(&CpuConfig::default()) }
fn any_row() -> VisibleRow { VisibleRow::Root { node_index: 0 } }
fn other_row() -> VisibleRow {
VisibleRow::Member {
node_index: 0,
group_index: 0,
member_index: 0,
}
}
fn empty_detail() -> (PackageData, GitData, TargetsData) {
(
PackageData::default(),
GitData::default(),
TargetsData::default(),
)
}
#[test]
fn new_panes_detail_is_current_only_with_no_selection() {
let panes = fresh();
assert!(panes.pane_data.detail_is_current(None));
assert!(!panes.pane_data.detail_is_current(Some(DetailCacheKey {
row: any_row(),
generation: 0,
})));
}
#[test]
fn set_detail_data_writes_all_panes_and_stamps() {
let mut panes = fresh();
let key = DetailCacheKey {
row: any_row(),
generation: 3,
};
let (pkg, git, targets) = empty_detail();
panes.set_detail_data(key, pkg, git, targets);
assert!(panes.pane_data.detail_is_current(Some(key)));
assert!(panes.package.content().is_some());
assert!(panes.git.content().is_some());
assert!(panes.targets.content().is_some());
assert!(!panes.pane_data.detail_is_current(None));
assert!(!panes.pane_data.detail_is_current(Some(DetailCacheKey {
row: any_row(),
generation: 4,
})));
assert!(!panes.pane_data.detail_is_current(Some(DetailCacheKey {
row: other_row(),
generation: 3,
})));
}
#[test]
fn clear_detail_data_clears_all_panes_and_records_stamp() {
let mut panes = fresh();
let key = DetailCacheKey {
row: any_row(),
generation: 7,
};
let (pkg, git, targets) = empty_detail();
panes.set_detail_data(key, pkg, git, targets);
let clear_key = DetailCacheKey {
row: other_row(),
generation: 7,
};
panes.clear_detail_data(Some(clear_key));
assert!(panes.pane_data.detail_is_current(Some(clear_key)));
assert!(panes.package.content().is_none());
assert!(panes.git.content().is_none());
assert!(panes.targets.content().is_none());
}
#[test]
fn clear_detail_with_none_matches_none() {
let mut panes = fresh();
panes.clear_detail_data(None);
assert!(panes.pane_data.detail_is_current(None));
}
}