add terminal
This commit is contained in:
parent
bf56185ca1
commit
62f3a85736
5 changed files with 264 additions and 20 deletions
32
src/main.rs
32
src/main.rs
|
@ -7,6 +7,7 @@ pub mod port;
|
||||||
pub mod channel;
|
pub mod channel;
|
||||||
pub mod singleton_buffer;
|
pub mod singleton_buffer;
|
||||||
pub mod vec_buffer;
|
pub mod vec_buffer;
|
||||||
|
pub mod terminal;
|
||||||
|
|
||||||
use {
|
use {
|
||||||
async_std::{
|
async_std::{
|
||||||
|
@ -20,7 +21,8 @@ use {
|
||||||
view::{View, Observer},
|
view::{View, Observer},
|
||||||
port::{InnerViewPort, OuterViewPort},
|
port::{InnerViewPort, OuterViewPort},
|
||||||
singleton_buffer::SingletonBuffer,
|
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 mut buf = VecBuffer::new(digits.inner());
|
||||||
|
|
||||||
let digit_view = digits.outer()
|
let digit_view = digits.outer()
|
||||||
|
|
||||||
// digit encoding
|
// digit encoding
|
||||||
.map_value(
|
.map_value(
|
||||||
|digit|
|
|digit|
|
||||||
if let Some(digit) = 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 {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// simple horizontal layout
|
// simple horizontal layout
|
||||||
.map_key(
|
.map_key(
|
||||||
|idx| Vector2::<i16>::new(idx as i16, 0),
|
|idx| Vector2::<i16>::new(idx as i16, 0),
|
||||||
|pos| pos.x as usize
|
|pos| pos.x as usize
|
||||||
);
|
);
|
||||||
|
|
||||||
let view = digit_view.get_view();
|
let fut = task::spawn(Terminal::show(digit_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");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
task::sleep(std::time::Duration::from_secs(1)).await;
|
||||||
buf.push(0);
|
buf.push(0);
|
||||||
buf.push(1);
|
buf.push(10);
|
||||||
task::sleep(std::time::Duration::from_secs(1)).await;
|
task::sleep(std::time::Duration::from_secs(1)).await;
|
||||||
buf.push(2);
|
buf.push(2);
|
||||||
buf.push(3);
|
buf.push(3);
|
||||||
task::sleep(std::time::Duration::from_secs(1)).await;
|
task::sleep(std::time::Duration::from_secs(1)).await;
|
||||||
buf.push(4);
|
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(buf);
|
||||||
drop(digits);
|
drop(digits);
|
||||||
drop(digit_view);
|
|
||||||
|
|
||||||
fut.await;
|
fut.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
56
src/terminal/atom.rs
Normal file
56
src/terminal/atom.rs
Normal 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
10
src/terminal/mod.rs
Normal 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
87
src/terminal/style.rs
Normal 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
99
src/terminal/terminal.rs
Normal 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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue