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 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
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