diff --git a/src/main.rs b/src/main.rs index 4593455..dbfdeb6 100644 --- a/src/main.rs +++ b/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::::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; } - diff --git a/src/terminal/atom.rs b/src/terminal/atom.rs new file mode 100644 index 0000000..1cf48e9 --- /dev/null +++ b/src/terminal/atom.rs @@ -0,0 +1,56 @@ + +use super::TerminalStyle; + +#[derive(Clone, Copy)] +pub struct TerminalAtom { + pub c: Option, + 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 for TerminalAtom { + fn from(c: char) -> Self { + TerminalAtom { + c: Some(c), + style: TerminalStyle::default() + } + } +} + +impl From> for TerminalAtom { + fn from(c: Option) -> Self { + TerminalAtom { + c, + style: TerminalStyle::default() + } + } +} + +impl From<&char> for TerminalAtom { + fn from(c: &char) -> Self { + TerminalAtom { + c: Some(*c), + style: TerminalStyle::default() + } + } +} + diff --git a/src/terminal/mod.rs b/src/terminal/mod.rs new file mode 100644 index 0000000..55daf75 --- /dev/null +++ b/src/terminal/mod.rs @@ -0,0 +1,10 @@ +pub mod style; +pub mod atom; +pub mod terminal; + +pub use { + style::{TerminalStyle}, + atom::{TerminalAtom}, + terminal::{Terminal, TerminalEvent}, +}; + diff --git a/src/terminal/style.rs b/src/terminal/style.rs new file mode 100644 index 0000000..3e9d6eb --- /dev/null +++ b/src/terminal/style.rs @@ -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, + pub italic: Option, + pub underline: Option +} + +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(()) + } +} + diff --git a/src/terminal/terminal.rs b/src/terminal/terminal.rs new file mode 100644 index 0000000..f96457d --- /dev/null +++ b/src/terminal/terminal.rs @@ -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> +} + +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, 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, Option)>>) -> std::io::Result<()> { + let mut out = MouseTerminal::from(stdout().into_raw_mode().unwrap()); + let mut cur_pos = Vector2::::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(()) + } +} +