add terminal

This commit is contained in:
Michael Sippel 2020-12-09 12:56:38 +01:00
parent bf56185ca1
commit 62f3a85736
Signed by: senvas
GPG key ID: F96CF119C34B64A6
5 changed files with 264 additions and 20 deletions

View file

@ -7,6 +7,7 @@ pub mod port;
pub mod channel;
pub mod singleton_buffer;
pub mod vec_buffer;
pub mod terminal;
use {
async_std::{
@ -20,7 +21,8 @@ use {
view::{View, Observer},
port::{InnerViewPort, OuterViewPort},
singleton_buffer::SingletonBuffer,
vec_buffer::VecBuffer
vec_buffer::VecBuffer,
terminal::{Terminal, TerminalAtom, TerminalStyle}
}
};
@ -30,50 +32,40 @@ async fn main() {
let mut buf = VecBuffer::new(digits.inner());
let digit_view = digits.outer()
// digit encoding
.map_value(
|digit|
if let Some(digit) = digit {
char::from_digit(digit, 16)
Some(TerminalAtom::new(char::from_digit(digit, 16).unwrap(), TerminalStyle::bg_color((100,30,30))))
} else {
None
}
)
// simple horizontal layout
.map_key(
|idx| Vector2::<i16>::new(idx as i16, 0),
|pos| pos.x as usize
);
let view = digit_view.get_view();
let mut stream = digit_view.stream().map({
move |idx| (idx, view.view(idx))
});
let fut = task::spawn({
async move {
while let Some((idx, val)) = stream.next().await {
println!("v[{:?}] = {:?}", idx, val);
}
println!("end print task");
}
});
let fut = task::spawn(Terminal::show(digit_view));
task::sleep(std::time::Duration::from_secs(1)).await;
buf.push(0);
buf.push(1);
buf.push(10);
task::sleep(std::time::Duration::from_secs(1)).await;
buf.push(2);
buf.push(3);
task::sleep(std::time::Duration::from_secs(1)).await;
buf.push(4);
task::sleep(std::time::Duration::from_secs(1)).await;
buf.insert(0, 15);
task::sleep(std::time::Duration::from_secs(1)).await;
buf.remove(2);
task::sleep(std::time::Duration::from_secs(1)).await;
drop(buf);
drop(digits);
drop(digit_view);
fut.await;
}

56
src/terminal/atom.rs Normal file
View file

@ -0,0 +1,56 @@
use super::TerminalStyle;
#[derive(Clone, Copy)]
pub struct TerminalAtom {
pub c: Option<char>,
pub style: TerminalStyle
}
impl TerminalAtom {
pub fn new(c: char, style: TerminalStyle) -> Self {
TerminalAtom { c: Some(c), style }
}
pub fn new_bg(bg_color: (u8, u8, u8)) -> Self {
TerminalAtom { c: None, style: TerminalStyle::bg_color(bg_color) }
}
pub fn add_style_front(mut self, style: TerminalStyle) -> Self {
self.style = self.style.add(style);
self
}
pub fn add_style_back(mut self, style: TerminalStyle) -> Self {
self.style = style.add(self.style);
self
}
}
impl From<char> for TerminalAtom {
fn from(c: char) -> Self {
TerminalAtom {
c: Some(c),
style: TerminalStyle::default()
}
}
}
impl From<Option<char>> for TerminalAtom {
fn from(c: Option<char>) -> Self {
TerminalAtom {
c,
style: TerminalStyle::default()
}
}
}
impl From<&char> for TerminalAtom {
fn from(c: &char) -> Self {
TerminalAtom {
c: Some(*c),
style: TerminalStyle::default()
}
}
}

10
src/terminal/mod.rs Normal file
View file

@ -0,0 +1,10 @@
pub mod style;
pub mod atom;
pub mod terminal;
pub use {
style::{TerminalStyle},
atom::{TerminalAtom},
terminal::{Terminal, TerminalEvent},
};

87
src/terminal/style.rs Normal file
View file

@ -0,0 +1,87 @@
#[derive(Default, Copy, Clone, PartialEq)]
pub struct TerminalStyle {
pub fg_color: Option<(u8, u8, u8)>,
pub bg_color: Option<(u8, u8, u8)>,
pub bold: Option<bool>,
pub italic: Option<bool>,
pub underline: Option<bool>
}
impl TerminalStyle {
pub fn add(&self, mut dominant: TerminalStyle) -> Self {
if dominant.fg_color == None {
dominant.fg_color = self.fg_color;
}
if dominant.bg_color == None {
dominant.bg_color = self.bg_color;
}
if dominant.bold == None {
dominant.bold = self.bold;
}
if dominant.italic == None {
dominant.italic = self.italic;
}
if dominant.underline == None {
dominant.underline = self.underline;
}
dominant
}
pub fn fg_color(rgb: (u8, u8, u8)) -> Self {
let mut style = TerminalStyle::default();
style.fg_color = Some(rgb);
style
}
pub fn bg_color(rgb: (u8, u8, u8)) -> Self {
let mut style = TerminalStyle::default();
style.bg_color = Some(rgb);
style
}
pub fn bold(b: bool) -> Self {
let mut style = TerminalStyle::default();
style.bold = Some(b);
style
}
pub fn italic(i: bool) -> Self {
let mut style = TerminalStyle::default();
style.italic = Some(i);
style
}
pub fn underline(u: bool) -> Self {
let mut style = TerminalStyle::default();
style.underline = Some(u);
style
}
}
impl std::fmt::Display for TerminalStyle {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
match self.fg_color {
Some((r, g, b)) => write!(fmt, "{}", termion::color::Fg(termion::color::Rgb(r, g, b)))?,
None => write!(fmt, "{}", termion::color::Fg(termion::color::Reset))?,
};
match self.bg_color {
Some((r, g, b)) => write!(fmt, "{}", termion::color::Bg(termion::color::Rgb(r, g, b)))?,
None => write!(fmt, "{}", termion::color::Bg(termion::color::Reset))?,
};
match self.bold {
Some(true) => write!(fmt, "{}", termion::style::Bold)?,
_ => write!(fmt, "{}", termion::style::NoBold)?,
};
match self.italic {
Some(true) => write!(fmt, "{}", termion::style::Italic)?,
_ => write!(fmt, "{}", termion::style::NoItalic)?,
};
match self.underline {
Some(true) => write!(fmt, "{}", termion::style::Underline)?,
_ => write!(fmt, "{}", termion::style::NoUnderline)?,
};
Ok(())
}
}

99
src/terminal/terminal.rs Normal file
View file

@ -0,0 +1,99 @@
use {
std::io::{Write, stdout, stdin},
async_std::stream::{Stream, StreamExt},
cgmath::Vector2,
termion::{
raw::IntoRawMode,
input::{TermRead, MouseTerminal}
},
super::{TerminalAtom, TerminalStyle},
crate::{
view::{View, Observer},
port::{OuterViewPort},
channel::ChannelReceiver
}
};
pub enum TerminalEvent {
Resize((u16, u16)),
Input(termion::event::Event)
}
pub struct Terminal {
events: ChannelReceiver<Vec<TerminalEvent>>
}
impl Terminal {
pub fn new() -> Self {
let (event_tx, event_rx) = crate::channel::queue_channel();
let input_tx = event_tx.clone();
std::thread::spawn(move || {
for event in stdin().events() {
input_tx.notify(TerminalEvent::Input(event.unwrap()));
}
});
/*
let mut resize_stream = signal(tokio::signal::unix::SignalKind::window_change()).unwrap();
let resize_tx = event_tx.clone();
tokio::spawn(async move {
loop {
resize_stream.recv().await;
resize_tx.send(TerminalEvent::Resize(termion::terminal_size().unwrap()));
}
});
*/
Terminal {
events: event_rx
}
}
pub async fn show(view_port: OuterViewPort<Vector2<i16>, TerminalAtom>) -> std::io::Result<()> {
let (atom_tx, atom_rx) = crate::channel::queue_channel();
let view = view_port.get_view();
view_port.add_observer_fn(move |pos| atom_tx.notify((pos, view.view(pos))));
Self::show_stream(atom_rx).await
}
pub async fn show_stream(recv: ChannelReceiver<Vec<(Vector2<i16>, Option<TerminalAtom>)>>) -> std::io::Result<()> {
let mut out = MouseTerminal::from(stdout().into_raw_mode().unwrap());
let mut cur_pos = Vector2::<i16>::new(0, 0);
let mut cur_style = TerminalStyle::default();
write!(out, "{}{}{}{}",
termion::clear::All,
termion::cursor::Goto(1, 1),
termion::cursor::Hide,
termion::cursor::Goto(1, 1))?;
while let Some(atoms) = recv.recv().await {
for (pos, atom) in atoms.into_iter() {
if pos != cur_pos {
write!(out, "{}", termion::cursor::Goto(pos.x as u16 + 1, pos.y as u16 + 1))?;
}
cur_pos = pos;
if let Some(atom) = atom {
if cur_style != atom.style {
cur_style = atom.style;
write!(out, "{}", atom.style)?;
}
write!(out, "{}", atom.c.unwrap_or(' '))?;
} else {
write!(out, "{} ", termion::style::Reset);
}
}
out.flush()?;
}
write!(out, "{}", termion::cursor::Show)?;
out.flush()?;
std::io::Result::Ok(())
}
}