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};
use tuirealm::listener::EventListenerCfg;
use tuirealm::props::{AttrValue, Attribute, QueryResult};
use tuirealm::ratatui::Frame;
use tuirealm::ratatui::layout::{Constraint, Direction, Layout, Rect};
use tuirealm::ratatui::widgets::Paragraph;
use tuirealm::state::State;
use tuirealm::terminal::{CrosstermTerminalAdapter, TerminalAdapter, TerminalResult};
fn crossterm() -> EventListenerCfg<UserEvent> {
EventListenerCfg::default().crossterm_input_listener(Duration::from_millis(10), 3)
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let event_listener = crossterm();
let mut app: Application<Id, Msg, UserEvent> = Application::init(event_listener);
app.mount(Id::Info, Box::new(Label::default()), Vec::new())?;
app.mount(Id::Display, Box::new(EventDisplay::default()), Vec::new())?;
app.active(&Id::Display).expect("failed to active");
let mut model = Model::new(app)?;
tokio::task::block_in_place(|| {
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.terminal.restore()?;
Ok(())
}
#[derive(Debug, PartialEq)]
pub enum Msg {
AppClose,
Redraw,
}
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
pub enum Id {
Info,
Display,
}
#[derive(Debug, Eq, Clone, Copy, PartialOrd, Ord)]
pub enum UserEvent {}
impl PartialEq for UserEvent {
fn eq(&self, other: &Self) -> bool {
std::mem::discriminant(self) == std::mem::discriminant(other)
}
}
pub struct Model {
pub app: Application<Id, Msg, UserEvent>,
pub quit: bool,
pub redraw: bool,
pub terminal: CrosstermTerminalAdapter,
}
impl Model {
fn init_adapter() -> TerminalResult<CrosstermTerminalAdapter> {
let mut adapter = CrosstermTerminalAdapter::new()?;
adapter.enable_raw_mode()?;
adapter.enter_alternate_screen()?;
adapter.enable_mouse_capture()?;
Ok(adapter)
}
pub fn new(app: Application<Id, Msg, UserEvent>) -> TerminalResult<Self> {
Ok(Self {
app,
quit: false,
redraw: true,
terminal: Self::init_adapter()?,
})
}
pub fn view(&mut self) {
self.terminal
.draw(|f| {
let [info, display] = Layout::default()
.direction(Direction::Vertical)
.margin(1)
.constraints(
[
Constraint::Length(3), 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");
}
}
impl Model {
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 display Pressed Keys. 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, UserEvent> for Label {
fn on(&mut self, _ev: &Event<UserEvent>) -> Option<Msg> {
None
}
}
#[derive(Default)]
pub struct EventDisplay {
last_event: Option<Event<UserEvent>>,
is_same: bool,
}
impl Component for EventDisplay {
fn view(&mut self, frame: &mut Frame, area: Rect) {
let text = format!(
"Is Same as previous: {}\nEvent: {:#?}",
self.is_same, self.last_event
);
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, UserEvent> for EventDisplay {
fn on(&mut self, ev: &Event<UserEvent>) -> Option<Msg> {
if let Event::Keyboard(KeyEvent {
code: Key::Esc,
modifiers: _,
}) = ev
{
return Some(Msg::AppClose);
}
let is_same = Some(ev) == self.last_event.as_ref();
self.last_event = Some(ev.clone());
self.is_same = is_same;
Some(Msg::Redraw)
}
}