use std::io::{self, Write};
use crossterm::event::{
DisableMouseCapture, EnableMouseCapture, Event as CtEvent, KeyCode as CtKeyCode, KeyEventKind,
KeyModifiers, MouseEventKind as CtMouseKind,
};
use crossterm::execute;
use crossterm::terminal::{EnterAlternateScreen, LeaveAlternateScreen};
use ratatui::buffer::Buffer;
use ratatui::layout::Rect;
use ratatui::style::Style;
use super::event::{Event, Key, KeyCode, Mouse, MouseKind};
use super::style::Theme;
use super::surface::Surface;
use super::view::{RenderCtx, View};
pub struct AltScreen {
active: bool,
}
impl AltScreen {
pub fn enter() -> io::Result<Self> {
let mut out = io::stdout();
execute!(out, EnterAlternateScreen, EnableMouseCapture)?;
out.flush()?;
Ok(Self { active: true })
}
pub fn leave(&mut self) {
if self.active {
let mut out = io::stdout();
let _ = execute!(out, DisableMouseCapture, LeaveAlternateScreen);
let _ = out.flush();
self.active = false;
}
}
}
impl Drop for AltScreen {
fn drop(&mut self) {
self.leave();
}
}
pub struct Overlay<'a> {
pub area: Rect,
pub view: &'a dyn View,
pub clear: bool,
}
pub fn paint(
buffer: &mut Buffer,
area: Rect,
theme: &Theme,
root: &dyn View,
overlays: &[Overlay],
) {
{
let mut surface = Surface::new(buffer, area);
surface.fill(Style::default().bg(theme.background));
}
let ctx = RenderCtx::new(theme);
{
let mut surface = Surface::new(buffer, area);
root.render(area, &mut surface, &ctx);
}
for overlay in overlays {
let mut surface = Surface::new(buffer, overlay.area);
if overlay.clear {
surface.fill(Style::default().bg(theme.surface));
}
overlay
.view
.render(overlay.area, &mut surface, &ctx.with_focus(true));
}
}
pub fn translate_event(event: CtEvent) -> Option<Event> {
match event {
CtEvent::Key(k) => {
if k.kind == KeyEventKind::Release {
return None;
}
let code = match k.code {
CtKeyCode::Char(c) => KeyCode::Char(c),
CtKeyCode::Enter => KeyCode::Enter,
CtKeyCode::Esc => KeyCode::Esc,
CtKeyCode::Backspace => KeyCode::Backspace,
CtKeyCode::Delete => KeyCode::Delete,
CtKeyCode::Tab => KeyCode::Tab,
CtKeyCode::BackTab => KeyCode::BackTab,
CtKeyCode::Up => KeyCode::Up,
CtKeyCode::Down => KeyCode::Down,
CtKeyCode::Left => KeyCode::Left,
CtKeyCode::Right => KeyCode::Right,
CtKeyCode::Home => KeyCode::Home,
CtKeyCode::End => KeyCode::End,
CtKeyCode::PageUp => KeyCode::PageUp,
CtKeyCode::PageDown => KeyCode::PageDown,
_ => return None,
};
Some(Event::Key(Key {
code,
ctrl: k.modifiers.contains(KeyModifiers::CONTROL),
alt: k.modifiers.contains(KeyModifiers::ALT),
shift: k.modifiers.contains(KeyModifiers::SHIFT),
}))
}
CtEvent::Mouse(m) => {
let kind = match m.kind {
CtMouseKind::Down(_) => MouseKind::Down,
CtMouseKind::Up(_) => MouseKind::Up,
CtMouseKind::ScrollUp => MouseKind::ScrollUp,
CtMouseKind::ScrollDown => MouseKind::ScrollDown,
CtMouseKind::Moved | CtMouseKind::Drag(_) => MouseKind::Moved,
_ => return None,
};
Some(Event::Mouse(Mouse {
kind,
column: m.column,
row: m.row,
}))
}
CtEvent::Paste(text) => Some(Event::Paste(text)),
CtEvent::Resize(width, height) => Some(Event::Resize { width, height }),
_ => None,
}
}