use std::{cell::Cell, rc::Rc};
use crossterm::{event::Event, terminal};
use ratatui::layout::Rect;
use crate::{
Hook, State, SystemContext, UseState,
input::{EventOptions, EventPriority, EventResult},
};
mod private {
pub trait Sealed {}
impl Sealed for crate::Hooks<'_, '_> {}
}
pub trait UseTerminalSize: private::Sealed {
fn use_terminal_size(&mut self) -> (u16, u16);
}
impl UseTerminalSize for crate::Hooks<'_, '_> {
fn use_terminal_size(&mut self) -> (u16, u16) {
let mut size = self.use_state(|| terminal::size().unwrap_or((0, 0)));
let hook = self.use_hook(UseTerminalSizeImpl::new);
hook.size = Some(size);
size.get()
}
}
struct UseTerminalSizeImpl {
size: Option<State<(u16, u16)>>,
}
impl UseTerminalSizeImpl {
fn new() -> Self {
Self { size: None }
}
}
impl Hook for UseTerminalSizeImpl {
fn post_component_update(&mut self, updater: &mut crate::ComponentUpdater) {
let Some(mut size) = self.size else {
return;
};
let mut system = updater
.get_context_mut::<SystemContext>()
.expect("`SystemContext` missing (the root context always provides it)");
system.input.register_handler(
None,
EventPriority::Normal,
EventOptions::default(),
Rc::new(Cell::new(Rect::default())),
Box::new(move |event| {
if let Event::Resize(width, height) = event {
size.set((width, height));
}
EventResult::Ignored
}),
);
}
}
pub trait UsePreviousSize: private::Sealed {
fn use_previous_size(&mut self) -> Rect;
}
pub struct UsePreviousSizeImpl {
size: Rect,
}
impl Default for UsePreviousSizeImpl {
fn default() -> Self {
Self::new()
}
}
impl UsePreviousSizeImpl {
pub fn new() -> Self {
UsePreviousSizeImpl {
size: Rect::default(),
}
}
}
impl Hook for UsePreviousSizeImpl {
fn pre_component_draw(&mut self, drawer: &mut crate::ComponentDrawer) {
self.size = drawer.area;
}
}
impl UsePreviousSize for crate::Hooks<'_, '_> {
fn use_previous_size(&mut self) -> Rect {
let hook = self.use_hook(UsePreviousSizeImpl::new);
hook.size
}
}