#[cfg(feature = "ratatui")]
mod enabled {
use std::time::Duration;
use animate_macros::animate;
use crossterm::event::{self, Event, KeyCode};
use ratatui::{
Frame,
layout::{Alignment, Constraint, Direction, Layout},
style::{Color, Style},
text::{Line, Span},
widgets::{Block, Borders, Gauge, Paragraph},
};
#[animate]
pub struct Counter {
#[once(duration = 400, easing = linear)]
value: u32,
#[alternate(duration = 800, easing = ease_in_quad)]
string: String,
}
impl Counter {
pub fn increment(&mut self) {
self.value += 10;
self.string.set("i love rust".to_string());
}
pub fn decrement(&mut self) {
self.value -= 10;
self.string.set("grease is cool".to_string());
}
pub fn render(&self, frame: &mut Frame, area: ratatui::layout::Rect) {
let display = format!("{}; {}", self.value, self.string);
let gauge_val = self.value.clamp(0, 100) as u16;
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Length(3), Constraint::Length(3)])
.split(area);
frame.render_widget(
Gauge::default()
.block(Block::default().borders(Borders::ALL).title(" counter "))
.gauge_style(Style::default().fg(ratatui::style::Color::Cyan))
.percent(gauge_val),
chunks[0],
);
frame.render_widget(
Paragraph::new(display)
.alignment(Alignment::Center)
.block(Block::default().borders(Borders::ALL)),
chunks[1],
);
}
}
#[animate]
pub struct ColorBox {
#[alternate(duration = 600, easing = linear)]
color: Color,
}
const PALETTE: &[Color] = &[
Color::Rgb(220, 60, 60),
Color::Rgb(60, 180, 220),
Color::Rgb(80, 210, 130),
Color::Rgb(200, 160, 50),
Color::Rgb(160, 80, 220),
];
impl ColorBox {
pub fn next_color(&mut self, idx: &mut usize) {
*idx = (*idx + 1) % PALETTE.len();
self.color.set(PALETTE[*idx]);
}
pub fn render(&self, frame: &mut Frame, area: ratatui::layout::Rect) {
let c = self.color.get();
frame.render_widget(
Paragraph::new(Line::from(vec![Span::raw(" color: ")])).block(
Block::default()
.borders(Borders::ALL)
.title(" color ")
.border_style(Style::default().fg(*c)),
),
area,
);
}
}
pub struct App {
pub counter: Counter,
pub color_box: ColorBox,
pub color_index: usize,
}
impl App {
pub fn new() -> Self {
Self {
counter: Counter::new(0, String::from("initial")),
color_box: ColorBox::new(PALETTE[0]),
color_index: 0,
}
}
pub fn draw(&self, frame: &mut Frame) {
let area = frame.area();
let chunks = Layout::default()
.direction(Direction::Vertical)
.margin(1)
.constraints([
Constraint::Length(7),
Constraint::Length(3),
Constraint::Min(3),
])
.split(area);
self.counter.render(frame, chunks[0]);
self.color_box.render(frame, chunks[1]);
frame.render_widget(
Paragraph::new(vec![
Line::from(""),
Line::from(" ↑ / k increment counter"),
Line::from(" ↓ / j decrement counter"),
Line::from(" space cycle color"),
Line::from(" q quit"),
Line::from(""),
Line::from(format!(" animating: ",)),
])
.block(Block::default().borders(Borders::ALL).title(" controls ")),
chunks[2],
);
}
}
pub fn run() -> Result<(), Box<dyn std::error::Error>> {
let mut app = App::new();
ratatui::run(|terminal| {
loop {
animate::tick();
terminal.draw(|frame| {
app.draw(frame);
})?;
let timeout = Duration::from_millis(8);
if event::poll(timeout)? {
if let Event::Key(key) = event::read()? {
match key.code {
KeyCode::Char('q') | KeyCode::Esc => break,
KeyCode::Up | KeyCode::Char('k') => app.counter.increment(),
KeyCode::Down | KeyCode::Char('j') => app.counter.decrement(),
KeyCode::Char(' ') => app.color_box.next_color(&mut app.color_index),
_ => {}
}
}
}
}
Ok(())
})
}
}
#[cfg(feature = "ratatui")]
fn main() -> Result<(), Box<dyn std::error::Error>> {
enabled::run()
}
#[cfg(not(feature = "ratatui"))]
fn main() {}