mirror of
https://github.com/ratatui-org/ratatui
synced 2024-11-10 07:04:17 +00:00
Convert backends to conditionnal features for compilation
This commit is contained in:
parent
d00c89ac48
commit
feefa5d54f
22 changed files with 361 additions and 322 deletions
11
Cargo.toml
11
Cargo.toml
|
@ -3,21 +3,24 @@ name = "tui"
|
|||
version = "0.1.0"
|
||||
authors = ["Florian Dehau <work@fdehau.com>"]
|
||||
description = """
|
||||
A library to build rich user interfaces or dashboard for terminals
|
||||
A library to build rich terminal user interfaces or dashboards
|
||||
"""
|
||||
keywords = ["tui", "terminal"]
|
||||
keywords = ["tui", "terminal", "dashboard"]
|
||||
repository = "https://github.com/fdehau/tui-rs"
|
||||
license = "MIT"
|
||||
exclude = ["docs", ".travis.yml"]
|
||||
|
||||
[features]
|
||||
default = ["rustbox", "termion"]
|
||||
|
||||
[dependencies]
|
||||
termion = "1.1.1"
|
||||
rustbox = "0.9.0"
|
||||
bitflags = "0.7"
|
||||
cassowary = "0.2.0"
|
||||
log = "0.3"
|
||||
unicode-segmentation = "0.1.2"
|
||||
unicode-width = "0.1.3"
|
||||
termion = { version = "1.1.1", optional = true }
|
||||
rustbox = { version = "0.9.0", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
log4rs = "0.5.0"
|
||||
|
|
|
@ -9,7 +9,8 @@ use std::sync::mpsc;
|
|||
use termion::event;
|
||||
use termion::input::TermRead;
|
||||
|
||||
use tui::{Terminal, TermionBackend};
|
||||
use tui::Terminal;
|
||||
use tui::backend::TermionBackend;
|
||||
use tui::widgets::{Widget, Block, border, BarChart};
|
||||
use tui::layout::{Group, Direction, Size};
|
||||
use tui::style::{Style, Color, Modifier};
|
||||
|
|
|
@ -5,7 +5,8 @@ use std::io;
|
|||
use termion::event;
|
||||
use termion::input::TermRead;
|
||||
|
||||
use tui::{Terminal, TermionBackend};
|
||||
use tui::Terminal;
|
||||
use tui::backend::TermionBackend;
|
||||
use tui::widgets::{Widget, Block, border};
|
||||
use tui::layout::{Group, Direction, Size};
|
||||
use tui::style::{Style, Color, Modifier};
|
||||
|
|
|
@ -9,7 +9,8 @@ use std::sync::mpsc;
|
|||
use termion::event;
|
||||
use termion::input::TermRead;
|
||||
|
||||
use tui::{Terminal, TermionBackend};
|
||||
use tui::Terminal;
|
||||
use tui::backend::TermionBackend;
|
||||
use tui::widgets::{Widget, Block, border};
|
||||
use tui::widgets::canvas::{Canvas, Map, MapResolution, Line};
|
||||
use tui::layout::{Group, Rect, Direction, Size};
|
||||
|
|
|
@ -12,7 +12,8 @@ use std::sync::mpsc;
|
|||
use termion::event;
|
||||
use termion::input::TermRead;
|
||||
|
||||
use tui::{Terminal, TermionBackend};
|
||||
use tui::Terminal;
|
||||
use tui::backend::TermionBackend;
|
||||
use tui::widgets::{Widget, Block, border, Chart, Axis, Marker, Dataset};
|
||||
use tui::style::{Style, Color, Modifier};
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
extern crate tui;
|
||||
|
||||
use tui::{Terminal, TermionBackend};
|
||||
use tui::Terminal;
|
||||
use tui::backend::TermionBackend;
|
||||
use tui::widgets::Widget;
|
||||
use tui::buffer::Buffer;
|
||||
use tui::layout::Rect;
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
extern crate log;
|
||||
|
||||
extern crate tui;
|
||||
#[macro_use]
|
||||
extern crate termion;
|
||||
|
||||
mod util;
|
||||
|
@ -16,7 +15,8 @@ use std::sync::mpsc;
|
|||
use termion::event;
|
||||
use termion::input::TermRead;
|
||||
|
||||
use tui::{Terminal, TermionBackend};
|
||||
use tui::Terminal;
|
||||
use tui::backend::TermionBackend;
|
||||
use tui::widgets::{Widget, Block, SelectableList, List, Gauge, Sparkline, Paragraph, border,
|
||||
Chart, Axis, Dataset, BarChart, Marker, Tabs, Table};
|
||||
use tui::widgets::canvas::{Canvas, Map, MapResolution, Line};
|
||||
|
|
|
@ -9,7 +9,8 @@ use std::sync::mpsc;
|
|||
use termion::event;
|
||||
use termion::input::TermRead;
|
||||
|
||||
use tui::{Terminal, TermionBackend};
|
||||
use tui::Terminal;
|
||||
use tui::backend::TermionBackend;
|
||||
use tui::widgets::{Widget, Block, border, Gauge};
|
||||
use tui::layout::{Group, Direction, Size};
|
||||
use tui::style::{Style, Color, Modifier};
|
||||
|
|
|
@ -9,7 +9,8 @@ use std::sync::mpsc;
|
|||
use termion::event;
|
||||
use termion::input::TermRead;
|
||||
|
||||
use tui::{Terminal, TermionBackend};
|
||||
use tui::Terminal;
|
||||
use tui::backend::TermionBackend;
|
||||
use tui::widgets::{Widget, Block, border, SelectableList, List};
|
||||
use tui::layout::{Group, Direction, Size};
|
||||
use tui::style::{Style, Color, Modifier};
|
||||
|
|
|
@ -5,7 +5,8 @@ use std::io;
|
|||
use termion::event;
|
||||
use termion::input::TermRead;
|
||||
|
||||
use tui::{Terminal, TermionBackend};
|
||||
use tui::Terminal;
|
||||
use tui::backend::TermionBackend;
|
||||
use tui::widgets::{Widget, Block, Paragraph};
|
||||
use tui::layout::{Group, Direction, Size};
|
||||
use tui::style::{Style, Color};
|
||||
|
|
|
@ -4,7 +4,8 @@ extern crate rustbox;
|
|||
use std::error::Error;
|
||||
use rustbox::Key;
|
||||
|
||||
use tui::{Terminal, RustboxBackend};
|
||||
use tui::Terminal;
|
||||
use tui::backend::RustboxBackend;
|
||||
use tui::widgets::{Widget, Block, border, Paragraph};
|
||||
use tui::layout::{Group, Direction, Size};
|
||||
use tui::style::{Style, Color, Modifier};
|
||||
|
|
|
@ -12,7 +12,8 @@ use std::sync::mpsc;
|
|||
use termion::event;
|
||||
use termion::input::TermRead;
|
||||
|
||||
use tui::{Terminal, TermionBackend};
|
||||
use tui::Terminal;
|
||||
use tui::backend::TermionBackend;
|
||||
use tui::widgets::{Widget, Block, border, Sparkline};
|
||||
use tui::layout::{Group, Direction, Size};
|
||||
use tui::style::{Style, Color};
|
||||
|
|
|
@ -6,7 +6,8 @@ use std::io;
|
|||
use termion::event;
|
||||
use termion::input::TermRead;
|
||||
|
||||
use tui::{Terminal, TermionBackend};
|
||||
use tui::Terminal;
|
||||
use tui::backend::TermionBackend;
|
||||
use tui::widgets::{Widget, Block, border, Table};
|
||||
use tui::layout::{Group, Direction, Size};
|
||||
use tui::style::{Style, Color, Modifier};
|
||||
|
|
|
@ -8,7 +8,8 @@ use std::io;
|
|||
use termion::event;
|
||||
use termion::input::TermRead;
|
||||
|
||||
use tui::{Terminal, TermionBackend};
|
||||
use tui::Terminal;
|
||||
use tui::backend::TermionBackend;
|
||||
use tui::widgets::{Widget, Block, border, Tabs};
|
||||
use tui::layout::{Group, Direction, Size};
|
||||
use tui::style::{Style, Color};
|
||||
|
|
24
src/backend/mod.rs
Normal file
24
src/backend/mod.rs
Normal file
|
@ -0,0 +1,24 @@
|
|||
use std::io;
|
||||
|
||||
use buffer::Cell;
|
||||
use layout::Rect;
|
||||
|
||||
#[cfg(feature = "rustbox")]
|
||||
mod rustbox;
|
||||
#[cfg(feature = "rustbox")]
|
||||
pub use self::rustbox::RustboxBackend;
|
||||
|
||||
#[cfg(feature = "termion")]
|
||||
mod termion;
|
||||
#[cfg(feature = "termion")]
|
||||
pub use self::termion::TermionBackend;
|
||||
|
||||
pub trait Backend {
|
||||
fn draw<'a, I>(&mut self, content: I) -> Result<(), io::Error>
|
||||
where I: Iterator<Item = (u16, u16, &'a Cell)>;
|
||||
fn hide_cursor(&mut self) -> Result<(), io::Error>;
|
||||
fn show_cursor(&mut self) -> Result<(), io::Error>;
|
||||
fn clear(&mut self) -> Result<(), io::Error>;
|
||||
fn size(&self) -> Result<Rect, io::Error>;
|
||||
fn flush(&mut self) -> Result<(), io::Error>;
|
||||
}
|
99
src/backend/rustbox.rs
Normal file
99
src/backend/rustbox.rs
Normal file
|
@ -0,0 +1,99 @@
|
|||
extern crate rustbox;
|
||||
|
||||
use std::io;
|
||||
|
||||
use super::Backend;
|
||||
use buffer::Cell;
|
||||
use layout::Rect;
|
||||
use style::{Color, Modifier};
|
||||
|
||||
pub struct RustboxBackend {
|
||||
rustbox: rustbox::RustBox,
|
||||
}
|
||||
|
||||
impl RustboxBackend {
|
||||
pub fn new() -> Result<RustboxBackend, rustbox::InitError> {
|
||||
let rustbox = try!(rustbox::RustBox::init(Default::default()));
|
||||
Ok(RustboxBackend { rustbox: rustbox })
|
||||
}
|
||||
|
||||
pub fn with_rustbox(instance: rustbox::RustBox) -> RustboxBackend {
|
||||
RustboxBackend { rustbox: instance }
|
||||
}
|
||||
|
||||
pub fn rustbox(&self) -> &rustbox::RustBox {
|
||||
&self.rustbox
|
||||
}
|
||||
}
|
||||
|
||||
impl Backend for RustboxBackend {
|
||||
fn draw<'a, I>(&mut self, content: I) -> Result<(), io::Error>
|
||||
where I: Iterator<Item = (u16, u16, &'a Cell)>
|
||||
{
|
||||
let mut inst = 0;
|
||||
for (x, y, cell) in content {
|
||||
inst += 1;
|
||||
self.rustbox.print(x as usize,
|
||||
y as usize,
|
||||
cell.style.modifier.into(),
|
||||
cell.style.fg.into(),
|
||||
cell.style.bg.into(),
|
||||
&cell.symbol);
|
||||
}
|
||||
debug!("{} instructions outputed", inst);
|
||||
Ok(())
|
||||
}
|
||||
fn hide_cursor(&mut self) -> Result<(), io::Error> {
|
||||
Ok(())
|
||||
}
|
||||
fn show_cursor(&mut self) -> Result<(), io::Error> {
|
||||
Ok(())
|
||||
}
|
||||
fn clear(&mut self) -> Result<(), io::Error> {
|
||||
self.rustbox.clear();
|
||||
Ok(())
|
||||
}
|
||||
fn size(&self) -> Result<Rect, io::Error> {
|
||||
Ok((Rect {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: self.rustbox.width() as u16,
|
||||
height: self.rustbox.height() as u16,
|
||||
}))
|
||||
}
|
||||
fn flush(&mut self) -> Result<(), io::Error> {
|
||||
self.rustbox.present();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn rgb_to_byte(r: u8, g: u8, b: u8) -> u16 {
|
||||
((((r & 255 & 0xC0) + ((g & 255 & 0xE0) >> 2) + ((b & 0xE0) >> 5))) & 0xFF) as u16
|
||||
}
|
||||
|
||||
impl Into<rustbox::Color> for Color {
|
||||
fn into(self) -> rustbox::Color {
|
||||
match self {
|
||||
Color::Reset => rustbox::Color::Default,
|
||||
Color::Black | Color::Gray | Color::DarkGray => rustbox::Color::Black,
|
||||
Color::Red | Color::LightRed => rustbox::Color::Red,
|
||||
Color::Green | Color::LightGreen => rustbox::Color::Green,
|
||||
Color::Yellow | Color::LightYellow => rustbox::Color::Yellow,
|
||||
Color::Magenta | Color::LightMagenta => rustbox::Color::Magenta,
|
||||
Color::Cyan | Color::LightCyan => rustbox::Color::Cyan,
|
||||
Color::White => rustbox::Color::White,
|
||||
Color::Rgb(r, g, b) => rustbox::Color::Byte(rgb_to_byte(r, g, b)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<rustbox::Style> for Modifier {
|
||||
fn into(self) -> rustbox::Style {
|
||||
match self {
|
||||
Modifier::Bold => rustbox::RB_BOLD,
|
||||
Modifier::Underline => rustbox::RB_UNDERLINE,
|
||||
Modifier::Invert => rustbox::RB_REVERSE,
|
||||
_ => rustbox::RB_NORMAL,
|
||||
}
|
||||
}
|
||||
}
|
195
src/backend/termion.rs
Normal file
195
src/backend/termion.rs
Normal file
|
@ -0,0 +1,195 @@
|
|||
extern crate termion;
|
||||
|
||||
use std::io;
|
||||
use std::io::Write;
|
||||
|
||||
use self::termion::raw::{IntoRawMode, RawTerminal};
|
||||
|
||||
use super::Backend;
|
||||
use buffer::Cell;
|
||||
use layout::Rect;
|
||||
use style::{Style, Color, Modifier};
|
||||
|
||||
pub struct TermionBackend {
|
||||
stdout: RawTerminal<io::Stdout>,
|
||||
}
|
||||
|
||||
impl TermionBackend {
|
||||
pub fn new() -> Result<TermionBackend, io::Error> {
|
||||
let stdout = try!(io::stdout().into_raw_mode());
|
||||
Ok(TermionBackend { stdout: stdout })
|
||||
}
|
||||
}
|
||||
|
||||
impl Backend for TermionBackend {
|
||||
/// Clears the entire screen and move the cursor to the top left of the screen
|
||||
fn clear(&mut self) -> Result<(), io::Error> {
|
||||
write!(self.stdout, "{}", termion::clear::All)?;
|
||||
write!(self.stdout, "{}", termion::cursor::Goto(1, 1))?;
|
||||
self.stdout.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Hides cursor
|
||||
fn hide_cursor(&mut self) -> Result<(), io::Error> {
|
||||
write!(self.stdout, "{}", termion::cursor::Hide)?;
|
||||
self.stdout.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Shows cursor
|
||||
fn show_cursor(&mut self) -> Result<(), io::Error> {
|
||||
write!(self.stdout, "{}", termion::cursor::Show)?;
|
||||
self.stdout.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn draw<'a, I>(&mut self, content: I) -> Result<(), io::Error>
|
||||
where I: Iterator<Item = (u16, u16, &'a Cell)>
|
||||
{
|
||||
let mut string = String::with_capacity(content.size_hint().0 * 3);
|
||||
let mut style = Style::default();
|
||||
let mut last_y = 0;
|
||||
let mut last_x = 0;
|
||||
let mut inst = 0;
|
||||
for (x, y, cell) in content {
|
||||
if y != last_y || x != last_x + 1 {
|
||||
string.push_str(&format!("{}", termion::cursor::Goto(x + 1, y + 1)));
|
||||
inst += 1;
|
||||
}
|
||||
last_x = x;
|
||||
last_y = y;
|
||||
if cell.style.modifier != style.modifier {
|
||||
string.push_str(&cell.style.modifier.termion_modifier());
|
||||
style.modifier = cell.style.modifier;
|
||||
if style.modifier == Modifier::Reset {
|
||||
style.bg = Color::Reset;
|
||||
style.fg = Color::Reset;
|
||||
}
|
||||
inst += 1;
|
||||
}
|
||||
if cell.style.fg != style.fg {
|
||||
string.push_str(&cell.style.fg.termion_fg());
|
||||
style.fg = cell.style.fg;
|
||||
inst += 1;
|
||||
}
|
||||
if cell.style.bg != style.bg {
|
||||
string.push_str(&cell.style.bg.termion_bg());
|
||||
style.bg = cell.style.bg;
|
||||
inst += 1;
|
||||
}
|
||||
string.push_str(&cell.symbol);
|
||||
inst += 1;
|
||||
}
|
||||
debug!("{} instructions outputed.", inst);
|
||||
write!(self.stdout,
|
||||
"{}{}{}{}",
|
||||
string,
|
||||
Color::Reset.termion_fg(),
|
||||
Color::Reset.termion_bg(),
|
||||
Modifier::Reset.termion_modifier())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Return the size of the terminal
|
||||
fn size(&self) -> Result<Rect, io::Error> {
|
||||
let terminal = try!(termion::terminal_size());
|
||||
Ok(Rect {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: terminal.0,
|
||||
height: terminal.1,
|
||||
})
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), io::Error> {
|
||||
try!(self.stdout.flush());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! termion_fg {
|
||||
($color:ident) => (format!("{}", termion::color::Fg(termion::color::$color)));
|
||||
}
|
||||
|
||||
macro_rules! termion_fg_rgb {
|
||||
($r:expr, $g:expr, $b:expr) => (format!("{}", termion::color::Fg(termion::color::Rgb($r, $g, $b))));
|
||||
}
|
||||
|
||||
macro_rules! termion_bg {
|
||||
($color:ident) => (format!("{}", termion::color::Bg(termion::color::$color)));
|
||||
}
|
||||
|
||||
macro_rules! termion_bg_rgb {
|
||||
($r:expr, $g:expr, $b:expr) => (format!("{}", termion::color::Bg(termion::color::Rgb($r, $g, $b))));
|
||||
}
|
||||
|
||||
macro_rules! termion_modifier {
|
||||
($style:ident) => (format!("{}", termion::style::$style));
|
||||
}
|
||||
|
||||
impl Color {
|
||||
pub fn termion_fg(&self) -> String {
|
||||
match *self {
|
||||
Color::Reset => termion_fg!(Reset),
|
||||
Color::Black => termion_fg!(Black),
|
||||
Color::Red => termion_fg!(Red),
|
||||
Color::Green => termion_fg!(Green),
|
||||
Color::Yellow => termion_fg!(Yellow),
|
||||
Color::Magenta => termion_fg!(Magenta),
|
||||
Color::Cyan => termion_fg!(Cyan),
|
||||
Color::Gray => termion_fg_rgb!(146, 131, 116),
|
||||
Color::DarkGray => termion_fg_rgb!(80, 73, 69),
|
||||
Color::LightRed => termion_fg!(LightRed),
|
||||
Color::LightGreen => termion_fg!(LightGreen),
|
||||
Color::LightYellow => termion_fg!(LightYellow),
|
||||
Color::LightMagenta => termion_fg!(LightMagenta),
|
||||
Color::LightCyan => termion_fg!(LightCyan),
|
||||
Color::White => termion_fg!(White),
|
||||
Color::Rgb(r, g, b) => termion_fg_rgb!(r, g, b),
|
||||
}
|
||||
}
|
||||
pub fn termion_bg(&self) -> String {
|
||||
match *self {
|
||||
Color::Reset => termion_bg!(Reset),
|
||||
Color::Black => termion_bg!(Black),
|
||||
Color::Red => termion_bg!(Red),
|
||||
Color::Green => termion_bg!(Green),
|
||||
Color::Yellow => termion_bg!(Yellow),
|
||||
Color::Magenta => termion_bg!(Magenta),
|
||||
Color::Cyan => termion_bg!(Cyan),
|
||||
Color::Gray => termion_bg_rgb!(146, 131, 116),
|
||||
Color::DarkGray => termion_bg_rgb!(80, 73, 69),
|
||||
Color::LightRed => termion_bg!(LightRed),
|
||||
Color::LightGreen => termion_bg!(LightGreen),
|
||||
Color::LightYellow => termion_bg!(LightYellow),
|
||||
Color::LightMagenta => termion_bg!(LightMagenta),
|
||||
Color::LightCyan => termion_bg!(LightCyan),
|
||||
Color::White => termion_bg!(White),
|
||||
Color::Rgb(r, g, b) => termion_bg_rgb!(r, g, b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Modifier {
|
||||
pub fn termion_modifier(&self) -> String {
|
||||
match *self {
|
||||
Modifier::Blink => termion_modifier!(Blink),
|
||||
Modifier::Bold => termion_modifier!(Bold),
|
||||
Modifier::CrossedOut => termion_modifier!(CrossedOut),
|
||||
Modifier::Faint => termion_modifier!(Faint),
|
||||
Modifier::Framed => termion_modifier!(Framed),
|
||||
Modifier::Invert => termion_modifier!(Invert),
|
||||
Modifier::Italic => termion_modifier!(Italic),
|
||||
Modifier::NoBlink => termion_modifier!(NoBlink),
|
||||
Modifier::NoBold => termion_modifier!(NoBold),
|
||||
Modifier::NoCrossedOut => termion_modifier!(NoCrossedOut),
|
||||
Modifier::NoFaint => termion_modifier!(NoFaint),
|
||||
Modifier::NoInvert => termion_modifier!(NoInvert),
|
||||
Modifier::NoItalic => termion_modifier!(NoItalic),
|
||||
Modifier::NoUnderline => termion_modifier!(NoUnderline),
|
||||
Modifier::Reset => termion_modifier!(Reset),
|
||||
Modifier::Underline => termion_modifier!(Underline),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,7 +5,8 @@ use cassowary::{Solver, Variable, Expression, Constraint};
|
|||
use cassowary::WeightedRelation::*;
|
||||
use cassowary::strength::{REQUIRED, WEAK};
|
||||
|
||||
use terminal::{Terminal, Backend};
|
||||
use terminal::Terminal;
|
||||
use backend::Backend;
|
||||
|
||||
#[derive(Debug, Hash, Clone, PartialEq, Eq)]
|
||||
pub enum Direction {
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
extern crate termion;
|
||||
extern crate rustbox;
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
#[macro_use]
|
||||
|
@ -10,9 +8,10 @@ extern crate unicode_width;
|
|||
|
||||
pub mod buffer;
|
||||
pub mod symbols;
|
||||
pub mod backend;
|
||||
pub mod terminal;
|
||||
pub mod widgets;
|
||||
pub mod style;
|
||||
pub mod layout;
|
||||
|
||||
pub use self::terminal::{Terminal, Backend, TermionBackend, RustboxBackend};
|
||||
pub use self::terminal::Terminal;
|
||||
|
|
120
src/style.rs
120
src/style.rs
|
@ -1,6 +1,3 @@
|
|||
use termion;
|
||||
use rustbox;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum Color {
|
||||
Reset,
|
||||
|
@ -78,120 +75,3 @@ impl Style {
|
|||
self
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! termion_fg {
|
||||
($color:ident) => (format!("{}", termion::color::Fg(termion::color::$color)));
|
||||
}
|
||||
|
||||
macro_rules! termion_fg_rgb {
|
||||
($r:expr, $g:expr, $b:expr) => (format!("{}", termion::color::Fg(termion::color::Rgb($r, $g, $b))));
|
||||
}
|
||||
|
||||
macro_rules! termion_bg {
|
||||
($color:ident) => (format!("{}", termion::color::Bg(termion::color::$color)));
|
||||
}
|
||||
|
||||
macro_rules! termion_bg_rgb {
|
||||
($r:expr, $g:expr, $b:expr) => (format!("{}", termion::color::Bg(termion::color::Rgb($r, $g, $b))));
|
||||
}
|
||||
|
||||
macro_rules! termion_modifier {
|
||||
($style:ident) => (format!("{}", termion::style::$style));
|
||||
}
|
||||
|
||||
impl Color {
|
||||
pub fn termion_fg(&self) -> String {
|
||||
match *self {
|
||||
Color::Reset => termion_fg!(Reset),
|
||||
Color::Black => termion_fg!(Black),
|
||||
Color::Red => termion_fg!(Red),
|
||||
Color::Green => termion_fg!(Green),
|
||||
Color::Yellow => termion_fg!(Yellow),
|
||||
Color::Magenta => termion_fg!(Magenta),
|
||||
Color::Cyan => termion_fg!(Cyan),
|
||||
Color::Gray => termion_fg_rgb!(146, 131, 116),
|
||||
Color::DarkGray => termion_fg_rgb!(80, 73, 69),
|
||||
Color::LightRed => termion_fg!(LightRed),
|
||||
Color::LightGreen => termion_fg!(LightGreen),
|
||||
Color::LightYellow => termion_fg!(LightYellow),
|
||||
Color::LightMagenta => termion_fg!(LightMagenta),
|
||||
Color::LightCyan => termion_fg!(LightCyan),
|
||||
Color::White => termion_fg!(White),
|
||||
Color::Rgb(r, g, b) => termion_fg_rgb!(r, g, b),
|
||||
}
|
||||
}
|
||||
pub fn termion_bg(&self) -> String {
|
||||
match *self {
|
||||
Color::Reset => termion_bg!(Reset),
|
||||
Color::Black => termion_bg!(Black),
|
||||
Color::Red => termion_bg!(Red),
|
||||
Color::Green => termion_bg!(Green),
|
||||
Color::Yellow => termion_bg!(Yellow),
|
||||
Color::Magenta => termion_bg!(Magenta),
|
||||
Color::Cyan => termion_bg!(Cyan),
|
||||
Color::Gray => termion_bg_rgb!(146, 131, 116),
|
||||
Color::DarkGray => termion_bg_rgb!(80, 73, 69),
|
||||
Color::LightRed => termion_bg!(LightRed),
|
||||
Color::LightGreen => termion_bg!(LightGreen),
|
||||
Color::LightYellow => termion_bg!(LightYellow),
|
||||
Color::LightMagenta => termion_bg!(LightMagenta),
|
||||
Color::LightCyan => termion_bg!(LightCyan),
|
||||
Color::White => termion_bg!(White),
|
||||
Color::Rgb(r, g, b) => termion_bg_rgb!(r, g, b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn rgb_to_byte(r: u8, g: u8, b: u8) -> u16 {
|
||||
((((r & 255 & 0xC0) + ((g & 255 & 0xE0) >> 2) + ((b & 0xE0) >> 5))) & 0xFF) as u16
|
||||
}
|
||||
|
||||
impl Into<rustbox::Color> for Color {
|
||||
fn into(self) -> rustbox::Color {
|
||||
match self {
|
||||
Color::Reset => rustbox::Color::Default,
|
||||
Color::Black | Color::Gray | Color::DarkGray => rustbox::Color::Black,
|
||||
Color::Red | Color::LightRed => rustbox::Color::Red,
|
||||
Color::Green | Color::LightGreen => rustbox::Color::Green,
|
||||
Color::Yellow | Color::LightYellow => rustbox::Color::Yellow,
|
||||
Color::Magenta | Color::LightMagenta => rustbox::Color::Magenta,
|
||||
Color::Cyan | Color::LightCyan => rustbox::Color::Cyan,
|
||||
Color::White => rustbox::Color::White,
|
||||
Color::Rgb(r, g, b) => rustbox::Color::Byte(rgb_to_byte(r, g, b)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Modifier {
|
||||
pub fn termion_modifier(&self) -> String {
|
||||
match *self {
|
||||
Modifier::Blink => termion_modifier!(Blink),
|
||||
Modifier::Bold => termion_modifier!(Bold),
|
||||
Modifier::CrossedOut => termion_modifier!(CrossedOut),
|
||||
Modifier::Faint => termion_modifier!(Faint),
|
||||
Modifier::Framed => termion_modifier!(Framed),
|
||||
Modifier::Invert => termion_modifier!(Invert),
|
||||
Modifier::Italic => termion_modifier!(Italic),
|
||||
Modifier::NoBlink => termion_modifier!(NoBlink),
|
||||
Modifier::NoBold => termion_modifier!(NoBold),
|
||||
Modifier::NoCrossedOut => termion_modifier!(NoCrossedOut),
|
||||
Modifier::NoFaint => termion_modifier!(NoFaint),
|
||||
Modifier::NoInvert => termion_modifier!(NoInvert),
|
||||
Modifier::NoItalic => termion_modifier!(NoItalic),
|
||||
Modifier::NoUnderline => termion_modifier!(NoUnderline),
|
||||
Modifier::Reset => termion_modifier!(Reset),
|
||||
Modifier::Underline => termion_modifier!(Underline),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<rustbox::Style> for Modifier {
|
||||
fn into(self) -> rustbox::Style {
|
||||
match self {
|
||||
Modifier::Bold => rustbox::RB_BOLD,
|
||||
Modifier::Underline => rustbox::RB_UNDERLINE,
|
||||
Modifier::Invert => rustbox::RB_REVERSE,
|
||||
_ => rustbox::RB_NORMAL,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
183
src/terminal.rs
183
src/terminal.rs
|
@ -1,184 +1,10 @@
|
|||
use std::io;
|
||||
use std::io::Write;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use termion;
|
||||
use termion::raw::{IntoRawMode, RawTerminal};
|
||||
|
||||
use rustbox;
|
||||
|
||||
use buffer::{Buffer, Cell};
|
||||
use backend::Backend;
|
||||
use buffer::Buffer;
|
||||
use layout::{Rect, Group, split};
|
||||
use widgets::Widget;
|
||||
use style::{Color, Modifier, Style};
|
||||
|
||||
pub trait Backend {
|
||||
fn draw<'a, I>(&mut self, content: I) -> Result<(), io::Error>
|
||||
where I: Iterator<Item = (u16, u16, &'a Cell)>;
|
||||
fn hide_cursor(&mut self) -> Result<(), io::Error>;
|
||||
fn show_cursor(&mut self) -> Result<(), io::Error>;
|
||||
fn clear(&mut self) -> Result<(), io::Error>;
|
||||
fn size(&self) -> Result<Rect, io::Error>;
|
||||
fn flush(&mut self) -> Result<(), io::Error>;
|
||||
}
|
||||
|
||||
pub struct TermionBackend {
|
||||
stdout: RawTerminal<io::Stdout>,
|
||||
}
|
||||
|
||||
impl TermionBackend {
|
||||
pub fn new() -> Result<TermionBackend, io::Error> {
|
||||
let stdout = try!(io::stdout().into_raw_mode());
|
||||
Ok(TermionBackend { stdout: stdout })
|
||||
}
|
||||
}
|
||||
|
||||
impl Backend for TermionBackend {
|
||||
/// Clears the entire screen and move the cursor to the top left of the screen
|
||||
fn clear(&mut self) -> Result<(), io::Error> {
|
||||
try!(write!(self.stdout, "{}", termion::clear::All));
|
||||
try!(write!(self.stdout, "{}", termion::cursor::Goto(1, 1)));
|
||||
try!(self.stdout.flush());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Hides cursor
|
||||
fn hide_cursor(&mut self) -> Result<(), io::Error> {
|
||||
try!(write!(self.stdout, "{}", termion::cursor::Hide));
|
||||
try!(self.stdout.flush());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Shows cursor
|
||||
fn show_cursor(&mut self) -> Result<(), io::Error> {
|
||||
try!(write!(self.stdout, "{}", termion::cursor::Show));
|
||||
try!(self.stdout.flush());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn draw<'a, I>(&mut self, content: I) -> Result<(), io::Error>
|
||||
where I: Iterator<Item = (u16, u16, &'a Cell)>
|
||||
{
|
||||
let mut string = String::with_capacity(content.size_hint().0 * 3);
|
||||
let mut style = Style::default();
|
||||
let mut last_y = 0;
|
||||
let mut last_x = 0;
|
||||
let mut inst = 0;
|
||||
for (x, y, cell) in content {
|
||||
if y != last_y || x != last_x + 1 {
|
||||
string.push_str(&format!("{}", termion::cursor::Goto(x + 1, y + 1)));
|
||||
inst += 1;
|
||||
}
|
||||
last_x = x;
|
||||
last_y = y;
|
||||
if cell.style.modifier != style.modifier {
|
||||
string.push_str(&cell.style.modifier.termion_modifier());
|
||||
style.modifier = cell.style.modifier;
|
||||
if style.modifier == Modifier::Reset {
|
||||
style.bg = Color::Reset;
|
||||
style.fg = Color::Reset;
|
||||
}
|
||||
inst += 1;
|
||||
}
|
||||
if cell.style.fg != style.fg {
|
||||
string.push_str(&cell.style.fg.termion_fg());
|
||||
style.fg = cell.style.fg;
|
||||
inst += 1;
|
||||
}
|
||||
if cell.style.bg != style.bg {
|
||||
string.push_str(&cell.style.bg.termion_bg());
|
||||
style.bg = cell.style.bg;
|
||||
inst += 1;
|
||||
}
|
||||
string.push_str(&cell.symbol);
|
||||
inst += 1;
|
||||
}
|
||||
debug!("{} instructions outputed.", inst);
|
||||
try!(write!(self.stdout,
|
||||
"{}{}{}{}",
|
||||
string,
|
||||
Color::Reset.termion_fg(),
|
||||
Color::Reset.termion_bg(),
|
||||
Modifier::Reset.termion_modifier()));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Return the size of the terminal
|
||||
fn size(&self) -> Result<Rect, io::Error> {
|
||||
let terminal = try!(termion::terminal_size());
|
||||
Ok(Rect {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: terminal.0,
|
||||
height: terminal.1,
|
||||
})
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), io::Error> {
|
||||
try!(self.stdout.flush());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RustboxBackend {
|
||||
rustbox: rustbox::RustBox,
|
||||
}
|
||||
|
||||
impl RustboxBackend {
|
||||
pub fn new() -> Result<RustboxBackend, rustbox::InitError> {
|
||||
let rustbox = try!(rustbox::RustBox::init(Default::default()));
|
||||
Ok(RustboxBackend { rustbox: rustbox })
|
||||
}
|
||||
|
||||
pub fn with_rustbox(instance: rustbox::RustBox) -> RustboxBackend {
|
||||
RustboxBackend { rustbox: instance }
|
||||
}
|
||||
|
||||
pub fn rustbox(&self) -> &rustbox::RustBox {
|
||||
&self.rustbox
|
||||
}
|
||||
}
|
||||
|
||||
impl Backend for RustboxBackend {
|
||||
fn draw<'a, I>(&mut self, content: I) -> Result<(), io::Error>
|
||||
where I: Iterator<Item = (u16, u16, &'a Cell)>
|
||||
{
|
||||
let mut inst = 0;
|
||||
for (x, y, cell) in content {
|
||||
inst += 1;
|
||||
self.rustbox.print(x as usize,
|
||||
y as usize,
|
||||
cell.style.modifier.into(),
|
||||
cell.style.fg.into(),
|
||||
cell.style.bg.into(),
|
||||
&cell.symbol);
|
||||
}
|
||||
debug!("{} instructions outputed", inst);
|
||||
Ok(())
|
||||
}
|
||||
fn hide_cursor(&mut self) -> Result<(), io::Error> {
|
||||
Ok(())
|
||||
}
|
||||
fn show_cursor(&mut self) -> Result<(), io::Error> {
|
||||
Ok(())
|
||||
}
|
||||
fn clear(&mut self) -> Result<(), io::Error> {
|
||||
self.rustbox.clear();
|
||||
Ok(())
|
||||
}
|
||||
fn size(&self) -> Result<Rect, io::Error> {
|
||||
Ok((Rect {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: self.rustbox.width() as u16,
|
||||
height: self.rustbox.height() as u16,
|
||||
}))
|
||||
}
|
||||
fn flush(&mut self) -> Result<(), io::Error> {
|
||||
self.rustbox.present();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Holds a computed layout and keeps track of its use between successive draw calls
|
||||
#[derive(Debug)]
|
||||
|
@ -278,15 +104,14 @@ impl<B> Terminal<B>
|
|||
self.buffers[1 - self.current].resize(area);
|
||||
self.buffers[1 - self.current].reset();
|
||||
self.layout_cache.clear();
|
||||
try!(self.backend.clear());
|
||||
Ok(())
|
||||
self.backend.clear()
|
||||
}
|
||||
|
||||
/// Flushes the current internal state and prepares the interface for the next draw call
|
||||
pub fn draw(&mut self) -> Result<(), io::Error> {
|
||||
|
||||
// Draw to stdout
|
||||
try!(self.flush());
|
||||
self.flush()?;
|
||||
|
||||
// Clean layout cache
|
||||
let hot = self.layout_cache
|
||||
|
|
|
@ -21,7 +21,8 @@ pub use self::table::Table;
|
|||
|
||||
use buffer::Buffer;
|
||||
use layout::Rect;
|
||||
use terminal::{Backend, Terminal};
|
||||
use terminal::Terminal;
|
||||
use backend::Backend;
|
||||
use style::Color;
|
||||
|
||||
/// Bitflags that can be composed to set the visible borders essentially on the block widget.
|
||||
|
|
Loading…
Reference in a new issue