add: more TUI stuff

This commit is contained in:
Charles
2024-11-23 14:00:13 -08:00
parent 20c48e3cc4
commit 49b81c7d97
7 changed files with 875 additions and 0 deletions
+33
View File
@@ -0,0 +1,33 @@
use tui_realm_stdlib::Label;
use tuirealm::{event::{Key, KeyEvent, KeyModifiers}, props::{Alignment, Color, TextModifiers}, Component, Event, MockComponent, NoUserEvent};
use crate::Msg;
#[derive(MockComponent)]
pub struct HelloLabel {
component: Label,
}
impl Default for HelloLabel {
fn default() -> Self {
Self {
component: Label::default()
.alignment(Alignment::Center)
.foreground(Color::Green)
.modifiers(TextModifiers::BOLD)
.text("This is a label"),
}
}
}
impl Component<Msg, NoUserEvent> for HelloLabel {
fn on(&mut self, ev: tuirealm::Event<NoUserEvent>) -> Option<Msg> {
match ev {
Event::Keyboard(KeyEvent{
code: Key::Esc,
modifiers: KeyModifiers::NONE,
}) => Some(Msg::AppClose),
_ => None,
}
}
}
+15
View File
@@ -0,0 +1,15 @@
mod model;
mod hellolabel;
pub use model::*;
#[derive(Debug, PartialEq)]
pub enum Msg {
AppClose,
}
// Let's define the component ids for our application
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
pub enum Id {
Label,
}
+34
View File
@@ -0,0 +1,34 @@
use tui_tea::*;
use tuirealm::Update;
fn main() {
let mut model = Model::default();
// Enter alternate screen
let _ = model.terminal.enter_alternate_screen();
let _ = model.terminal.enable_raw_mode();
// main loop
while !model.quit {
match model.app.tick(tuirealm::PollStrategy::Once) {
Err(_) => break,
Ok(messages) => {
for msg in messages.into_iter() {
let mut msg = Some(msg);
while msg.is_some() {
model.redraw = true;
msg = model.update(msg);
}
}
}
};
if model.redraw {
model.view();
model.redraw = false;
}
}
// Terminate terminal
let _ = model.terminal.leave_alternate_screen();
let _ = model.terminal.disable_raw_mode();
let _ = model.terminal.clear_screen();
}
+69
View File
@@ -0,0 +1,69 @@
use std::time::Duration;
use tuirealm::{terminal::{CrosstermTerminalAdapter, TerminalAdapter, TerminalBridge}, Application, EventListenerCfg, NoUserEvent, Update};
use crate::Id;
pub struct Model<T>
where T: TerminalAdapter {
pub app: Application<super::Id, super::Msg, NoUserEvent>,
pub terminal: TerminalBridge<T>,
pub quit: bool,
pub redraw: bool,
}
impl Default for Model<CrosstermTerminalAdapter> {
fn default() -> Self {
Self {
app: Self::init_app(),
terminal: TerminalBridge::init_crossterm().expect("failed to init terminal"),
quit: false,
redraw: true,
}
}
}
impl<T> Model<T>
where T: TerminalAdapter {
pub fn view(&mut self) {
self.terminal.draw(|f| {
self.app.view(&super::Id::Label, f, f.area());
}).expect("failed to render view");
}
fn init_app() -> Application<super::Id, super::Msg, NoUserEvent> {
// Setup application
// NOTE: NoUserEvent is a shorthand to tell tui-realm we're not going to use any custom user event
// NOTE: the event listener is configured to use the default crossterm input listener and to raise a Tick event each second
// which we will use to update the clock
let mut app = Application::init(
EventListenerCfg::default()
.crossterm_input_listener(Duration::from_millis(20), 3)
.poll_timeout(Duration::from_millis(10))
.tick_interval(Duration::from_secs(1)),
);
app.mount(super::Id::Label, Box::new(
super::hellolabel::HelloLabel::default()
), Vec::default()).expect("failed to create label");
app.active(&Id::Label).unwrap();
app
}
}
impl<T> Update<super::Msg> for Model<T>
where T: TerminalAdapter {
fn update(&mut self, msg: Option<super::Msg>) -> Option<super::Msg> {
if msg.is_none() {
return None;
}
self.redraw = true;
match msg.unwrap() {
super::Msg::AppClose => {
self.quit = true;
None
},
_ => None,
}
}
}