use ratatui::Frame;
use ratatui::layout::Position;
use ratatui::layout::Rect;
use tui_pane::CpuMonitor;
use tui_pane::CpuUsage;
use tui_pane::Hittable;
use tui_pane::RenderFocus;
use tui_pane::Renderable;
use tui_pane::Viewport;
use crate::channel;
use crate::channel::Receiver;
use crate::config::CpuConfig;
use crate::tui::hit_test::HoverTarget;
use crate::tui::panes::PaneId;
use crate::tui::panes::RenderStyles;
use crate::tui::panes::cpu;
use crate::tui::render_context::PaneRenderCtx;
use crate::tui::startup_services::StartupEffect;
use crate::tui::startup_services::StartupServices;
enum CpuMonitorSlot {
Active(CpuMonitor),
Inert(Receiver<CpuUsage>),
}
impl CpuMonitorSlot {
fn new(cpu_config: &CpuConfig, startup_services: &StartupServices) -> Self {
let effect = startup_services.cpu_monitor_effect();
startup_services.record_cpu_monitor(effect);
match effect {
StartupEffect::Real => Self::Active(CpuMonitor::new(cpu_config.poll_ms)),
StartupEffect::Suppressed => {
let (_sample_tx, samples) = channel::unbounded();
Self::Inert(samples)
},
}
}
fn latest(&self) -> Option<CpuUsage> {
match self {
Self::Active(monitor) => monitor.latest(),
Self::Inert(_) => None,
}
}
fn placeholder_cpu_usage(&self) -> CpuUsage {
match self {
Self::Active(monitor) => monitor.placeholder_cpu_usage(),
Self::Inert(_) => CpuUsage::placeholder(0),
}
}
const fn receiver(&self) -> &Receiver<CpuUsage> {
match self {
Self::Active(monitor) => monitor.receiver(),
Self::Inert(samples) => samples,
}
}
const fn is_sampling(&self) -> bool {
match self {
Self::Active(monitor) => monitor.is_sampling(),
Self::Inert(_) => false,
}
}
}
pub struct CpuPane {
pub viewport: Viewport,
pub focus: RenderFocus,
content: Option<CpuUsage>,
monitor: CpuMonitorSlot,
startup_services: StartupServices,
row_rects: Vec<(Rect, usize)>,
}
impl CpuPane {
pub fn new(cpu_config: &CpuConfig, startup_services: StartupServices) -> Self {
let mut pane = Self {
viewport: Viewport::new(),
focus: RenderFocus::inactive(),
content: None,
monitor: CpuMonitorSlot::new(cpu_config, &startup_services),
startup_services,
row_rects: Vec::new(),
};
pane.install_placeholder();
pane
}
pub fn tick(&mut self) {
if let Some(usage) = self.monitor.latest() {
self.content = Some(usage);
}
}
pub const fn sample_rx(&self) -> &Receiver<CpuUsage> { self.monitor.receiver() }
pub const fn is_sampling(&self) -> bool { self.monitor.is_sampling() }
pub fn reset(&mut self, cpu_config: &CpuConfig) {
self.monitor = CpuMonitorSlot::new(cpu_config, &self.startup_services);
self.install_placeholder();
}
pub fn install_placeholder(&mut self) {
self.content = Some(self.monitor.placeholder_cpu_usage());
}
pub const fn content(&self) -> Option<&CpuUsage> { self.content.as_ref() }
pub fn set_row_rects(&mut self, rects: Vec<(Rect, usize)>) { self.row_rects = rects; }
pub fn clear_row_rects(&mut self) { self.row_rects.clear(); }
}
impl Renderable<PaneRenderCtx<'_>> for CpuPane {
fn render(&mut self, frame: &mut Frame<'_>, area: Rect, ctx: &PaneRenderCtx<'_>) {
let styles = RenderStyles {
readonly_label: ratatui::style::Style::default().fg(tui_pane::label_color()),
chrome: tui_pane::default_pane_chrome(),
};
cpu::render_cpu_pane_body(frame, area, self, &styles, ctx);
}
}
impl Hittable<HoverTarget> for CpuPane {
fn hit_test_at(&self, pos: Position) -> Option<HoverTarget> {
let (_rect, row) = self
.row_rects
.iter()
.find(|(rect, _)| rect.contains(pos))
.copied()?;
Some(HoverTarget::PaneRow {
pane: PaneId::Cpu,
row,
})
}
}