use std::error::Error;
use std::time::Duration;
use tui_realm_stdlib::components::{Span, Spinner};
use tuirealm::application::PollStrategy;
use tuirealm::command::CmdResult;
use tuirealm::component::{AppComponent, Component};
use tuirealm::event::{Event, Key, KeyEvent, NoUserEvent};
use tuirealm::props::{Color, HorizontalAlignment, TextModifiers};
use tuirealm::ratatui::layout::{Constraint, Direction as LayoutDirection, Layout};
use tuirealm::ratatui::style::Stylize;
use tuirealm::ratatui::text::Span as RSpan;
use tuirealm::subscription::{EventClause, Sub, SubClause};
use tuirealm::terminal::TerminalAdapter;
mod utils;
use utils::Model;
#[derive(Debug, PartialEq)]
pub enum Msg {
AppClose,
Redraw,
}
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
pub enum Id {
SpanAlfa,
SpanBeta,
SpinnerAlfa,
SpinnerBeta,
}
impl Model<Id, Msg> {
fn view(&mut self) {
self.terminal
.draw(|f| {
let chunks = Layout::default()
.direction(LayoutDirection::Vertical)
.margin(1)
.constraints(
[
Constraint::Length(3),
Constraint::Length(3),
Constraint::Length(1),
]
.as_ref(),
)
.split(f.area());
let row1 = Layout::default()
.direction(LayoutDirection::Horizontal)
.margin(1)
.constraints([Constraint::Length(2), Constraint::Min(10)].as_ref())
.split(chunks[0]);
self.app.view(&Id::SpinnerAlfa, f, row1[0]);
self.app.view(&Id::SpanAlfa, f, row1[1]);
let row2 = Layout::default()
.direction(LayoutDirection::Horizontal)
.margin(1)
.constraints([Constraint::Length(2), Constraint::Min(10)].as_ref())
.split(chunks[1]);
self.app.view(&Id::SpinnerBeta, f, row2[0]);
self.app.view(&Id::SpanBeta, f, row2[1]);
})
.expect("Drawing to the terminal failed");
}
fn update(&mut self, msg: Msg) {
self.redraw = true;
match msg {
Msg::AppClose => {
self.quit = true;
}
Msg::Redraw => (),
}
}
fn mount_main(&mut self) -> Result<(), Box<dyn Error>> {
self.app
.mount(Id::SpanAlfa, Box::new(SpanAlfa::default()), vec![])?;
self.app
.mount(Id::SpanBeta, Box::new(SpanBeta::default()), vec![])?;
self.app.mount(
Id::SpinnerAlfa,
Box::new(SpinnerAlfa::default()),
vec![Sub::new(EventClause::Tick, SubClause::Always)],
)?;
self.app
.mount(Id::SpinnerBeta, Box::new(SpinnerBeta::default()), vec![])?;
self.app.active(&Id::SpanAlfa)?;
Ok(())
}
}
fn main() {
let mut model = Model::new();
model.mount_main().expect("Mount all main components");
while !model.quit {
if let Ok(messages) = model
.app
.tick(PollStrategy::Once(Duration::from_millis(10)))
{
for msg in messages {
model.update(msg);
}
}
model.view();
}
}
#[derive(Component)]
struct SpanAlfa {
component: Span,
}
impl Default for SpanAlfa {
fn default() -> Self {
Self {
component: Span::default().foreground(Color::Yellow).spans([
RSpan::raw("Downloading tui-realm...")
.underlined()
.fg(Color::Green),
RSpan::raw(" "),
RSpan::from("Please wait!"),
]),
}
}
}
impl AppComponent<Msg, NoUserEvent> for SpanAlfa {
fn on(&mut self, ev: &Event<NoUserEvent>) -> Option<Msg> {
match ev {
Event::Keyboard(KeyEvent { code: Key::Esc, .. }) => return Some(Msg::AppClose),
_ => CmdResult::NoChange,
};
Some(Msg::Redraw)
}
}
#[derive(Component)]
struct SpanBeta {
component: Span,
}
impl Default for SpanBeta {
fn default() -> Self {
Self {
component: Span::default()
.foreground(Color::White)
.background(Color::DarkGray)
.alignment_horizontal(HorizontalAlignment::Right)
.modifiers(TextModifiers::BOLD)
.spans([
RSpan::raw("Downloading tui-realm-stdlib...")
.underlined()
.fg(Color::Yellow)
.bg(Color::Black),
RSpan::raw(" "),
RSpan::from("Please wait!"),
]),
}
}
}
impl AppComponent<Msg, NoUserEvent> for SpanBeta {
fn on(&mut self, ev: &Event<NoUserEvent>) -> Option<Msg> {
match ev {
Event::Keyboard(KeyEvent { code: Key::Esc, .. }) => return Some(Msg::AppClose),
_ => CmdResult::NoChange,
};
Some(Msg::Redraw)
}
}
#[derive(Component)]
struct SpinnerAlfa {
component: Spinner,
}
impl Default for SpinnerAlfa {
fn default() -> Self {
Self {
component: Spinner::default()
.foreground(Color::LightBlue)
.sequence("⣾⣽⣻⢿⡿⣟⣯⣷")
.manual_step(),
}
}
}
impl AppComponent<Msg, NoUserEvent> for SpinnerAlfa {
fn on(&mut self, ev: &Event<NoUserEvent>) -> Option<Msg> {
if *ev == Event::Tick {
self.component.states.step();
}
None
}
}
#[derive(Component)]
struct SpinnerBeta {
component: Spinner,
}
impl Default for SpinnerBeta {
fn default() -> Self {
Self {
component: Spinner::default()
.foreground(Color::LightBlue)
.sequence("▉▊▋▌▍▎▏▎▍▌▋▊▉"),
}
}
}
impl AppComponent<Msg, NoUserEvent> for SpinnerBeta {
fn on(&mut self, _: &Event<NoUserEvent>) -> Option<Msg> {
None
}
}