use std::time::Duration;
use tuirealm::application::{Application, PollStrategy};
use tuirealm::command::{Cmd, CmdResult};
use tuirealm::component::{AppComponent, Component};
use tuirealm::event::{Event, Key, KeyEvent, NoUserEvent};
use tuirealm::listener::EventListenerCfg;
use tuirealm::props::{AttrValue, Attribute, QueryResult};
use tuirealm::ratatui::layout::{Constraint, Direction, Layout, Rect};
use tuirealm::ratatui::style::{Color, Style};
use tuirealm::ratatui::widgets::{LineGauge, Paragraph};
use tuirealm::ratatui::{Frame, TerminalOptions, Viewport};
use tuirealm::state::State;
use tuirealm::terminal::{CrosstermTerminalAdapter, TerminalAdapter};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut app: Application<Id, Msg, NoUserEvent> = Application::init(
EventListenerCfg::default()
.crossterm_input_listener(Duration::from_millis(10), 3)
.tick_interval(Duration::from_millis(500)),
);
app.mount(Id::Info, Box::new(Label::default()), Vec::new())?;
app.mount(Id::Display, Box::new(ProgressBar::default()), Vec::new())?;
app.active(&Id::Display).expect("failed to active");
let mut model = Model::new(
app,
CrosstermTerminalAdapter::new_with_options(TerminalOptions {
viewport: Viewport::Inline(5), })?,
);
model.view();
while !model.quit {
match model.app.tick(PollStrategy::BlockCollectUpTo(5)) {
Err(err) => {
panic!("application error {err}");
}
Ok(messages) if !messages.is_empty() => {
for msg in messages {
model.update(msg);
}
}
_ => {}
}
if model.redraw {
model.view();
model.redraw = false;
}
}
model.restore()?;
Ok(())
}
#[derive(Debug, PartialEq)]
pub enum Msg {
AppClose,
Redraw,
}
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
pub enum Id {
Info,
Display,
}
pub struct Model<T>
where
T: TerminalAdapter,
{
pub app: Application<Id, Msg, NoUserEvent>,
pub quit: bool,
pub redraw: bool,
pub terminal: T,
}
impl<T> Model<T>
where
T: TerminalAdapter,
{
pub fn new(app: Application<Id, Msg, NoUserEvent>, mut adapter: T) -> Self {
adapter.enable_raw_mode().expect("Enabling rawmode");
Self {
app,
quit: false,
redraw: true,
terminal: adapter,
}
}
pub fn view(&mut self) {
self.terminal
.draw(|f| {
let [info, display] = Layout::default()
.direction(Direction::Vertical)
.margin(1)
.spacing(1)
.constraints(
[
Constraint::Length(1), Constraint::Fill(1),
]
.as_ref(),
)
.areas(f.area());
self.app.view(&Id::Info, f, info);
self.app.view(&Id::Display, f, display);
})
.expect("Draw correctly");
}
pub fn restore(&mut self) -> Result<(), Box<dyn std::error::Error>> {
self.terminal.disable_raw_mode()?;
Ok(())
}
}
impl<T> Model<T>
where
T: TerminalAdapter,
{
fn update(&mut self, msg: Msg) {
self.redraw = true;
match msg {
Msg::AppClose => {
self.quit = true; }
Msg::Redraw => (),
}
}
}
#[derive(Default)]
pub struct Label {}
impl Component for Label {
fn view(&mut self, frame: &mut Frame, area: Rect) {
let text = "Demo to showcase tui-realm can be used inline too. Press ESCAPE to exit";
frame.render_widget(Paragraph::new(text), area);
}
fn query<'a>(&'a self, _attr: Attribute) -> Option<QueryResult<'a>> {
None
}
fn attr(&mut self, _attr: Attribute, _value: AttrValue) {}
fn state(&self) -> State {
State::None
}
fn perform(&mut self, cmd: Cmd) -> CmdResult {
CmdResult::Invalid(cmd)
}
}
impl AppComponent<Msg, NoUserEvent> for Label {
fn on(&mut self, _ev: &Event<NoUserEvent>) -> Option<Msg> {
None
}
}
#[derive(Default)]
pub struct ProgressBar {
state: f64,
}
impl Component for ProgressBar {
fn view(&mut self, frame: &mut Frame, area: Rect) {
let progressbar = LineGauge::default()
.filled_style(Style::default().fg(Color::Blue))
.unfilled_style(Style::default().fg(Color::Gray))
.ratio(self.state);
frame.render_widget(progressbar, area);
}
fn query<'a>(&'a self, _attr: Attribute) -> Option<QueryResult<'a>> {
None
}
fn attr(&mut self, _attr: Attribute, _value: AttrValue) {
}
fn state(&self) -> State {
State::None
}
fn perform(&mut self, cmd: Cmd) -> CmdResult {
CmdResult::Invalid(cmd)
}
}
impl AppComponent<Msg, NoUserEvent> for ProgressBar {
fn on(&mut self, ev: &Event<NoUserEvent>) -> Option<Msg> {
if let Event::Keyboard(KeyEvent {
code: Key::Esc,
modifiers: _,
}) = ev
{
return Some(Msg::AppClose);
}
if let Event::Tick = ev {
self.state += 0.01;
if self.state >= 1.0 {
self.state = 0.0;
}
}
Some(Msg::Redraw)
}
}