wip: first pass

This commit is contained in:
Jonathan Kelley 2021-07-28 10:52:38 -04:00
commit e3171e8303
6 changed files with 337 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
/target
Cargo.lock

3
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,3 @@
{
"rust-analyzer.inlayHints.enable": true
}

2
.vscode/spellright.dict vendored Normal file
View file

@ -0,0 +1,2 @@
esque
Tui

17
Cargo.toml Normal file
View file

@ -0,0 +1,17 @@
[package]
name = "rink"
version = "0.1.0"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
stretch = "0.3.2"
tui = { version = "0.15.0", features = ["crossterm"] }
crossterm = "0.20.0"
anyhow = "1.0.42"
thiserror = "1.0.24"
dioxus = { path = "../../dioxus", features = ["core"] }
hecs = "0.6.0"
tui-template = { path = "../../Tinkering/tui-builder" }
# tui-template = { git = "https://github.com/jkelleyrtp/tui-builder.git" }

43
README.md Normal file
View file

@ -0,0 +1,43 @@
# Rink: Like "Ink" but for Rust and Dioxus
The fastest portable TUIs in the west
🔫🤠🔫
🐎🔥🔥🔥
Rink lets you build terminal user interfaces in Rust with Dioxus.
You can use html-esque semantics with stylesheets, inline styles, tree hierarchy, components, etc, but your Tui app is probably not going to work well or look good in the web. It still technically is a limited subset of HTML, so use at your own risk.
```rust
static App: FC<()> = |cx| {
cx.render(rsx!{
div { width: "100%", height: "3px", border_style: "solid",
h1 { "Hello world!" }
p { "This is a paragraph." }
}
})
}
```
an image should go here
Rink is basically a port of [Ink]() but for Rust and Dioxus. Rink doesn't depend on Node.js or any other JavaScript runtime, so your binaries are portable and beautiful.
## Features
Rink features:
- Flexbox based layout system
- CSS selectors
- inline css support
- Built-in focusing system
- high-quality keyboard support
- Support for events, hooks, and callbacks
- support for a very limited subset of HTML and CSS primitives
HTML elements supported:
- div, h1-h6, p, input, textarea, tables, nav
CSS Supported:
- Flex

270
src/lib.rs Normal file
View file

@ -0,0 +1,270 @@
use anyhow::Result;
use dioxus::core::*;
use tui::backend::CrosstermBackend;
use tui::layout::{Layout, Rect};
use tui::style::{Modifier, Style as TuiStyle};
use tui::text::{Span, Spans};
use tui::widgets::{Block, Paragraph};
use tui_template::tuiapp::CrosstermFrame;
struct RinkDom {
vdom: VirtualDom,
}
impl RinkDom {
pub fn new(app: FC<()>) -> Self {
Self {
vdom: VirtualDom::new(app),
}
}
fn render_vnode<'a>(
&self,
f: &mut CrosstermFrame,
node: &'a VNode<'a>,
state: &mut RenderState<'a>,
) -> Rect {
match &node.kind {
VNodeKind::Fragment(_) => todo!(),
VNodeKind::Component(_) => todo!(),
VNodeKind::Suspended { node } => todo!(),
VNodeKind::Text(te) => {
let span = Span::styled(te.text, TuiStyle::default());
let mut m = Modifier::empty();
for style in &state.current_styles {
match style {
Styles::Bold => m = m | Modifier::BOLD,
Styles::Italic => m = m | Modifier::ITALIC,
Styles::Strikethrough => m = m | Modifier::CROSSED_OUT,
Styles::Emphasis => m = m | Modifier::ITALIC,
Styles::Underline => m = m | Modifier::UNDERLINED,
}
}
let style = TuiStyle::default().add_modifier(m);
let span = span.styled_graphemes(style);
let cur_block = state.block_stack.last_mut().unwrap();
// Paragraph
// f.render_widget(span);
}
VNodeKind::Element(el) => {
//
let mut new_layout = false;
// all of our supported styles
match el.tag_name {
// obviously semantically not really correct
"div" => {
state.layouts.push(Layout::default());
new_layout = true;
}
"title" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6" => {
let mut block = state.block_stack.pop().unwrap();
let children = el.children;
if let (1, Some(VNodeKind::Text(te))) =
(children.len(), children.get(0).map(|f| &f.kind))
{
block = block.title(vec![Span::from(te.text)]);
}
state.block_stack.push(block);
}
"span" | "header" => {}
"footer" => {}
"p" => {
state.layouts.push(Layout::default());
new_layout = true;
}
// elements that style for whatever reason
"em" => state.current_styles.push(Styles::Emphasis),
"i" => state.current_styles.push(Styles::Italic),
"b" => state.current_styles.push(Styles::Bold),
"u" => state.current_styles.push(Styles::Underline),
"strike" => state.current_styles.push(Styles::Strikethrough),
"li" => {}
"ul" => {}
"ol" => {}
"code" => {}
"hr" => {}
// Tables
"table" => {}
"tr" => {}
"th" => {}
"td" => {}
// Inputs
"input" => {}
"label" => {}
_ => {}
}
let cur_layout = state.layouts.last_mut().unwrap();
let cur_block = state.block_stack.last_mut().unwrap();
let mut cur_style = TuiStyle::default();
for attr in el.attributes {
if attr.namespace == Some("style") {
match attr.name {
"width" => {}
"height" => {}
"background" => {
//
// cur_style.bg
// cur_block.style()
}
"background-color" => {}
"border" => {}
"border-bottom" => {}
"border-bottom-color" => {}
"border-bottom-style" => {}
"border-bottom-width" => {}
"border-color" => {}
"border-left" => {}
"border-left-color" => {}
"border-left-style" => {}
"border-left-width" => {}
"border-right" => {}
"border-right-color" => {}
"border-right-style" => {}
"border-right-width" => {}
"border-style" => {}
"border-top" => {}
"border-top-color" => {}
"border-top-style" => {}
"border-top-width" => {}
"border-width" => {}
"clear" => {}
"clip" => {}
"color" => {}
"cursor" => {}
"display" => {}
"filter" => {}
"float" => {}
"font" => {}
"font-family" => {}
"font-size" => {}
"font-variant" => {}
"font-weight" => {}
"left" => {}
"letter-spacing" => {}
"line-height" => {}
"list-style" => {}
"list-style-image" => {}
"list-style-position" => {}
"list-style-type" => {}
"margin" => {}
"margin-bottom" => {}
"margin-left" => {}
"margin-right" => {}
"margin-top" => {}
"overflow" => {}
"padding" => {}
"padding-bottom" => {}
"padding-left" => {}
"padding-right" => {}
"padding-top" => {}
"position" => {}
"stroke-dasharray" => {}
"stroke-dashoffset" => {}
"text-align" => {}
"text-decoration" => {}
"text-indent" => {}
"text-transform" => {}
"top" => {}
"vertical-align" => {}
"visibility" => {}
"z-index" => {}
"page-break-after"
| "page-break-before"
| "background-position"
| "background-attachment"
| "background-image"
| "background-repeat"
| _ => {}
}
}
}
for child in el.children {}
}
}
Rect::new(0, 0, 0, 0)
}
fn render_text(&self, f: &mut CrosstermFrame, node: &VNode) {}
fn render_fragment(&self, f: &mut CrosstermFrame) {}
}
impl<'a> tui_template::tuiapp::TuiApp for RinkDom {
fn render(&mut self, frame: &mut CrosstermFrame) {
let base_scope = self.vdom.base_scope();
let root = base_scope.root();
let mut render_state = RenderState::new();
self.render_vnode(frame, root, &mut render_state);
}
fn event_handler(&self, action: tui_template::crossterm::event::Event) -> Result<()> {
todo!()
}
fn handle_key(&mut self, key: tui_template::crossterm::event::KeyEvent) {
todo!()
}
fn tick(&mut self) {
todo!()
}
fn should_quit(&self) -> bool {
todo!()
}
}
struct RenderState<'a> {
block_stack: Vec<Block<'a>>,
layouts: Vec<Layout>,
/// All the current styles applied through the "style" tag
current_styles: Vec<Styles>,
}
// I don't think we can do any of these?
enum Styles {
Bold,
Italic,
Strikethrough,
Emphasis,
Underline,
}
impl<'a> RenderState<'a> {
fn new() -> Self {
let block_stack = Vec::new();
Self {
block_stack,
current_styles: Vec::new(),
layouts: Vec::new(),
}
}
}