This commit is contained in:
Evan Almloff 2022-02-04 20:19:19 -06:00
commit dd60c9ce3c
8 changed files with 103 additions and 108 deletions

View file

@ -1,12 +1,67 @@
# Rink: Like "Ink" but for Rust and Dioxus
<div align="center">
<h1>Rink</h1>
<p>
<strong>Beautiful terminal user interfaces in Rust with <a href="https://dioxuslabs.com/">Dioxus </a>.</strong>
</p>
</div>
The fastest portable TUIs in the west
🔫🤠🔫
🐎🔥🔥🔥
<div align="center">
<!-- Crates version -->
<a href="https://crates.io/crates/dioxus">
<img src="https://img.shields.io/crates/v/dioxus.svg?style=flat-square"
alt="Crates.io version" />
</a>
<!-- Downloads -->
<a href="https://crates.io/crates/dioxus">
<img src="https://img.shields.io/crates/d/dioxus.svg?style=flat-square"
alt="Download" />
</a>
<!-- docs -->
<a href="https://docs.rs/dioxus">
<img src="https://img.shields.io/badge/docs-latest-blue.svg?style=flat-square"
alt="docs.rs docs" />
</a>
<!-- CI -->
<a href="https://github.com/jkelleyrtp/dioxus/actions">
<img src="https://github.com/dioxuslabs/dioxus/actions/workflows/main.yml/badge.svg"
alt="CI status" />
</a>
Rink lets you build terminal user interfaces in Rust with [`Dioxus`](https://dioxuslabs.com/).
<!--Awesome -->
<a href="https://github.com/dioxuslabs/awesome-dioxus">
<img src="https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg" alt="Awesome Page" />
</a>
<!-- Discord -->
<a href="https://discord.gg/XgGxMSkvUM">
<img src="https://img.shields.io/discord/899851952891002890.svg?logo=discord&style=flat-square" alt="Discord Link" />
</a>
</div>
You can use Html-like semantics with stylesheets, inline styles, tree hierarchy, components, and more in your [`text-based user interface (TUI)`](https://en.wikipedia.org/wiki/Text-based_user_interface) application.
<br/>
Leverage React-like patterns, CSS, HTML, and Rust to build beautiful, portable, terminal user interfaces with Dioxus.
```rust
fn app(cx: Scope) -> Element {
cx.render(rsx!{
div {
width: "100%",
height: "10px",
background_color: "red",
justify_content: "center",
align_items: "center",
"Hello world!"
}
})
}
```
![demo app](examples/example.png)
## Background
You can use Html-like semantics with stylesheets, inline styles, tree hierarchy, components, and more in your [`text-based user interface (TUI)`](https://en.wikipedia.org/wiki/Text-based_user_interface) application.
Rink is basically a port of [Ink](https://github.com/vadimdemedes/ink) but for [`Rust`](https://www.rust-lang.org/) and [`Dioxus`](https://dioxuslabs.com/). Rink doesn't depend on Node.js or any other JavaScript runtime, so your binaries are portable and beautiful.
@ -17,27 +72,6 @@ Terminals can only render a subset of HTML. We support as much as we can.
- **Particular frontend design**
Terminals and browsers are and look different. Therefore, the same design might not be the best to cover both renderers.
## Example
Let's print `Hello world!` in the center of the screen.
```rust
static App: FC<()> = |cx| {
cx.render(rsx!{
div {
width: "100%",
height: "10px",
background_color: "red",
justify_content: "center",
align_items: "center",
"Hello world!"
}
})
}
```
![demo app](examples/example.png)
## Status

View file

@ -7,10 +7,10 @@ fn main() {
#[derive(Props, PartialEq)]
struct QuadrentProps {
color: String,
text: String
text: String,
}
fn Quadrent(cx: Scope<QuadrentProps>) -> Element{
fn Quadrent(cx: Scope<QuadrentProps>) -> Element {
cx.render(rsx! {
div {
border_width: "1px",

View file

@ -1,8 +1,5 @@
use std::cell::RefCell;
use crossterm::event::{KeyCode, KeyEvent, MouseEvent};
use dioxus::prelude::*;
use rink::InputHandler;
fn main() {
rink::launch(app);

View file

@ -1,7 +1,6 @@
use std::collections::HashMap;
use dioxus::{core::ElementId, prelude::*};
use rink::TuiNode;
use dioxus::prelude::*;
fn main() {
let mut dom = VirtualDom::new(app);
@ -19,7 +18,7 @@ fn main() {
.compute_layout(node.layout, stretch2::geometry::Size::undefined())
.unwrap();
for (id, node) in nodes.drain() {
for (_id, node) in nodes.drain() {
println!("{:?}", layout.layout(node.layout));
}
}

View file

@ -343,7 +343,7 @@ fn apply_overflow(name: &str, value: &str, style: &mut StyleModifer) {
}
}
fn apply_display(name: &str, value: &str, style: &mut StyleModifer) {
fn apply_display(_name: &str, value: &str, style: &mut StyleModifer) {
use stretch2::style::Display;
style.style.display = match value {
"flex" => Display::Flex,
@ -387,29 +387,28 @@ fn apply_background(name: &str, value: &str, style: &mut StyleModifer) {
"white" => style.tui_style.bg.replace(Color::White),
"black" => style.tui_style.bg.replace(Color::Black),
_ => {
if value.len() == 7{
if value.len() == 7 {
let mut values = [0, 0, 0];
let mut color_ok = true;
for i in 0..values.len(){
if let Ok(v) = u8::from_str_radix(&value[(1+2*i)..(1+2*(i+1))], 16){
for i in 0..values.len() {
if let Ok(v) =
u8::from_str_radix(&value[(1 + 2 * i)..(1 + 2 * (i + 1))], 16)
{
values[i] = v;
}
else{
} else {
color_ok = false;
}
}
if color_ok{
if color_ok {
let color = Color::Rgb(values[0], values[1], values[2]);
style.tui_style.bg.replace(color)
}
else{
} else {
None
}
}
else{
} else {
None
}
},
}
};
}
"background" => {}
@ -424,7 +423,7 @@ fn apply_background(name: &str, value: &str, style: &mut StyleModifer) {
}
}
fn apply_border(name: &str, value: &str, style: &mut StyleModifer) {
fn apply_border(name: &str, value: &str, _style: &mut StyleModifer) {
match name {
"border" => {}
"border-bottom" => {}
@ -459,7 +458,7 @@ fn apply_border(name: &str, value: &str, style: &mut StyleModifer) {
"border-top-style" => {}
"border-top-width" => {}
"border-width" => {
if let Ok(px) = value.trim_end_matches("px").parse::<f32>() {
if let Ok(_px) = value.trim_end_matches("px").parse::<f32>() {
// tuistyle = px;
}
}
@ -467,7 +466,7 @@ fn apply_border(name: &str, value: &str, style: &mut StyleModifer) {
}
}
fn apply_animation(name: &str, value: &str, style: &mut StyleModifer) {
fn apply_animation(name: &str, _value: &str, _style: &mut StyleModifer) {
match name {
"animation" => {}
"animation-delay" => {}
@ -482,7 +481,7 @@ fn apply_animation(name: &str, value: &str, style: &mut StyleModifer) {
}
}
fn apply_column(name: &str, value: &str, style: &mut StyleModifer) {
fn apply_column(name: &str, _value: &str, _style: &mut StyleModifer) {
match name {
"column-count" => {}
"column-fill" => {}
@ -551,7 +550,7 @@ fn apply_flex(name: &str, value: &str, style: &mut StyleModifer) {
}
}
fn apply_font(name: &str, value: &str, style: &mut StyleModifer) {
fn apply_font(_name: &str, _value: &str, _style: &mut StyleModifer) {
todo!()
}
@ -588,15 +587,15 @@ fn apply_padding(name: &str, value: &str, style: &mut StyleModifer) {
}
}
fn apply_text(name: &str, value: &str, style: &mut StyleModifer) {
fn apply_text(_name: &str, _value: &str, _style: &mut StyleModifer) {
todo!()
}
fn apply_transform(name: &str, value: &str, style: &mut StyleModifer) {
fn apply_transform(_name: &str, _value: &str, _style: &mut StyleModifer) {
todo!()
}
fn apply_transition(name: &str, value: &str, style: &mut StyleModifer) {
fn apply_transition(_name: &str, _value: &str, _style: &mut StyleModifer) {
todo!()
}
@ -640,7 +639,7 @@ fn apply_align(name: &str, value: &str, style: &mut StyleModifer) {
}
}
pub fn apply_size(name: &str, value: &str, style: &mut StyleModifer) {
pub fn apply_size(_name: &str, _value: &str, _style: &mut StyleModifer) {
//
}

View file

@ -1,24 +1,12 @@
use anyhow::Result;
use crossterm::{
event::{
DisableMouseCapture, EnableMouseCapture, Event as TermEvent, KeyCode, KeyEvent, MouseEvent,
},
execute,
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
};
use dioxus::{core::exports::futures_channel::mpsc::unbounded, prelude::Props};
use dioxus::{core::*, prelude::*};
use futures::{channel::mpsc::UnboundedReceiver, future::Either, pin_mut, StreamExt};
use crossterm::event::{Event as TermEvent, KeyEvent, MouseEvent};
use dioxus::core::*;
use dioxus::prelude::Props;
use futures::{channel::mpsc::UnboundedReceiver, StreamExt};
use std::{
cell::{Cell, RefCell},
collections::HashMap,
io,
rc::Rc,
time::{Duration, Instant},
};
use stretch2::{prelude::Size, Stretch};
use tokio::sync::broadcast::Receiver;
use tui::{backend::CrosstermBackend, style::Style as TuiStyle, Terminal};
pub struct RinkContext {
last_event: Rc<Cell<Option<TermEvent>>>,
@ -44,8 +32,8 @@ impl RinkContext {
});
Self {
last_event: last_event,
subscribers: subscribers,
last_event,
subscribers,
}
}

View file

@ -1,21 +1,15 @@
use anyhow::Result;
use crossterm::{
event::{DisableMouseCapture, EnableMouseCapture, Event as TermEvent, KeyCode, KeyEvent},
event::{DisableMouseCapture, EnableMouseCapture, Event as TermEvent, KeyCode, KeyModifiers},
execute,
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
};
use dioxus::{core::exports::futures_channel::mpsc::unbounded, prelude::Props};
use dioxus::{core::*, prelude::*};
use futures::{
channel::mpsc::{UnboundedReceiver, UnboundedSender},
future::Either,
pin_mut, StreamExt,
};
use dioxus::core::exports::futures_channel::mpsc::unbounded;
use dioxus::core::*;
use futures::{channel::mpsc::UnboundedSender, pin_mut, StreamExt};
use std::{
cell::Cell,
collections::HashMap,
io,
rc::Rc,
time::{Duration, Instant},
};
use stretch2::{prelude::Size, Stretch};
@ -137,17 +131,17 @@ pub fn render_vdom(vdom: &mut VirtualDom, ctx: UnboundedSender<TermEvent>) -> Re
pin_mut!(wait);
match select(wait, rx.next()).await {
Either::Left((a, b)) => {
Either::Left((_a, _b)) => {
//
}
Either::Right((evt, o)) => {
//
Either::Right((evt, _o)) => {
match evt.as_ref().unwrap() {
InputEvent::UserInput(event) => match event {
TermEvent::Key(key) => {
match key.code {
KeyCode::Char('q') => break,
_ => {} // handle event
if matches!(key.code, KeyCode::Char('c'))
&& key.modifiers.contains(KeyModifiers::CONTROL)
{
break;
}
}
TermEvent::Resize(_, _) | TermEvent::Mouse(_) => {}

View file

@ -1,22 +1,8 @@
use anyhow::Result;
use crossterm::{
event,
event::{DisableMouseCapture, EnableMouseCapture, Event as TermEvent, KeyCode, KeyEvent},
execute,
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
};
use dioxus::core::*;
use std::{
collections::HashMap,
io::{self, Stdout},
sync::mpsc,
thread,
time::{Duration, Instant},
};
use std::{collections::HashMap, io::Stdout};
use stretch2::{
geometry::Point,
prelude::{Layout, Size},
style::Style as StretchStyle,
Stretch,
};
use tui::{
@ -24,9 +10,7 @@ use tui::{
buffer::Buffer,
layout::Rect,
style::Style as TuiStyle,
text::Span,
widgets::{canvas::Label, Block, BorderType, Borders, Widget},
Terminal,
widgets::{Block, Widget},
};
use crate::TuiNode;