Rust library to use TUI and markup to build UI terminal interfaces.
Xml Code:
xml
<layout id="root" direction="vertical">
<block constraint="10"> <!-- Don't forget the size, 1 by default -->
<p align="center">
Press q to quit.
</p>
</block>
<block id="bts_block" constraint="6">
<button id="btn_hello" action="open_dialog" index="1"> Say Hello </button>
</block>
<dialog id="dlg" show="show_dialog" buttons="Okay" action="on_dialog_event">
<block id="dlg_block" border="all">
<p align="center">
Hello World!!!
</p>
</block>
</dialog>
</layout>
Rust Code:
```rust use crossterm::event::KeyCode::{self, Char}; use std::{collections::HashMap, io}; use tui::backend::CrosstermBackend; use tuimarkuprenderer::{eventresponse::EventResponse, markupparser::MarkupParser};
fn main() -> Result<(), Box
// prepare the markup parser
let mut mp = MarkupParser::new("./assets/layout.tml".to_string(), None, state);
// Dialogs generate button identifiers following the convention "on_<dialog id>_btn_<button name>"
mp.add_action("open_dialog", |state| {
let mut state = state.clone();
state.insert("show_dialog".to_string(), "true".to_string());
EventResponse::STATE(state)
})
.add_action("on_dlg_btn_Okay", |state| {
let mut state = state.clone();
state.insert("show_dialog".to_string(), "false".to_string());
EventResponse::STATE(state)
})
.ui_loop(backend, |key_event, mut state| {
let mut pressed = "none";
match key_event.code {
KeyCode::Esc => {
pressed = "close_dialog";
}
Char('q') => {
pressed = "close";
}
_ => {}
}
match pressed {
"close_dialog" => {
state.insert("show_dialog".to_string(), "false".to_string());
EventResponse::STATE(state)
}
"close" => {
state.insert("show_dialog".to_string(), "false".to_string());
EventResponse::QUIT
}
_ => EventResponse::NOOP,
}
})
}
```
As a developer is easier to create a known data structure describing the user interface.
Sample markup code:
xml
<layout id="root" direction="vertical">
<container id="nav_container" constraint="5">
<p id="toolbar" title="Navigation" border="all" styles="fg:green">
This is the navigation
</p>
</container>
<container id="body_container" constraint="10min">
<p id="body" title="Body" border="all" styles="fg:red">
This is a sample
</p>
</container>
</layout>
generates:
```xml
This is a sample
<layout id="content_info" direction="horizontal">
<container id="ats_container" constraint="20%">
<block id="ats_block" title="Ats" border="all">
</block>
</container>
<container id="cnt_container" constraint="20min">
<block id="cnt_block" title="Cnt" border="all">
</block>
</container>
</layout>
</block>
generates:
Layout code:
Something better?
```xml
button {
fg: red;
bg: black;
}
button:focus {
fg: white;
bg: red;
}
#footer {
bg:black;
fg:blue;
}
Header sample
<layout id="content_info" direction="horizontal">
<container id="ats_container" constraint="20%" title="Ats" border="all">
<layout id="vert_info" direction="vertical">
<block id="ats_block" constraint="5">
<button id="btn_hello" action="do_something" index="1" styles="fg:magenta" focus_styles="fg:white;bg:magenta"> Hello </button>
</block>
<block id="bts_block" constraint="5">
<button id="btn_hello_2" action="do_something_else" index="3"> Simple </button>
</block>
<block id="bts_block" constraint="5">
<button id="btn_hello_3" action="do_something_else" index="2"> World </button>
</block>
</layout>
</container>
<container id="cnt_container" constraint="20min">
<block id="cnt_block" title="Cnt" border="all">
<p>
lorem ipsum dolor sit amet sample.
</p>
</block>
</container>
</layout>
</block>
Rust Code:
```rust use clap::Parser; use crossterm::event::KeyCode::{Char, self}; use std::{collections::HashMap, io}; use tui::backend::CrosstermBackend; use tuimarkuprenderer::{ markupparser::MarkupParser, eventresponse::EventResponse, };
struct Args { #[arg(short, long, defaultvaluet = String::from("run"))] executiontype: String, #[arg(short, long, defaultvaluet = String::from("./assets/layout1.tml"))] layout: String, #[arg(short, long, defaultvaluet = false)] printargs: bool, }
fn main() -> Result<(), Box
let stdout = io::stdout();
let backend = CrosstermBackend::new(stdout);
let state = Some(HashMap::new());
let mut mp = MarkupParser::new(layout.clone(), None, state);
mp.add_action(
"do_something",
|_state: &mut HashMap<String, String>| {
println!("hello!!!");
EventResponse::NOOP
},
)
.add_action(
"do_something_else",
|_state: &mut HashMap<String, String>| {
println!("world!!!");
EventResponse::NOOP
},
)
.add_action(
"on_dlg1_btn_Yes",
|_state: &mut HashMap<String, String>| {
EventResponse::QUIT
},
)
.add_action(
"on_dlg1_btn_Cancel",
|state: &mut HashMap<String, String>| {
let key = "showQuitDialog".to_string();
state.insert(key, "false".to_string());
EventResponse::STATE(state.clone())
},
)
;
if print_args {
println!(
"[layout: {}, execution_type: {}, print_args: {}]",
layout, execution_type, print_args
);
}
if execution_type == String::from("run") {
// async move
mp.ui_loop(backend, |key_event, state| {
let mut new_state = state.clone();
let key = "showQuitDialog".to_string();
// let back_value = String::new();
let mut pressed = '\n';
match key_event.code {
KeyCode::Esc => {
pressed = '\r';
}
Char(character) => {
pressed = character;
}
_ => {}
}
if pressed == '\r' {
let new_value = "false";
new_state.insert(
key,
new_value.to_string(),
);
return EventResponse::STATE(new_state);
}
if pressed == 'q' {
let new_value = "true";
new_state.insert(
key,
new_value.to_string(),
);
return EventResponse::STATE(new_state);
}
return EventResponse::NOOP;
})
} else {
env_logger::init();
mp.test_check(backend)
}
}
```
Will generate this: