mirror of
https://github.com/ratatui-org/ratatui
synced 2024-11-22 12:43:16 +00:00
Add support for text styling
This commit is contained in:
parent
0b950de09f
commit
224eb2d8e0
19 changed files with 605 additions and 495 deletions
|
@ -5,13 +5,13 @@ use std::io;
|
|||
use termion::event;
|
||||
use termion::input::TermRead;
|
||||
|
||||
use tui::Terminal;
|
||||
use tui::{Terminal, TermionBackend};
|
||||
use tui::widgets::{Widget, Block, border};
|
||||
use tui::layout::{Group, Direction, Size};
|
||||
use tui::style::Color;
|
||||
|
||||
fn main() {
|
||||
let mut terminal = Terminal::new().unwrap();
|
||||
let mut terminal = Terminal::new(TermionBackend::new().unwrap()).unwrap();
|
||||
let stdin = io::stdin();
|
||||
terminal.clear().unwrap();
|
||||
terminal.hide_cursor().unwrap();
|
||||
|
@ -26,41 +26,43 @@ fn main() {
|
|||
terminal.show_cursor().unwrap();
|
||||
}
|
||||
|
||||
fn draw(t: &mut Terminal) {
|
||||
fn draw(t: &mut Terminal<TermionBackend>) {
|
||||
|
||||
let size = t.size().unwrap();
|
||||
|
||||
Group::default()
|
||||
.direction(Direction::Vertical)
|
||||
.sizes(&[Size::Fixed(7), Size::Min(0), Size::Fixed(7)])
|
||||
.render(t, &Terminal::size().unwrap(), |t, chunks| {
|
||||
.render(t, &size, |t, chunks| {
|
||||
Block::default()
|
||||
.title("Top")
|
||||
.title_color(Color::Magenta)
|
||||
.background_color(Color::White)
|
||||
.border_color(Color::Magenta)
|
||||
.borders(border::BOTTOM)
|
||||
.render(&chunks[0], t);
|
||||
.render(t, &chunks[0]);
|
||||
Group::default()
|
||||
.direction(Direction::Horizontal)
|
||||
.sizes(&[Size::Fixed(7), Size::Min(0), Size::Fixed(7)])
|
||||
.render(t, &chunks[1], |t, chunks| {
|
||||
Block::default().title("Left").title_color(Color::Yellow).render(&chunks[0], t);
|
||||
Block::default().title("Left").title_color(Color::Yellow).render(t, &chunks[0]);
|
||||
Block::default()
|
||||
.title("Middle")
|
||||
.title_color(Color::Cyan)
|
||||
.border_color(Color::Cyan)
|
||||
.borders(border::LEFT | border::RIGHT)
|
||||
.render(&chunks[1], t);
|
||||
.render(t, &chunks[1]);
|
||||
Block::default()
|
||||
.title("Right")
|
||||
.title_color(Color::Green)
|
||||
.render(&chunks[2], t);
|
||||
.render(t, &chunks[2]);
|
||||
});
|
||||
Block::default()
|
||||
.title("Bottom")
|
||||
.title_color(Color::Red)
|
||||
.border_color(Color::Red)
|
||||
.borders(border::TOP)
|
||||
.render(&chunks[2], t);
|
||||
.render(t, &chunks[2]);
|
||||
});
|
||||
|
||||
t.draw().unwrap();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
extern crate tui;
|
||||
|
||||
use tui::Terminal;
|
||||
use tui::{Terminal, TermionBackend};
|
||||
use tui::widgets::Widget;
|
||||
use tui::buffer::Buffer;
|
||||
use tui::layout::Rect;
|
||||
|
@ -34,8 +34,9 @@ impl<'a> Label<'a> {
|
|||
}
|
||||
|
||||
fn main() {
|
||||
let mut terminal = Terminal::new().unwrap();
|
||||
let mut terminal = Terminal::new(TermionBackend::new().unwrap()).unwrap();
|
||||
let size = terminal.size().unwrap();
|
||||
terminal.clear().unwrap();
|
||||
Label::default().text("Test").render(&Terminal::size().unwrap(), &mut terminal);
|
||||
Label::default().text("Test").render(&mut terminal, &size);
|
||||
terminal.draw().unwrap();
|
||||
}
|
||||
|
|
|
@ -22,11 +22,11 @@ use log4rs::encode::pattern::PatternEncoder;
|
|||
use log4rs::config::{Appender, Config, Root};
|
||||
|
||||
use tui::{Terminal, TermionBackend};
|
||||
use tui::widgets::{Widget, Block, List, Gauge, Sparkline, Text, border, Chart, Axis, Dataset,
|
||||
use tui::widgets::{Widget, Block, List, Gauge, Sparkline, Paragraph, border, Chart, Axis, Dataset,
|
||||
BarChart, Marker, Tabs, Table};
|
||||
use tui::widgets::canvas::{Canvas, Map, MapResolution, Line};
|
||||
use tui::layout::{Group, Direction, Size, Rect};
|
||||
use tui::style::Color;
|
||||
use tui::style::{Style, Color, Modifier};
|
||||
|
||||
#[derive(Clone)]
|
||||
struct RandomSignal {
|
||||
|
@ -330,10 +330,10 @@ fn draw(t: &mut Terminal<TermionBackend>, app: &App) -> Result<(), io::Error> {
|
|||
Tabs::default()
|
||||
.block(Block::default().borders(border::ALL).title("Tabs"))
|
||||
.titles(&app.tabs.titles)
|
||||
.color(Color::Green)
|
||||
.highlight_color(Color::Yellow)
|
||||
.style(Style::default().fg(Color::Green))
|
||||
.highlight_style(Style::default().fg(Color::Yellow))
|
||||
.select(app.tabs.selection)
|
||||
.render(&chunks[0], t);
|
||||
.render(t, &chunks[0]);
|
||||
match app.tabs.selection {
|
||||
0 => {
|
||||
draw_main(t, app, &chunks[1]);
|
||||
|
@ -348,13 +348,13 @@ fn draw(t: &mut Terminal<TermionBackend>, app: &App) -> Result<(), io::Error> {
|
|||
.title("Servers")
|
||||
.borders(border::ALL))
|
||||
.header(&["Server", "Location", "Status"])
|
||||
.header_color(Color::Red)
|
||||
.header_style(Style::default().fg(Color::Red))
|
||||
.widths(&[15, 15, 10])
|
||||
.rows(app.servers
|
||||
.iter()
|
||||
.map(|s| vec![s.name, s.location, s.status])
|
||||
.collect::<Vec<Vec<&str>>>())
|
||||
.render(&chunks[0], t);
|
||||
.render(t, &chunks[0]);
|
||||
|
||||
Canvas::default()
|
||||
.block(Block::default().title("World").borders(border::ALL))
|
||||
|
@ -386,7 +386,7 @@ fn draw(t: &mut Terminal<TermionBackend>, app: &App) -> Result<(), io::Error> {
|
|||
})
|
||||
.x_bounds([-180.0, 180.0])
|
||||
.y_bounds([-90.0, 90.0])
|
||||
.render(&chunks[1], t);
|
||||
.render(t, &chunks[1]);
|
||||
})
|
||||
}
|
||||
_ => {}
|
||||
|
@ -404,7 +404,7 @@ fn draw_main(t: &mut Terminal<TermionBackend>, app: &App, area: &Rect) {
|
|||
Block::default()
|
||||
.borders(border::ALL)
|
||||
.title("Graphs")
|
||||
.render(&chunks[0], t);
|
||||
.render(t, &chunks[0]);
|
||||
Group::default()
|
||||
.direction(Direction::Vertical)
|
||||
.margin(1)
|
||||
|
@ -412,14 +412,14 @@ fn draw_main(t: &mut Terminal<TermionBackend>, app: &App, area: &Rect) {
|
|||
.render(t, &chunks[0], |t, chunks| {
|
||||
Gauge::default()
|
||||
.block(Block::default().title("Gauge:"))
|
||||
.color(Color::Magenta)
|
||||
.style(Style::default().fg(Color::Magenta).bg(Color::Black).modifier(Modifier::Italic))
|
||||
.percent(app.progress)
|
||||
.render(&chunks[0], t);
|
||||
.render(t, &chunks[0]);
|
||||
Sparkline::default()
|
||||
.block(Block::default().title("Sparkline:"))
|
||||
.color(Color::Green)
|
||||
.style(Style::default().fg(Color::Green))
|
||||
.data(&app.data)
|
||||
.render(&chunks[1], t);
|
||||
.render(t, &chunks[1]);
|
||||
});
|
||||
let sizes = if app.show_chart {
|
||||
vec![Size::Percent(50), Size::Percent(50)]
|
||||
|
@ -444,16 +444,16 @@ fn draw_main(t: &mut Terminal<TermionBackend>, app: &App, area: &Rect) {
|
|||
.title("List"))
|
||||
.items(&app.items)
|
||||
.select(app.selected)
|
||||
.highlight_color(Color::Yellow)
|
||||
.highlight_style(Style::default().fg(Color::Yellow).modifier(Modifier::Bold))
|
||||
.highlight_symbol(">")
|
||||
.render(&chunks[0], t);
|
||||
.render(t, &chunks[0]);
|
||||
List::default()
|
||||
.block(Block::default()
|
||||
.borders(border::ALL)
|
||||
.title("List"))
|
||||
.items(&app.items2)
|
||||
.color(Color::Gray)
|
||||
.render(&chunks[1], t);
|
||||
.style(Style::default().fg(Color::Gray))
|
||||
.render(t, &chunks[1]);
|
||||
});
|
||||
BarChart::default()
|
||||
.block(Block::default()
|
||||
|
@ -462,50 +462,52 @@ fn draw_main(t: &mut Terminal<TermionBackend>, app: &App, area: &Rect) {
|
|||
.data(&app.data4)
|
||||
.bar_width(3)
|
||||
.bar_gap(2)
|
||||
.bar_color(Color::Green)
|
||||
.value_color(Color::Black)
|
||||
.label_color(Color::Yellow)
|
||||
.render(&chunks[1], t);
|
||||
.value_style(Style::default().fg(Color::Black).bg(Color::Green).modifier(Modifier::Italic))
|
||||
.label_style(Style::default().fg(Color::Yellow))
|
||||
.style(Style::default().fg(Color::Green))
|
||||
.render(t, &chunks[1]);
|
||||
});
|
||||
if app.show_chart {
|
||||
Chart::default()
|
||||
.block(Block::default().title("Chart"))
|
||||
.block(Block::default().title("Chart").borders(border::ALL))
|
||||
.x_axis(Axis::default()
|
||||
.title("X Axis")
|
||||
.color(Color::Gray)
|
||||
.style(Style::default().fg(Color::Gray))
|
||||
.labels_style(Style::default().modifier(Modifier::Italic))
|
||||
.bounds(app.window)
|
||||
.labels(&[&format!("{}", app.window[0]),
|
||||
&format!("{}", (app.window[0] + app.window[1]) / 2.0),
|
||||
&format!("{}", app.window[1])]))
|
||||
.y_axis(Axis::default()
|
||||
.title("Y Axis")
|
||||
.color(Color::Gray)
|
||||
.style(Style::default().fg(Color::Gray))
|
||||
.labels_style(Style::default().modifier(Modifier::Italic))
|
||||
.bounds([-20.0, 20.0])
|
||||
.labels(&["-20", "0", "20"]))
|
||||
.datasets(&[Dataset::default()
|
||||
.name("data2")
|
||||
.marker(Marker::Dot)
|
||||
.color(Color::Cyan)
|
||||
.style(Style::default().fg(Color::Cyan))
|
||||
.data(&app.data2),
|
||||
Dataset::default()
|
||||
.name("data3")
|
||||
.marker(Marker::Braille)
|
||||
.color(Color::Yellow)
|
||||
.style(Style::default().fg(Color::Yellow))
|
||||
.data(&app.data3)])
|
||||
.render(&chunks[1], t);
|
||||
.render(t, &chunks[1]);
|
||||
}
|
||||
});
|
||||
Text::default()
|
||||
Paragraph::default()
|
||||
.block(Block::default().borders(border::ALL).title("Footer"))
|
||||
.wrap(true)
|
||||
.color(app.colors[app.color_index])
|
||||
.style(Style::default().fg(app.colors[app.color_index]))
|
||||
.text("This is a paragraph with several lines.\nYou can change the color.\nUse \
|
||||
\\{[color] [text]} to highlight the text with a color. For example, {red \
|
||||
u}{green n}{yellow d}{magenta e}{cyan r} {gray t}{light_gray h}{light_red \
|
||||
e} {light_green r}{light_yellow a}{light_magenta i}{light_cyan n}{white \
|
||||
b}{red o}{green w}.\nOh, and if you didn't notice you can automatically \
|
||||
wrap your text =).\nOne more thing is that it should display unicode \
|
||||
\\{fg=[color];bg=[color];mod=[modifier] [text]} to highlight the text with a color. For example, {fg=red \
|
||||
u}{fg=green n}{fg=yellow d}{fg=magenta e}{fg=cyan r} {fg=gray t}{fg=light_gray h}{fg=light_red \
|
||||
e} {fg=light_green r}{fg=light_yellow a}{fg=light_magenta i}{fg=light_cyan n}{fg=white \
|
||||
b}{fg=red o}{fg=green w}.\nOh, and if you didn't {mod=italic notice} you can {mod=bold automatically} \
|
||||
{mod=invert wrap} your {mod=underline text} =).\nOne more thing is that it should display unicode \
|
||||
characters properly: 日本国, ٩(-̮̮̃-̃)۶ ٩(●̮̮̃•̃)۶ ٩(͡๏̯͡๏)۶ ٩(-̮̮̃•̃).")
|
||||
.render(&chunks[2], t);
|
||||
.render(t, &chunks[2]);
|
||||
});
|
||||
}
|
||||
|
|
54
examples/rustbox.rs
Normal file
54
examples/rustbox.rs
Normal file
|
@ -0,0 +1,54 @@
|
|||
extern crate tui;
|
||||
extern crate rustbox;
|
||||
|
||||
use std::error::Error;
|
||||
use rustbox::Key;
|
||||
|
||||
use tui::{Terminal, RustboxBackend};
|
||||
use tui::widgets::{Widget, Block, border, Paragraph};
|
||||
use tui::layout::{Group, Direction, Size};
|
||||
use tui::style::{Style, Color, Modifier};
|
||||
|
||||
fn main() {
|
||||
let mut terminal = Terminal::new(RustboxBackend::new().unwrap()).unwrap();
|
||||
terminal.clear().unwrap();
|
||||
terminal.hide_cursor().unwrap();
|
||||
draw(&mut terminal);
|
||||
loop {
|
||||
match terminal.backend().rustbox().poll_event(false) {
|
||||
Ok(rustbox::Event::KeyEvent(key)) => {
|
||||
match key {
|
||||
Key::Char('q') => {
|
||||
break;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Err(e) => panic!("{}", e.description()),
|
||||
_ => {}
|
||||
};
|
||||
draw(&mut terminal);
|
||||
}
|
||||
terminal.show_cursor().unwrap();
|
||||
}
|
||||
|
||||
fn draw(t: &mut Terminal<RustboxBackend>) {
|
||||
|
||||
let size = t.size().unwrap();
|
||||
|
||||
Group::default()
|
||||
.direction(Direction::Vertical)
|
||||
.sizes(&[Size::Percent(100)])
|
||||
.render(t, &size, |t, chunks| {
|
||||
Paragraph::default()
|
||||
.block(Block::default()
|
||||
.title("Rustbox backend")
|
||||
.title_style(Style::default().fg(Color::Yellow).modifier(Modifier::Bold))
|
||||
.borders(border::ALL)
|
||||
.border_style(Style::default().fg(Color::Magenta)))
|
||||
.text("It {yellow works}!")
|
||||
.render(t, &chunks[0]);
|
||||
});
|
||||
|
||||
t.draw().unwrap();
|
||||
}
|
131
src/buffer.rs
131
src/buffer.rs
|
@ -4,22 +4,52 @@ use std::usize;
|
|||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
use layout::Rect;
|
||||
use style::Color;
|
||||
use style::{Style, Color, Modifier};
|
||||
|
||||
/// A buffer cell
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Cell {
|
||||
pub fg: Color,
|
||||
pub bg: Color,
|
||||
pub symbol: String,
|
||||
pub style: Style,
|
||||
}
|
||||
|
||||
impl Cell {
|
||||
pub fn set_symbol(&mut self, symbol: &str) -> &mut Cell {
|
||||
self.symbol.clear();
|
||||
self.symbol.push_str(symbol);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_char(&mut self, ch: char) -> &mut Cell {
|
||||
self.symbol.clear();
|
||||
self.symbol.push(ch);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_fg(&mut self, color: Color) -> &mut Cell {
|
||||
self.style.fg = color;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_bg(&mut self, color: Color) -> &mut Cell {
|
||||
self.style.bg = color;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_modifier(&mut self, modifier: Modifier) -> &mut Cell {
|
||||
self.style.modifier = modifier;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_style(&mut self, style: Style) -> &mut Cell {
|
||||
self.style = style;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.symbol.clear();
|
||||
self.symbol.push(' ');
|
||||
self.fg = Color::Reset;
|
||||
self.bg = Color::Reset;
|
||||
self.style.reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,8 +57,7 @@ impl Default for Cell {
|
|||
fn default() -> Cell {
|
||||
Cell {
|
||||
symbol: " ".into(),
|
||||
fg: Color::Reset,
|
||||
bg: Color::Reset,
|
||||
style: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -46,19 +75,20 @@ impl Default for Cell {
|
|||
/// # extern crate tui;
|
||||
/// use tui::buffer::{Buffer, Cell};
|
||||
/// use tui::layout::Rect;
|
||||
/// use tui::style::Color;
|
||||
/// use tui::style::{Color, Style};
|
||||
///
|
||||
/// # fn main() {
|
||||
/// let mut buf = Buffer::empty(Rect{x: 0, y: 0, width: 10, height: 5});
|
||||
/// buf.set_symbol(0, 2, "x");
|
||||
/// assert_eq!(buf.at(0, 2).symbol, "x");
|
||||
/// buf.get_mut(0, 2).set_symbol("x");
|
||||
/// assert_eq!(buf.get(0, 2).symbol, "x");
|
||||
/// buf.set_string(3, 0, "string", Color::Red, Color::White);
|
||||
/// assert_eq!(buf.at(5, 0), &Cell{symbol: String::from("r"), fg: Color::Red, bg: Color::White});
|
||||
/// buf.update_cell(5, 0, |c| {
|
||||
/// c.symbol.clear();
|
||||
/// c.symbol.push('x');
|
||||
/// });
|
||||
/// assert_eq!(buf.at(5, 0).symbol, "x");
|
||||
/// assert_eq!(buf.get(5, 0), &Cell{
|
||||
/// symbol: String::from("r"),
|
||||
/// fg: Color::Red,
|
||||
/// bg: Color::White,
|
||||
/// style: Style::Reset});
|
||||
/// buf.get_mut(5, 0).set_char('x');
|
||||
/// assert_eq!(buf.get(5, 0).symbol, "x");
|
||||
/// # }
|
||||
/// ```
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -110,11 +140,17 @@ impl Buffer {
|
|||
}
|
||||
|
||||
/// Returns a reference to Cell at the given coordinates
|
||||
pub fn at(&self, x: u16, y: u16) -> &Cell {
|
||||
pub fn get(&self, x: u16, y: u16) -> &Cell {
|
||||
let i = self.index_of(x, y);
|
||||
&self.content[i]
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to Cell at the given coordinates
|
||||
pub fn get_mut(&mut self, x: u16, y: u16) -> &mut Cell {
|
||||
let i = self.index_of(x, y);
|
||||
&mut self.content[i]
|
||||
}
|
||||
|
||||
/// Returns the index in the Vec<Cell> for the given (x, y)
|
||||
pub fn index_of(&self, x: u16, y: u16) -> usize {
|
||||
debug_assert!(x >= self.area.left() && x < self.area.right() && y >= self.area.top() &&
|
||||
|
@ -136,76 +172,25 @@ impl Buffer {
|
|||
(self.area.x + i as u16 % self.area.width, self.area.y + i as u16 / self.area.width)
|
||||
}
|
||||
|
||||
/// Update the symbol of a cell at (x, y)
|
||||
pub fn set_symbol(&mut self, x: u16, y: u16, symbol: &str) {
|
||||
let i = self.index_of(x, y);
|
||||
self.content[i].symbol.clear();
|
||||
self.content[i].symbol.push_str(symbol);
|
||||
}
|
||||
|
||||
/// Update the foreground color of a cell at (x, y)
|
||||
pub fn set_fg(&mut self, x: u16, y: u16, color: Color) {
|
||||
let i = self.index_of(x, y);
|
||||
self.content[i].fg = color;
|
||||
}
|
||||
|
||||
/// Update the background color of a cell at (x, y)
|
||||
pub fn set_bg(&mut self, x: u16, y: u16, color: Color) {
|
||||
let i = self.index_of(x, y);
|
||||
self.content[i].bg = color;
|
||||
}
|
||||
|
||||
/// Print a string, starting at the position (x, y)
|
||||
pub fn set_string(&mut self, x: u16, y: u16, string: &str, fg: Color, bg: Color) {
|
||||
self.set_stringn(x, y, string, usize::MAX, fg, bg);
|
||||
pub fn set_string(&mut self, x: u16, y: u16, string: &str, style: &Style) {
|
||||
self.set_stringn(x, y, string, usize::MAX, style);
|
||||
}
|
||||
|
||||
/// Print at most the first n characters of a string if enough space is available
|
||||
/// until the end of the line
|
||||
pub fn set_stringn(&mut self,
|
||||
x: u16,
|
||||
y: u16,
|
||||
string: &str,
|
||||
limit: usize,
|
||||
fg: Color,
|
||||
bg: Color) {
|
||||
pub fn set_stringn(&mut self, x: u16, y: u16, string: &str, limit: usize, style: &Style) {
|
||||
let mut index = self.index_of(x, y);
|
||||
let graphemes = UnicodeSegmentation::graphemes(string, true).collect::<Vec<&str>>();
|
||||
let max_index = min((self.area.width - x) as usize, limit);
|
||||
let max_index = min((self.area.right() - x) as usize, limit);
|
||||
for s in graphemes.into_iter().take(max_index) {
|
||||
self.content[index].symbol.clear();
|
||||
self.content[index].symbol.push_str(s);
|
||||
self.content[index].fg = fg;
|
||||
self.content[index].bg = bg;
|
||||
self.content[index].style = style.clone();
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Update both the foreground and the background colors in a single method call
|
||||
pub fn set_colors(&mut self, x: u16, y: u16, fg: Color, bg: Color) {
|
||||
let i = self.index_of(x, y);
|
||||
self.content[i].fg = fg;
|
||||
self.content[i].bg = bg;
|
||||
}
|
||||
|
||||
/// Update all attributes of a cell at the given position
|
||||
pub fn set_cell(&mut self, x: u16, y: u16, symbol: &str, fg: Color, bg: Color) {
|
||||
let i = self.index_of(x, y);
|
||||
self.content[i].symbol.clear();
|
||||
self.content[i].symbol.push_str(symbol);
|
||||
self.content[i].fg = fg;
|
||||
self.content[i].bg = bg;
|
||||
}
|
||||
|
||||
/// Update a cell using the closure passed as last argument
|
||||
pub fn update_cell<F>(&mut self, x: u16, y: u16, f: F)
|
||||
where F: Fn(&mut Cell)
|
||||
{
|
||||
let i = self.index_of(x, y);
|
||||
f(&mut self.content[i]);
|
||||
}
|
||||
|
||||
/// Resize the buffer so that the mapped area matches the given area and that the buffer
|
||||
/// length is equal to area.width * area.height
|
||||
pub fn resize(&mut self, area: Rect) {
|
||||
|
|
|
@ -7,7 +7,7 @@ use cassowary::strength::{REQUIRED, WEAK};
|
|||
|
||||
use terminal::{Terminal, Backend};
|
||||
|
||||
#[derive(Debug, Hash, PartialEq)]
|
||||
#[derive(Debug, Hash, PartialEq, Eq)]
|
||||
pub enum Direction {
|
||||
Horizontal,
|
||||
Vertical,
|
||||
|
@ -109,7 +109,7 @@ impl Rect {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum Size {
|
||||
Fixed(u16),
|
||||
Percent(u16),
|
||||
|
@ -222,6 +222,17 @@ pub fn split(area: &Rect, dir: &Direction, margin: u16, sizes: &[Size]) -> Vec<R
|
|||
_ => {}
|
||||
}
|
||||
}
|
||||
// Fix imprecision by extending the last item a bit if necessary
|
||||
if let Some(last) = results.last_mut() {
|
||||
match *dir {
|
||||
Direction::Vertical => {
|
||||
last.height = dest_area.bottom() - last.y;
|
||||
}
|
||||
Direction::Horizontal => {
|
||||
last.width = dest_area.right() - last.x;
|
||||
}
|
||||
}
|
||||
}
|
||||
results
|
||||
}
|
||||
|
||||
|
@ -274,7 +285,7 @@ impl Element {
|
|||
/// .sizes(&[Size::Percent(50), Size::Percent(50)]);
|
||||
/// # }
|
||||
/// ```
|
||||
#[derive(Debug, Hash)]
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Group {
|
||||
pub direction: Direction,
|
||||
pub margin: u16,
|
||||
|
|
103
src/style.rs
103
src/style.rs
|
@ -21,6 +21,64 @@ pub enum Color {
|
|||
Rgb(u8, u8, u8),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum Modifier {
|
||||
Blink,
|
||||
Bold,
|
||||
CrossedOut,
|
||||
Faint,
|
||||
Framed,
|
||||
Invert,
|
||||
Italic,
|
||||
NoBlink,
|
||||
NoBold,
|
||||
NoCrossedOut,
|
||||
NoFaint,
|
||||
NoInvert,
|
||||
NoItalic,
|
||||
NoUnderline,
|
||||
Reset,
|
||||
Underline,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct Style {
|
||||
pub fg: Color,
|
||||
pub bg: Color,
|
||||
pub modifier: Modifier,
|
||||
}
|
||||
|
||||
impl Default for Style {
|
||||
fn default() -> Style {
|
||||
Style {
|
||||
fg: Color::Reset,
|
||||
bg: Color::Reset,
|
||||
modifier: Modifier::Reset,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Style {
|
||||
pub fn reset(&mut self) {
|
||||
self.fg = Color::Reset;
|
||||
self.bg = Color::Reset;
|
||||
self.modifier = Modifier::Reset;
|
||||
}
|
||||
|
||||
pub fn fg(mut self, color: Color) -> Style {
|
||||
self.fg = color;
|
||||
self
|
||||
}
|
||||
pub fn bg(mut self, color: Color) -> Style {
|
||||
self.bg = color;
|
||||
self
|
||||
}
|
||||
pub fn modifier(mut self, modifier: Modifier) -> Style {
|
||||
self.modifier = modifier;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! termion_fg {
|
||||
($color:ident) => (format!("{}", termion::color::Fg(termion::color::$color)));
|
||||
}
|
||||
|
@ -37,6 +95,10 @@ 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 {
|
||||
|
@ -80,6 +142,10 @@ impl Color {
|
|||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -98,7 +164,42 @@ impl Into<rustbox::Color> for Color {
|
|||
Color::LightMagenta => rustbox::Color::Magenta,
|
||||
Color::LightCyan => rustbox::Color::Cyan,
|
||||
Color::White => rustbox::Color::White,
|
||||
Color::Rgb(r, g, b) => rustbox::Color::Default,
|
||||
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::Reset => rustbox::RB_NORMAL,
|
||||
Modifier::Bold => rustbox::RB_BOLD,
|
||||
Modifier::Underline => rustbox::RB_UNDERLINE,
|
||||
Modifier::Invert => rustbox::RB_REVERSE,
|
||||
_ => rustbox::RB_NORMAL,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ use rustbox;
|
|||
use buffer::{Buffer, Cell};
|
||||
use layout::{Rect, Group, split};
|
||||
use widgets::Widget;
|
||||
use style::Color;
|
||||
use style::{Color, Modifier, Style};
|
||||
use util::hash;
|
||||
|
||||
pub trait Backend {
|
||||
|
@ -61,8 +61,7 @@ impl Backend for TermionBackend {
|
|||
where I: Iterator<Item = (u16, u16, &'a Cell)>
|
||||
{
|
||||
let mut string = String::with_capacity(content.size_hint().0 * 3);
|
||||
let mut fg = Color::Reset;
|
||||
let mut bg = Color::Reset;
|
||||
let mut style = Style::default();
|
||||
let mut last_y = 0;
|
||||
let mut last_x = 0;
|
||||
let mut inst = 0;
|
||||
|
@ -73,14 +72,23 @@ impl Backend for TermionBackend {
|
|||
}
|
||||
last_x = x;
|
||||
last_y = y;
|
||||
if cell.fg != fg {
|
||||
string.push_str(&cell.fg.termion_fg());
|
||||
fg = cell.fg;
|
||||
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.bg != bg {
|
||||
string.push_str(&cell.bg.termion_bg());
|
||||
bg = cell.bg;
|
||||
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);
|
||||
|
@ -88,10 +96,11 @@ impl Backend for TermionBackend {
|
|||
}
|
||||
debug!("{} instructions outputed.", inst);
|
||||
try!(write!(self.stdout,
|
||||
"{}{}{}",
|
||||
"{}{}{}{}",
|
||||
string,
|
||||
Color::Reset.termion_fg(),
|
||||
Color::Reset.termion_bg()));
|
||||
Color::Reset.termion_bg(),
|
||||
Modifier::Reset.termion_modifier()));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -121,6 +130,10 @@ impl RustboxBackend {
|
|||
let rustbox = try!(rustbox::RustBox::init(Default::default()));
|
||||
Ok(RustboxBackend { rustbox: rustbox })
|
||||
}
|
||||
|
||||
pub fn rustbox(&self) -> &rustbox::RustBox {
|
||||
&self.rustbox
|
||||
}
|
||||
}
|
||||
|
||||
impl Backend for RustboxBackend {
|
||||
|
@ -132,9 +145,9 @@ impl Backend for RustboxBackend {
|
|||
inst += 1;
|
||||
self.rustbox.print(x as usize,
|
||||
y as usize,
|
||||
rustbox::RB_NORMAL,
|
||||
cell.fg.into(),
|
||||
cell.bg.into(),
|
||||
cell.style.modifier.into(),
|
||||
cell.style.fg.into(),
|
||||
cell.style.bg.into(),
|
||||
&cell.symbol);
|
||||
}
|
||||
debug!("{} instructions outputed", inst);
|
||||
|
|
|
@ -5,7 +5,7 @@ use unicode_width::UnicodeWidthStr;
|
|||
use widgets::{Widget, Block};
|
||||
use buffer::Buffer;
|
||||
use layout::Rect;
|
||||
use style::Color;
|
||||
use style::Style;
|
||||
use symbols::bar;
|
||||
|
||||
/// Display multiple bars in a single widgets
|
||||
|
@ -36,14 +36,12 @@ pub struct BarChart<'a> {
|
|||
bar_width: u16,
|
||||
/// The gap between each bar
|
||||
bar_gap: u16,
|
||||
/// Color of the bars
|
||||
bar_color: Color,
|
||||
/// Color of the values printed at the bottom of each bar
|
||||
value_color: Color,
|
||||
/// Color of the labels printed under each bar
|
||||
label_color: Color,
|
||||
/// Background color for the widget
|
||||
background_color: Color,
|
||||
/// Style of the values printed at the bottom of each bar
|
||||
value_style: Style,
|
||||
/// Style of the labels printed under each bar
|
||||
label_style: Style,
|
||||
/// Style for the widget
|
||||
style: Style,
|
||||
/// Slice of (label, value) pair to plot on the chart
|
||||
data: &'a [(&'a str, u64)],
|
||||
/// Value necessary for a bar to reach the maximum height (if no value is specified,
|
||||
|
@ -62,10 +60,9 @@ impl<'a> Default for BarChart<'a> {
|
|||
values: Vec::new(),
|
||||
bar_width: 1,
|
||||
bar_gap: 1,
|
||||
bar_color: Color::Reset,
|
||||
value_color: Color::Reset,
|
||||
label_color: Color::Reset,
|
||||
background_color: Color::Reset,
|
||||
value_style: Default::default(),
|
||||
label_style: Default::default(),
|
||||
style: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -97,20 +94,16 @@ impl<'a> BarChart<'a> {
|
|||
self.bar_gap = gap;
|
||||
self
|
||||
}
|
||||
pub fn bar_color(&'a mut self, color: Color) -> &mut BarChart<'a> {
|
||||
self.bar_color = color;
|
||||
pub fn value_style(&'a mut self, style: Style) -> &mut BarChart<'a> {
|
||||
self.value_style = style;
|
||||
self
|
||||
}
|
||||
pub fn value_color(&'a mut self, color: Color) -> &mut BarChart<'a> {
|
||||
self.value_color = color;
|
||||
pub fn label_style(&'a mut self, style: Style) -> &mut BarChart<'a> {
|
||||
self.label_style = style;
|
||||
self
|
||||
}
|
||||
pub fn label_color(&'a mut self, color: Color) -> &mut BarChart<'a> {
|
||||
self.label_color = color;
|
||||
self
|
||||
}
|
||||
pub fn background_color(&'a mut self, color: Color) -> &mut BarChart<'a> {
|
||||
self.background_color = color;
|
||||
pub fn style(&'a mut self, style: Style) -> &mut BarChart<'a> {
|
||||
self.style = style;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -129,9 +122,7 @@ impl<'a> Widget for BarChart<'a> {
|
|||
return;
|
||||
}
|
||||
|
||||
if self.background_color != Color::Reset {
|
||||
self.background(&chart_area, buf, self.background_color);
|
||||
}
|
||||
self.background(&chart_area, buf, self.style.bg);
|
||||
|
||||
let max = self.max.unwrap_or(self.data.iter().fold(0, |acc, &(_, v)| max(v, acc)));
|
||||
let max_index = min((chart_area.width / (self.bar_width + self.bar_gap)) as usize,
|
||||
|
@ -156,12 +147,11 @@ impl<'a> Widget for BarChart<'a> {
|
|||
};
|
||||
|
||||
for x in 0..self.bar_width {
|
||||
buf.set_cell(chart_area.left() + i as u16 * (self.bar_width + self.bar_gap) +
|
||||
buf.get_mut(chart_area.left() + i as u16 * (self.bar_width + self.bar_gap) +
|
||||
x,
|
||||
chart_area.top() + j,
|
||||
symbol,
|
||||
self.bar_color,
|
||||
self.background_color);
|
||||
chart_area.top() + j)
|
||||
.set_symbol(symbol)
|
||||
.set_style(self.style);
|
||||
}
|
||||
|
||||
if d.1 > 8 {
|
||||
|
@ -182,16 +172,14 @@ impl<'a> Widget for BarChart<'a> {
|
|||
(self.bar_width - width) / 2,
|
||||
chart_area.bottom() - 2,
|
||||
value_label,
|
||||
self.value_color,
|
||||
self.bar_color);
|
||||
&self.value_style);
|
||||
}
|
||||
}
|
||||
buf.set_stringn(chart_area.left() + i as u16 * (self.bar_width + self.bar_gap),
|
||||
chart_area.bottom() - 1,
|
||||
label,
|
||||
self.bar_width as usize,
|
||||
self.label_color,
|
||||
self.background_color);
|
||||
&self.label_style);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use buffer::Buffer;
|
||||
use layout::Rect;
|
||||
use style::Color;
|
||||
use style::Style;
|
||||
use widgets::{Widget, border};
|
||||
use symbols::line;
|
||||
|
||||
|
@ -26,24 +26,24 @@ use symbols::line;
|
|||
pub struct Block<'a> {
|
||||
/// Optional title place on the upper left of the block
|
||||
title: Option<&'a str>,
|
||||
/// Title color
|
||||
title_color: Color,
|
||||
/// Title style
|
||||
title_style: Style,
|
||||
/// Visible borders
|
||||
borders: border::Flags,
|
||||
/// Borders color
|
||||
border_color: Color,
|
||||
/// Background color
|
||||
background_color: Color,
|
||||
/// Border style
|
||||
border_style: Style,
|
||||
/// Widget style
|
||||
style: Style,
|
||||
}
|
||||
|
||||
impl<'a> Default for Block<'a> {
|
||||
fn default() -> Block<'a> {
|
||||
Block {
|
||||
title: None,
|
||||
title_color: Color::Reset,
|
||||
title_style: Default::default(),
|
||||
borders: border::NONE,
|
||||
border_color: Color::Reset,
|
||||
background_color: Color::Reset,
|
||||
border_style: Default::default(),
|
||||
style: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -54,18 +54,18 @@ impl<'a> Block<'a> {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn title_color(mut self, color: Color) -> Block<'a> {
|
||||
self.title_color = color;
|
||||
pub fn title_style(mut self, style: Style) -> Block<'a> {
|
||||
self.title_style = style;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn border_color(mut self, color: Color) -> Block<'a> {
|
||||
self.border_color = color;
|
||||
pub fn border_style(mut self, style: Style) -> Block<'a> {
|
||||
self.border_style = style;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn background_color(mut self, color: Color) -> Block<'a> {
|
||||
self.background_color = color;
|
||||
pub fn style(mut self, style: Style) -> Block<'a> {
|
||||
self.style = style;
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -105,62 +105,60 @@ impl<'a> Widget for Block<'a> {
|
|||
return;
|
||||
}
|
||||
|
||||
if self.background_color != Color::Reset {
|
||||
self.background(area, buf, self.background_color)
|
||||
}
|
||||
self.background(area, buf, self.style.bg);
|
||||
|
||||
// Sides
|
||||
if self.borders.intersects(border::LEFT) {
|
||||
for y in area.top()..area.bottom() {
|
||||
buf.set_cell(area.left(),
|
||||
y,
|
||||
line::VERTICAL,
|
||||
self.border_color,
|
||||
self.background_color);
|
||||
buf.get_mut(area.left(), y)
|
||||
.set_symbol(line::VERTICAL)
|
||||
.set_style(self.border_style);
|
||||
}
|
||||
}
|
||||
if self.borders.intersects(border::TOP) {
|
||||
for x in area.left()..area.right() {
|
||||
buf.set_cell(x,
|
||||
area.top(),
|
||||
line::HORIZONTAL,
|
||||
self.border_color,
|
||||
self.background_color);
|
||||
buf.get_mut(x, area.top())
|
||||
.set_symbol(line::HORIZONTAL)
|
||||
.set_style(self.border_style);
|
||||
}
|
||||
}
|
||||
if self.borders.intersects(border::RIGHT) {
|
||||
let x = area.right() - 1;
|
||||
for y in area.top()..area.bottom() {
|
||||
buf.set_cell(x,
|
||||
y,
|
||||
line::VERTICAL,
|
||||
self.border_color,
|
||||
self.background_color);
|
||||
buf.get_mut(x, y)
|
||||
.set_symbol(line::VERTICAL)
|
||||
.set_style(self.border_style);
|
||||
}
|
||||
}
|
||||
if self.borders.intersects(border::BOTTOM) {
|
||||
let y = area.bottom() - 1;
|
||||
for x in area.left()..area.right() {
|
||||
buf.set_cell(x,
|
||||
y,
|
||||
line::HORIZONTAL,
|
||||
self.border_color,
|
||||
self.background_color);
|
||||
buf.get_mut(x, y)
|
||||
.set_symbol(line::HORIZONTAL)
|
||||
.set_style(self.border_style);
|
||||
}
|
||||
}
|
||||
|
||||
// Corners
|
||||
if self.borders.contains(border::LEFT | border::TOP) {
|
||||
buf.set_symbol(area.left(), area.top(), line::TOP_LEFT);
|
||||
buf.get_mut(area.left(), area.top())
|
||||
.set_symbol(line::TOP_LEFT)
|
||||
.set_style(self.border_style);
|
||||
}
|
||||
if self.borders.contains(border::RIGHT | border::TOP) {
|
||||
buf.set_symbol(area.right() - 1, area.top(), line::TOP_RIGHT);
|
||||
buf.get_mut(area.right() - 1, area.top())
|
||||
.set_symbol(line::TOP_RIGHT)
|
||||
.set_style(self.border_style);
|
||||
}
|
||||
if self.borders.contains(border::LEFT | border::BOTTOM) {
|
||||
buf.set_symbol(area.left(), area.bottom() - 1, line::BOTTOM_LEFT);
|
||||
buf.get_mut(area.left(), area.bottom() - 1)
|
||||
.set_symbol(line::BOTTOM_LEFT)
|
||||
.set_style(self.border_style);
|
||||
}
|
||||
if self.borders.contains(border::RIGHT | border::BOTTOM) {
|
||||
buf.set_symbol(area.right() - 1, area.bottom() - 1, line::BOTTOM_RIGHT);
|
||||
buf.get_mut(area.right() - 1, area.bottom() - 1)
|
||||
.set_symbol(line::BOTTOM_RIGHT)
|
||||
.set_style(self.border_style);
|
||||
}
|
||||
|
||||
if area.width > 2 {
|
||||
|
@ -180,8 +178,7 @@ impl<'a> Widget for Block<'a> {
|
|||
area.top(),
|
||||
title,
|
||||
width as usize,
|
||||
self.title_color,
|
||||
self.background_color);
|
||||
&self.title_style);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ pub use self::points::Points;
|
|||
pub use self::line::Line;
|
||||
pub use self::map::{Map, MapResolution};
|
||||
|
||||
use style::Color;
|
||||
use style::{Style, Color};
|
||||
use buffer::Buffer;
|
||||
use widgets::{Block, Widget};
|
||||
use layout::Rect;
|
||||
|
@ -254,19 +254,16 @@ impl<'a, F> Widget for Canvas<'a, F>
|
|||
.enumerate() {
|
||||
if ch != BRAILLE_BLANK {
|
||||
let (x, y) = (i % width, i / width);
|
||||
buf.update_cell(x as u16 + canvas_area.left(),
|
||||
y as u16 + canvas_area.top(),
|
||||
|c| {
|
||||
c.symbol.clear();
|
||||
c.symbol.push(ch);
|
||||
c.fg = color;
|
||||
c.bg = self.background_color;
|
||||
});
|
||||
buf.get_mut(x as u16 + canvas_area.left(), y as u16 + canvas_area.top())
|
||||
.set_char(ch)
|
||||
.set_fg(color)
|
||||
.set_bg(self.background_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finally draw the labels
|
||||
let style = Style::default().bg(self.background_color);
|
||||
for label in ctx.labels.iter().filter(|l| {
|
||||
!(l.x < self.x_bounds[0] || l.x > self.x_bounds[1] || l.y < self.y_bounds[0] ||
|
||||
l.y > self.y_bounds[1])
|
||||
|
@ -278,8 +275,7 @@ impl<'a, F> Widget for Canvas<'a, F>
|
|||
buf.set_string(dx + canvas_area.left(),
|
||||
dy + canvas_area.top(),
|
||||
label.text,
|
||||
label.color,
|
||||
self.background_color);
|
||||
&style.fg(label.color));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,34 +6,34 @@ use widgets::{Widget, Block, border};
|
|||
use widgets::canvas::{Canvas, Points};
|
||||
use buffer::Buffer;
|
||||
use layout::Rect;
|
||||
use style::Color;
|
||||
use style::Style;
|
||||
use symbols;
|
||||
|
||||
/// An X or Y axis for the chart widget
|
||||
pub struct Axis<'a> {
|
||||
/// Title displayed next to axis end
|
||||
title: Option<&'a str>,
|
||||
/// Color of the title
|
||||
title_color: Color,
|
||||
/// Style of the title
|
||||
title_style: Style,
|
||||
/// Bounds for the axis (all data points outside these limits will not be represented)
|
||||
bounds: [f64; 2],
|
||||
/// A list of labels to put to the left or below the axis
|
||||
labels: Option<&'a [&'a str]>,
|
||||
/// The labels' color
|
||||
labels_color: Color,
|
||||
/// The color used to draw the axis itself
|
||||
color: Color,
|
||||
/// The labels' style
|
||||
labels_style: Style,
|
||||
/// The style used to draw the axis itself
|
||||
style: Style,
|
||||
}
|
||||
|
||||
impl<'a> Default for Axis<'a> {
|
||||
fn default() -> Axis<'a> {
|
||||
Axis {
|
||||
title: None,
|
||||
title_color: Color::Reset,
|
||||
title_style: Default::default(),
|
||||
bounds: [0.0, 0.0],
|
||||
labels: None,
|
||||
labels_color: Color::Reset,
|
||||
color: Color::Reset,
|
||||
labels_style: Default::default(),
|
||||
style: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -44,8 +44,8 @@ impl<'a> Axis<'a> {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn title_color(mut self, color: Color) -> Axis<'a> {
|
||||
self.title_color = color;
|
||||
pub fn title_style(mut self, style: Style) -> Axis<'a> {
|
||||
self.title_style = style;
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -59,13 +59,13 @@ impl<'a> Axis<'a> {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn labels_color(mut self, color: Color) -> Axis<'a> {
|
||||
self.labels_color = color;
|
||||
pub fn labels_style(mut self, style: Style) -> Axis<'a> {
|
||||
self.labels_style = style;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn color(mut self, color: Color) -> Axis<'a> {
|
||||
self.color = color;
|
||||
pub fn style(mut self, style: Style) -> Axis<'a> {
|
||||
self.style = style;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -86,8 +86,8 @@ pub struct Dataset<'a> {
|
|||
data: &'a [(f64, f64)],
|
||||
/// Symbol used for each points of this dataset
|
||||
marker: Marker,
|
||||
/// Color of the corresponding points and of the legend entry
|
||||
color: Color,
|
||||
/// Style used to plot this dataset
|
||||
style: Style,
|
||||
}
|
||||
|
||||
impl<'a> Default for Dataset<'a> {
|
||||
|
@ -96,7 +96,7 @@ impl<'a> Default for Dataset<'a> {
|
|||
name: "",
|
||||
data: &[],
|
||||
marker: Marker::Dot,
|
||||
color: Color::Reset,
|
||||
style: Style::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -117,8 +117,8 @@ impl<'a> Dataset<'a> {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn color(mut self, color: Color) -> Dataset<'a> {
|
||||
self.color = color;
|
||||
pub fn style(mut self, style: Style) -> Dataset<'a> {
|
||||
self.style = style;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -193,8 +193,8 @@ pub struct Chart<'a> {
|
|||
y_axis: Axis<'a>,
|
||||
/// A reference to the datasets
|
||||
datasets: &'a [Dataset<'a>],
|
||||
/// The background color
|
||||
background_color: Color,
|
||||
/// The widget base style
|
||||
style: Style,
|
||||
}
|
||||
|
||||
impl<'a> Default for Chart<'a> {
|
||||
|
@ -203,7 +203,7 @@ impl<'a> Default for Chart<'a> {
|
|||
block: None,
|
||||
x_axis: Axis::default(),
|
||||
y_axis: Axis::default(),
|
||||
background_color: Color::Reset,
|
||||
style: Default::default(),
|
||||
datasets: &[],
|
||||
}
|
||||
}
|
||||
|
@ -215,8 +215,8 @@ impl<'a> Chart<'a> {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn background_color(&mut self, background_color: Color) -> &mut Chart<'a> {
|
||||
self.background_color = background_color;
|
||||
pub fn style(&mut self, style: Style) -> &mut Chart<'a> {
|
||||
self.style = style;
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -318,18 +318,16 @@ impl<'a> Widget for Chart<'a> {
|
|||
return;
|
||||
}
|
||||
|
||||
if self.background_color != Color::Reset {
|
||||
self.background(&chart_area, buf, self.background_color);
|
||||
}
|
||||
self.background(&chart_area, buf, self.style.bg);
|
||||
|
||||
if let Some((x, y)) = layout.title_x {
|
||||
let title = self.x_axis.title.unwrap();
|
||||
buf.set_string(x, y, title, self.x_axis.title_color, self.background_color);
|
||||
buf.set_string(x, y, title, &self.x_axis.style);
|
||||
}
|
||||
|
||||
if let Some((x, y)) = layout.title_y {
|
||||
let title = self.y_axis.title.unwrap();
|
||||
buf.set_string(x, y, title, self.y_axis.title_color, self.background_color);
|
||||
buf.set_string(x, y, title, &self.y_axis.style);
|
||||
}
|
||||
|
||||
if let Some(y) = layout.label_x {
|
||||
|
@ -343,8 +341,7 @@ impl<'a> Widget for Chart<'a> {
|
|||
label.width() as u16,
|
||||
y,
|
||||
label,
|
||||
self.x_axis.labels_color,
|
||||
self.background_color);
|
||||
&self.x_axis.labels_style);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -358,39 +355,32 @@ impl<'a> Widget for Chart<'a> {
|
|||
buf.set_string(x,
|
||||
graph_area.bottom() - 1 - dy,
|
||||
label,
|
||||
self.y_axis.labels_color,
|
||||
self.background_color);
|
||||
&self.y_axis.labels_style);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(y) = layout.axis_x {
|
||||
for x in graph_area.left()..graph_area.right() {
|
||||
buf.set_cell(x,
|
||||
y,
|
||||
symbols::line::HORIZONTAL,
|
||||
self.x_axis.color,
|
||||
self.background_color);
|
||||
buf.get_mut(x, y)
|
||||
.set_symbol(symbols::line::HORIZONTAL)
|
||||
.set_style(self.x_axis.style);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(x) = layout.axis_y {
|
||||
for y in graph_area.top()..graph_area.bottom() {
|
||||
buf.set_cell(x,
|
||||
y,
|
||||
symbols::line::VERTICAL,
|
||||
self.y_axis.color,
|
||||
self.background_color);
|
||||
buf.get_mut(x, y)
|
||||
.set_symbol(symbols::line::VERTICAL)
|
||||
.set_style(self.y_axis.style);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(y) = layout.axis_x {
|
||||
if let Some(x) = layout.axis_y {
|
||||
buf.set_cell(x,
|
||||
y,
|
||||
symbols::line::BOTTOM_LEFT,
|
||||
self.x_axis.color,
|
||||
self.background_color);
|
||||
buf.get_mut(x, y)
|
||||
.set_symbol(symbols::line::BOTTOM_LEFT)
|
||||
.set_style(self.x_axis.style);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -409,22 +399,21 @@ impl<'a> Widget for Chart<'a> {
|
|||
(self.x_axis.bounds[1] -
|
||||
self.x_axis.bounds[0])) as u16;
|
||||
|
||||
buf.set_cell(graph_area.left() + dx,
|
||||
graph_area.top() + dy,
|
||||
symbols::DOT,
|
||||
dataset.color,
|
||||
self.background_color);
|
||||
buf.get_mut(graph_area.left() + dx, graph_area.top() + dy)
|
||||
.set_symbol(symbols::DOT)
|
||||
.set_fg(dataset.style.fg)
|
||||
.set_bg(dataset.style.bg);
|
||||
}
|
||||
}
|
||||
Marker::Braille => {
|
||||
Canvas::default()
|
||||
.background_color(self.background_color)
|
||||
.background_color(self.style.bg)
|
||||
.x_bounds(self.x_axis.bounds)
|
||||
.y_bounds(self.y_axis.bounds)
|
||||
.paint(|ctx| {
|
||||
ctx.draw(&Points {
|
||||
coords: dataset.data,
|
||||
color: dataset.color,
|
||||
color: dataset.style.fg,
|
||||
});
|
||||
})
|
||||
.draw(&graph_area, buf);
|
||||
|
@ -440,8 +429,7 @@ impl<'a> Widget for Chart<'a> {
|
|||
buf.set_string(legend_area.x + 1,
|
||||
legend_area.y + 1 + i as u16,
|
||||
dataset.name,
|
||||
dataset.color,
|
||||
self.background_color);
|
||||
&dataset.style);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use unicode_width::UnicodeWidthStr;
|
|||
|
||||
use widgets::{Widget, Block};
|
||||
use buffer::Buffer;
|
||||
use style::Color;
|
||||
use style::{Style, Color};
|
||||
use layout::Rect;
|
||||
|
||||
/// A widget to display a task progress.
|
||||
|
@ -12,11 +12,11 @@ use layout::Rect;
|
|||
/// ```
|
||||
/// # extern crate tui;
|
||||
/// # use tui::widgets::{Widget, Gauge, Block, border};
|
||||
/// # use tui::style::Color;
|
||||
/// # use tui::style::{Style, Color, ;
|
||||
/// # fn main() {
|
||||
/// Gauge::default()
|
||||
/// .block(Block::default().borders(border::ALL).title("Progress"))
|
||||
/// .color(Color::White)
|
||||
/// .style(Style::default().fg(Color::White).bg(Color::Black).modifier(Modifier::Italic))
|
||||
/// .background_color(Color::Black)
|
||||
/// .percent(20);
|
||||
/// # }
|
||||
|
@ -24,8 +24,7 @@ use layout::Rect;
|
|||
pub struct Gauge<'a> {
|
||||
block: Option<Block<'a>>,
|
||||
percent: u16,
|
||||
color: Color,
|
||||
background_color: Color,
|
||||
style: Style,
|
||||
}
|
||||
|
||||
impl<'a> Default for Gauge<'a> {
|
||||
|
@ -33,14 +32,13 @@ impl<'a> Default for Gauge<'a> {
|
|||
Gauge {
|
||||
block: None,
|
||||
percent: 0,
|
||||
color: Color::Reset,
|
||||
background_color: Color::Reset,
|
||||
style: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Gauge<'a> {
|
||||
pub fn block(&'a mut self, block: Block<'a>) -> &mut Gauge<'a> {
|
||||
pub fn block(&mut self, block: Block<'a>) -> &mut Gauge<'a> {
|
||||
self.block = Some(block);
|
||||
self
|
||||
}
|
||||
|
@ -50,13 +48,8 @@ impl<'a> Gauge<'a> {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn color(&mut self, color: Color) -> &mut Gauge<'a> {
|
||||
self.color = color;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn background_color(&mut self, color: Color) -> &mut Gauge<'a> {
|
||||
self.background_color = color;
|
||||
pub fn style(&mut self, style: Style) -> &mut Gauge<'a> {
|
||||
self.style = style;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -74,8 +67,8 @@ impl<'a> Widget for Gauge<'a> {
|
|||
return;
|
||||
}
|
||||
|
||||
if self.background_color != Color::Reset {
|
||||
self.background(&gauge_area, buf, self.background_color);
|
||||
if self.style.bg != Color::Reset {
|
||||
self.background(&gauge_area, buf, self.style.bg);
|
||||
}
|
||||
|
||||
// Gauge
|
||||
|
@ -83,21 +76,18 @@ impl<'a> Widget for Gauge<'a> {
|
|||
let end = gauge_area.left() + width;
|
||||
|
||||
for x in gauge_area.left()..end {
|
||||
buf.set_symbol(x, gauge_area.top(), " ");
|
||||
buf.get_mut(x, gauge_area.top())
|
||||
.set_symbol(" ");
|
||||
}
|
||||
|
||||
// Label
|
||||
let label = format!("{}%", self.percent);
|
||||
let label_width = label.width() as u16;
|
||||
let middle = (gauge_area.width - label_width) / 2 + gauge_area.left();
|
||||
buf.set_string(middle,
|
||||
gauge_area.top(),
|
||||
&label,
|
||||
self.color,
|
||||
self.background_color);
|
||||
buf.set_string(middle, gauge_area.top(), &label, &self.style);
|
||||
|
||||
for x in gauge_area.left()..end {
|
||||
buf.set_colors(x, gauge_area.top(), self.background_color, self.color);
|
||||
buf.get_mut(x, gauge_area.top()).set_fg(self.style.bg).set_bg(self.style.fg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use unicode_width::UnicodeWidthStr;
|
|||
use buffer::Buffer;
|
||||
use widgets::{Widget, Block};
|
||||
use layout::Rect;
|
||||
use style::Color;
|
||||
use style::Style;
|
||||
|
||||
/// A widget to display several items among which one can be selected (optional)
|
||||
///
|
||||
|
@ -31,12 +31,10 @@ pub struct List<'a> {
|
|||
items: &'a [&'a str],
|
||||
/// Index of the one selected
|
||||
selected: Option<usize>,
|
||||
/// Color used to render non selected items
|
||||
color: Color,
|
||||
/// Background color of the widget
|
||||
background_color: Color,
|
||||
/// Color used to render selected item
|
||||
highlight_color: Color,
|
||||
/// Base style of the widget
|
||||
style: Style,
|
||||
/// Style used to render selected item
|
||||
highlight_style: Style,
|
||||
/// Symbol in front of the selected item (Shift all items to the right)
|
||||
highlight_symbol: Option<&'a str>,
|
||||
}
|
||||
|
@ -47,9 +45,8 @@ impl<'a> Default for List<'a> {
|
|||
block: None,
|
||||
items: &[],
|
||||
selected: None,
|
||||
color: Color::Reset,
|
||||
background_color: Color::Reset,
|
||||
highlight_color: Color::Reset,
|
||||
style: Default::default(),
|
||||
highlight_style: Default::default(),
|
||||
highlight_symbol: None,
|
||||
}
|
||||
}
|
||||
|
@ -66,13 +63,8 @@ impl<'a> List<'a> {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn color(&'a mut self, color: Color) -> &mut List<'a> {
|
||||
self.color = color;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn background_color(&'a mut self, color: Color) -> &mut List<'a> {
|
||||
self.background_color = color;
|
||||
pub fn style(&'a mut self, style: Style) -> &mut List<'a> {
|
||||
self.style = style;
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -81,8 +73,8 @@ impl<'a> List<'a> {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn highlight_color(&'a mut self, highlight_color: Color) -> &mut List<'a> {
|
||||
self.highlight_color = highlight_color;
|
||||
pub fn highlight_style(&'a mut self, highlight_style: Style) -> &mut List<'a> {
|
||||
self.highlight_style = highlight_style;
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -107,52 +99,50 @@ impl<'a> Widget for List<'a> {
|
|||
return;
|
||||
}
|
||||
|
||||
if self.background_color != Color::Reset {
|
||||
self.background(&list_area, buf, self.background_color);
|
||||
}
|
||||
self.background(&list_area, buf, self.style.bg);
|
||||
|
||||
let list_length = self.items.len();
|
||||
let list_height = list_area.height as usize;
|
||||
let bound = min(list_height, list_length);
|
||||
let (selected, highlight_color) = match self.selected {
|
||||
Some(i) => (i, self.highlight_color),
|
||||
None => (0, self.color),
|
||||
|
||||
// Use highlight_style only if something is selected
|
||||
let (selected, highlight_style) = match self.selected {
|
||||
Some(i) => (i, &self.highlight_style),
|
||||
None => (0, &self.style),
|
||||
};
|
||||
|
||||
// Make sure the list show the selected item
|
||||
let offset = if selected >= list_height {
|
||||
selected - list_height + 1
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
// Move items to the right if a highlight symbol was provided
|
||||
let x = match self.highlight_symbol {
|
||||
Some(s) => (s.width() + 1) as u16 + list_area.left(),
|
||||
None => list_area.left(),
|
||||
};
|
||||
|
||||
// Render items
|
||||
if x < list_area.right() {
|
||||
let width = (list_area.right() - x) as usize;
|
||||
for i in 0..bound {
|
||||
let max_index = min(list_height, list_length);
|
||||
for i in 0..max_index {
|
||||
let index = i + offset;
|
||||
let item = self.items[index];
|
||||
let color = if index == selected {
|
||||
highlight_color
|
||||
let style = if index == selected {
|
||||
highlight_style
|
||||
} else {
|
||||
self.color
|
||||
&self.style
|
||||
};
|
||||
buf.set_stringn(x,
|
||||
list_area.top() + i as u16,
|
||||
item,
|
||||
width,
|
||||
color,
|
||||
self.background_color);
|
||||
buf.set_stringn(x, list_area.top() + i as u16, item, width, style);
|
||||
}
|
||||
|
||||
if let Some(s) = self.highlight_symbol {
|
||||
buf.set_string(list_area.left(),
|
||||
list_area.top() + (selected - offset) as u16,
|
||||
s,
|
||||
self.highlight_color,
|
||||
self.background_color);
|
||||
&self.highlight_style);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
mod block;
|
||||
mod text;
|
||||
mod paragraph;
|
||||
mod list;
|
||||
mod gauge;
|
||||
mod sparkline;
|
||||
|
@ -10,7 +10,7 @@ mod table;
|
|||
pub mod canvas;
|
||||
|
||||
pub use self::block::Block;
|
||||
pub use self::text::Text;
|
||||
pub use self::paragraph::Paragraph;
|
||||
pub use self::list::List;
|
||||
pub use self::gauge::Gauge;
|
||||
pub use self::sparkline::Sparkline;
|
||||
|
@ -53,12 +53,12 @@ pub trait Widget {
|
|||
fn background(&self, area: &Rect, buf: &mut Buffer, color: Color) {
|
||||
for y in area.top()..area.bottom() {
|
||||
for x in area.left()..area.right() {
|
||||
buf.set_bg(x, y, color);
|
||||
buf.get_mut(x, y).set_bg(color);
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Helper method that can be chained with a widget's builder methods to render it.
|
||||
fn render<B>(&self, area: &Rect, t: &mut Terminal<B>)
|
||||
fn render<B>(&self, t: &mut Terminal<B>, area: &Rect)
|
||||
where Self: Sized,
|
||||
B: Backend
|
||||
{
|
||||
|
|
|
@ -4,7 +4,7 @@ use unicode_width::UnicodeWidthStr;
|
|||
use widgets::{Widget, Block};
|
||||
use buffer::Buffer;
|
||||
use layout::Rect;
|
||||
use style::Color;
|
||||
use style::{Style, Color, Modifier};
|
||||
|
||||
/// A widget to display some text. You can specify colors using commands embedded in
|
||||
/// the text such as "{[color] [text]}".
|
||||
|
@ -13,64 +13,56 @@ use style::Color;
|
|||
///
|
||||
/// ```
|
||||
/// # extern crate tui;
|
||||
/// # use tui::widgets::{Block, border, Text};
|
||||
/// # use tui::widgets::{Block, border, Paragraph};
|
||||
/// # use tui::style::Color;
|
||||
/// # fn main() {
|
||||
/// Text::default()
|
||||
/// .block(Block::default().title("Text").borders(border::ALL))
|
||||
/// Paragraph::default()
|
||||
/// .block(Block::default().title("Paragraph").borders(border::ALL))
|
||||
/// .color(Color::White)
|
||||
/// .background_color(Color::Black)
|
||||
/// .wrap(true)
|
||||
/// .text("First line\nSecond line\n{red Colored text}.");
|
||||
/// # }
|
||||
/// ```
|
||||
pub struct Text<'a> {
|
||||
pub struct Paragraph<'a> {
|
||||
/// A block to wrap the widget in
|
||||
block: Option<Block<'a>>,
|
||||
/// The base color used to render the text
|
||||
color: Color,
|
||||
/// Background color of the widget
|
||||
background_color: Color,
|
||||
/// Widget style
|
||||
style: Style,
|
||||
/// Wrap the text or not
|
||||
wrapping: bool,
|
||||
/// The text to display
|
||||
text: &'a str,
|
||||
}
|
||||
|
||||
impl<'a> Default for Text<'a> {
|
||||
fn default() -> Text<'a> {
|
||||
Text {
|
||||
impl<'a> Default for Paragraph<'a> {
|
||||
fn default() -> Paragraph<'a> {
|
||||
Paragraph {
|
||||
block: None,
|
||||
color: Color::Reset,
|
||||
background_color: Color::Reset,
|
||||
style: Default::default(),
|
||||
wrapping: false,
|
||||
text: "",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Text<'a> {
|
||||
pub fn block(&'a mut self, block: Block<'a>) -> &mut Text<'a> {
|
||||
impl<'a> Paragraph<'a> {
|
||||
pub fn block(&'a mut self, block: Block<'a>) -> &mut Paragraph<'a> {
|
||||
self.block = Some(block);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn text(&mut self, text: &'a str) -> &mut Text<'a> {
|
||||
pub fn text(&mut self, text: &'a str) -> &mut Paragraph<'a> {
|
||||
self.text = text;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn background_color(&mut self, background_color: Color) -> &mut Text<'a> {
|
||||
self.background_color = background_color;
|
||||
pub fn style(&mut self, style: Style) -> &mut Paragraph<'a> {
|
||||
self.style = style;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn color(&mut self, color: Color) -> &mut Text<'a> {
|
||||
self.color = color;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn wrap(&mut self, flag: bool) -> &mut Text<'a> {
|
||||
pub fn wrap(&mut self, flag: bool) -> &mut Paragraph<'a> {
|
||||
self.wrapping = flag;
|
||||
self
|
||||
}
|
||||
|
@ -79,28 +71,54 @@ impl<'a> Text<'a> {
|
|||
struct Parser<'a> {
|
||||
text: Vec<&'a str>,
|
||||
mark: bool,
|
||||
color_string: String,
|
||||
color: Color,
|
||||
base_color: Color,
|
||||
cmd_string: String,
|
||||
style: Style,
|
||||
base_style: Style,
|
||||
escaping: bool,
|
||||
coloring: bool,
|
||||
styling: bool,
|
||||
}
|
||||
|
||||
impl<'a> Parser<'a> {
|
||||
fn new(text: &'a str, base_color: Color) -> Parser<'a> {
|
||||
fn new(text: &'a str, base_style: Style) -> Parser<'a> {
|
||||
Parser {
|
||||
text: UnicodeSegmentation::graphemes(text, true).rev().collect::<Vec<&str>>(),
|
||||
mark: false,
|
||||
color_string: String::from(""),
|
||||
color: base_color,
|
||||
base_color: base_color,
|
||||
cmd_string: String::from(""),
|
||||
style: base_style,
|
||||
base_style: base_style,
|
||||
escaping: false,
|
||||
coloring: false,
|
||||
styling: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn update_color(&mut self) {
|
||||
self.color = match &*self.color_string {
|
||||
fn update_style(&mut self) {
|
||||
for cmd in self.cmd_string.split(';') {
|
||||
let args = cmd.split('=').collect::<Vec<&str>>();
|
||||
if let Some(first) = args.get(0) {
|
||||
match *first {
|
||||
"fg" => {
|
||||
if let Some(snd) = args.get(1) {
|
||||
self.style.fg = Parser::str_to_color(snd);
|
||||
}
|
||||
}
|
||||
"bg" => {
|
||||
if let Some(snd) = args.get(1) {
|
||||
self.style.bg = Parser::str_to_color(snd);
|
||||
}
|
||||
}
|
||||
"mod" => {
|
||||
if let Some(snd) = args.get(1) {
|
||||
self.style.modifier = Parser::str_to_modifier(snd);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn str_to_color(string: &str) -> Color {
|
||||
match string {
|
||||
"black" => Color::Black,
|
||||
"red" => Color::Red,
|
||||
"green" => Color::Green,
|
||||
|
@ -119,22 +137,32 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn str_to_modifier(string: &str) -> Modifier {
|
||||
match string {
|
||||
"bold" => Modifier::Bold,
|
||||
"italic" => Modifier::Italic,
|
||||
"underline" => Modifier::Underline,
|
||||
"invert" => Modifier::Invert,
|
||||
_ => Modifier::Reset,
|
||||
}
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
self.coloring = false;
|
||||
self.styling = false;
|
||||
self.mark = false;
|
||||
self.color = self.base_color;
|
||||
self.color_string.clear();
|
||||
self.style = self.base_style;
|
||||
self.cmd_string.clear();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Parser<'a> {
|
||||
type Item = (&'a str, Color);
|
||||
fn next(&mut self) -> Option<(&'a str, Color)> {
|
||||
type Item = (&'a str, Style);
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self.text.pop() {
|
||||
Some(s) => {
|
||||
if s == "\\" {
|
||||
if self.escaping {
|
||||
Some((s, self.color))
|
||||
Some((s, self.style))
|
||||
} else {
|
||||
self.escaping = true;
|
||||
self.next()
|
||||
|
@ -142,9 +170,9 @@ impl<'a> Iterator for Parser<'a> {
|
|||
} else if s == "{" {
|
||||
if self.escaping {
|
||||
self.escaping = false;
|
||||
Some((s, self.color))
|
||||
Some((s, self.style))
|
||||
} else {
|
||||
self.color = self.base_color;
|
||||
self.style = self.base_style;
|
||||
self.mark = true;
|
||||
self.next()
|
||||
}
|
||||
|
@ -152,14 +180,14 @@ impl<'a> Iterator for Parser<'a> {
|
|||
self.reset();
|
||||
self.next()
|
||||
} else if s == " " && self.mark {
|
||||
self.coloring = true;
|
||||
self.update_color();
|
||||
self.styling = true;
|
||||
self.update_style();
|
||||
self.next()
|
||||
} else if self.mark && !self.coloring {
|
||||
self.color_string.push_str(s);
|
||||
} else if self.mark && !self.styling {
|
||||
self.cmd_string.push_str(s);
|
||||
self.next()
|
||||
} else {
|
||||
Some((s, self.color))
|
||||
Some((s, self.style))
|
||||
}
|
||||
}
|
||||
None => None,
|
||||
|
@ -167,7 +195,7 @@ impl<'a> Iterator for Parser<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Widget for Text<'a> {
|
||||
impl<'a> Widget for Paragraph<'a> {
|
||||
fn draw(&self, area: &Rect, buf: &mut Buffer) {
|
||||
let text_area = match self.block {
|
||||
Some(ref b) => {
|
||||
|
@ -181,14 +209,12 @@ impl<'a> Widget for Text<'a> {
|
|||
return;
|
||||
}
|
||||
|
||||
if self.background_color != Color::Reset {
|
||||
self.background(&text_area, buf, self.background_color);
|
||||
}
|
||||
self.background(&text_area, buf, self.style.bg);
|
||||
|
||||
let mut x = 0;
|
||||
let mut y = 0;
|
||||
for (s, c) in Parser::new(self.text, self.color) {
|
||||
if s == "\n" {
|
||||
for (string, style) in Parser::new(self.text, self.style) {
|
||||
if string == "\n" {
|
||||
x = 0;
|
||||
y += 1;
|
||||
continue;
|
||||
|
@ -205,12 +231,10 @@ impl<'a> Widget for Text<'a> {
|
|||
break;
|
||||
}
|
||||
|
||||
buf.set_cell(text_area.left() + x,
|
||||
text_area.top() + y,
|
||||
s,
|
||||
c,
|
||||
self.background_color);
|
||||
x += s.width() as u16;
|
||||
buf.get_mut(text_area.left() + x, text_area.top() + y)
|
||||
.set_symbol(string)
|
||||
.set_style(style);
|
||||
x += string.width() as u16;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@ use std::cmp::min;
|
|||
use layout::Rect;
|
||||
use buffer::Buffer;
|
||||
use widgets::{Widget, Block};
|
||||
use style::Color;
|
||||
use style::Style;
|
||||
use symbols::bar;
|
||||
|
||||
/// Widget to render a sparkline over one or more lines.
|
||||
|
@ -26,10 +26,8 @@ use symbols::bar;
|
|||
pub struct Sparkline<'a> {
|
||||
/// A block to wrap the widget in
|
||||
block: Option<Block<'a>>,
|
||||
/// Color of the bars
|
||||
color: Color,
|
||||
/// Background color of the widget
|
||||
background_color: Color,
|
||||
/// Widget style
|
||||
style: Style,
|
||||
/// A slice of the data to display
|
||||
data: &'a [u64],
|
||||
/// The maximum value to take to compute the maximum bar height (if nothing is specified, the
|
||||
|
@ -41,8 +39,7 @@ impl<'a> Default for Sparkline<'a> {
|
|||
fn default() -> Sparkline<'a> {
|
||||
Sparkline {
|
||||
block: None,
|
||||
color: Color::Reset,
|
||||
background_color: Color::Reset,
|
||||
style: Default::default(),
|
||||
data: &[],
|
||||
max: None,
|
||||
}
|
||||
|
@ -55,17 +52,11 @@ impl<'a> Sparkline<'a> {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn color(&mut self, color: Color) -> &mut Sparkline<'a> {
|
||||
self.color = color;
|
||||
pub fn style(&mut self, style: Style) -> &mut Sparkline<'a> {
|
||||
self.style = style;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn background_color(&mut self, color: Color) -> &mut Sparkline<'a> {
|
||||
self.background_color = color;
|
||||
self
|
||||
}
|
||||
|
||||
|
||||
pub fn data(&mut self, data: &'a [u64]) -> &mut Sparkline<'a> {
|
||||
self.data = data;
|
||||
self
|
||||
|
@ -114,11 +105,10 @@ impl<'a> Widget for Sparkline<'a> {
|
|||
7 => bar::SEVEN_EIGHTHS,
|
||||
_ => bar::FULL,
|
||||
};
|
||||
buf.set_cell(spark_area.left() + i as u16,
|
||||
spark_area.top() + j,
|
||||
symbol,
|
||||
self.color,
|
||||
self.background_color);
|
||||
buf.get_mut(spark_area.left() + i as u16, spark_area.top() + j)
|
||||
.set_symbol(symbol)
|
||||
.set_fg(self.style.fg)
|
||||
.set_bg(self.style.bg);
|
||||
|
||||
if *d > 8 {
|
||||
*d -= 8;
|
||||
|
|
|
@ -6,7 +6,7 @@ use unicode_width::UnicodeWidthStr;
|
|||
use buffer::Buffer;
|
||||
use widgets::{Widget, Block};
|
||||
use layout::Rect;
|
||||
use style::Color;
|
||||
use style::Style;
|
||||
|
||||
/// A widget to display data in formatted column
|
||||
///
|
||||
|
@ -32,10 +32,12 @@ use style::Color;
|
|||
pub struct Table<'a> {
|
||||
/// A block to wrap the widget in
|
||||
block: Option<Block<'a>>,
|
||||
/// Base style for the widget
|
||||
style: Style,
|
||||
/// Header row for all columns
|
||||
header: &'a [&'a str],
|
||||
/// Color of the text in the header
|
||||
header_color: Color,
|
||||
/// Style for the header
|
||||
header_style: Style,
|
||||
/// Width of each column (if the total width is greater than the widget width some columns may
|
||||
/// not be displayed)
|
||||
widths: &'a [u16],
|
||||
|
@ -43,23 +45,21 @@ pub struct Table<'a> {
|
|||
column_spacing: u16,
|
||||
/// Data to display in each row
|
||||
rows: Vec<Cow<'a, [&'a str]>>,
|
||||
/// Color of the text
|
||||
color: Color,
|
||||
/// Background color for the widget
|
||||
background_color: Color,
|
||||
/// Style for each row
|
||||
row_style: Style,
|
||||
}
|
||||
|
||||
impl<'a> Default for Table<'a> {
|
||||
fn default() -> Table<'a> {
|
||||
Table {
|
||||
block: None,
|
||||
style: Style::default(),
|
||||
header: &[],
|
||||
header_color: Color::Reset,
|
||||
header_style: Style::default(),
|
||||
widths: &[],
|
||||
rows: Vec::new(),
|
||||
color: Color::Reset,
|
||||
row_style: Style::default(),
|
||||
column_spacing: 1,
|
||||
background_color: Color::Reset,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -75,8 +75,8 @@ impl<'a> Table<'a> {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn header_color(&mut self, color: Color) -> &mut Table<'a> {
|
||||
self.header_color = color;
|
||||
pub fn header_style(&mut self, style: Style) -> &mut Table<'a> {
|
||||
self.header_style = style;
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -92,8 +92,8 @@ impl<'a> Table<'a> {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn color(&mut self, color: Color) -> &mut Table<'a> {
|
||||
self.color = color;
|
||||
pub fn row_style(&mut self, style: Style) -> &mut Table<'a> {
|
||||
self.row_style = style;
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -101,11 +101,6 @@ impl<'a> Table<'a> {
|
|||
self.column_spacing = spacing;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn background_color(&mut self, color: Color) -> &mut Table<'a> {
|
||||
self.background_color = color;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Widget for Table<'a> {
|
||||
|
@ -121,9 +116,7 @@ impl<'a> Widget for Table<'a> {
|
|||
};
|
||||
|
||||
// Set the background
|
||||
if self.background_color != Color::Reset {
|
||||
self.background(&table_area, buf, self.background_color);
|
||||
}
|
||||
self.background(&table_area, buf, self.style.bg);
|
||||
|
||||
let mut x = 0;
|
||||
let mut widths = Vec::with_capacity(self.widths.len());
|
||||
|
@ -137,10 +130,11 @@ impl<'a> Widget for Table<'a> {
|
|||
|
||||
let mut y = table_area.top();
|
||||
|
||||
// Header
|
||||
if y < table_area.bottom() {
|
||||
x = table_area.left();
|
||||
for (w, t) in widths.iter().zip(self.header.iter()) {
|
||||
buf.set_string(x, y, t, self.header_color, self.background_color);
|
||||
buf.set_string(x, y, t, &self.header_style);
|
||||
x += *w + self.column_spacing;
|
||||
}
|
||||
}
|
||||
|
@ -151,12 +145,7 @@ impl<'a> Widget for Table<'a> {
|
|||
for (i, row) in self.rows.iter().take(remaining).enumerate() {
|
||||
x = table_area.left();
|
||||
for (w, elt) in widths.iter().zip(row.iter()) {
|
||||
buf.set_stringn(x,
|
||||
y + i as u16,
|
||||
elt,
|
||||
*w as usize,
|
||||
self.color,
|
||||
self.background_color);
|
||||
buf.set_stringn(x, y + i as u16, elt, *w as usize, &self.row_style);
|
||||
x += *w + self.column_spacing;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ use unicode_width::UnicodeWidthStr;
|
|||
use widgets::{Block, Widget};
|
||||
use buffer::Buffer;
|
||||
use layout::Rect;
|
||||
use style::Color;
|
||||
use style::Style;
|
||||
use symbols::line;
|
||||
|
||||
/// A widget to display available tabs in a multiple panels context.
|
||||
|
@ -30,12 +30,10 @@ pub struct Tabs<'a> {
|
|||
titles: &'a [&'a str],
|
||||
/// The index of the selected tabs
|
||||
selected: usize,
|
||||
/// The color used to draw the text
|
||||
color: Color,
|
||||
/// The background color of this widget
|
||||
background_color: Color,
|
||||
/// The color used to display the selected item
|
||||
highlight_color: Color,
|
||||
/// The style used to draw the text
|
||||
style: Style,
|
||||
/// The style used to display the selected item
|
||||
highlight_style: Style,
|
||||
}
|
||||
|
||||
impl<'a> Default for Tabs<'a> {
|
||||
|
@ -44,9 +42,8 @@ impl<'a> Default for Tabs<'a> {
|
|||
block: None,
|
||||
titles: &[],
|
||||
selected: 0,
|
||||
color: Color::Reset,
|
||||
background_color: Color::Reset,
|
||||
highlight_color: Color::Reset,
|
||||
style: Default::default(),
|
||||
highlight_style: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -67,18 +64,13 @@ impl<'a> Tabs<'a> {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn color(&mut self, color: Color) -> &mut Tabs<'a> {
|
||||
self.color = color;
|
||||
pub fn style(&mut self, style: Style) -> &mut Tabs<'a> {
|
||||
self.style = style;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn background_color(&mut self, color: Color) -> &mut Tabs<'a> {
|
||||
self.background_color = color;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn highlight_color(&mut self, color: Color) -> &mut Tabs<'a> {
|
||||
self.highlight_color = color;
|
||||
pub fn highlight_style(&mut self, style: Style) -> &mut Tabs<'a> {
|
||||
self.highlight_style = style;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -98,30 +90,27 @@ impl<'a> Widget for Tabs<'a> {
|
|||
return;
|
||||
}
|
||||
|
||||
if self.background_color != Color::Reset {
|
||||
self.background(&tabs_area, buf, self.background_color);
|
||||
}
|
||||
self.background(&tabs_area, buf, self.style.bg);
|
||||
|
||||
let mut x = tabs_area.left();
|
||||
for (title, color) in self.titles.iter().enumerate().map(|(i, t)| if i == self.selected {
|
||||
(t, self.highlight_color)
|
||||
for (title, style) in self.titles.iter().enumerate().map(|(i, t)| if i == self.selected {
|
||||
(t, &self.highlight_style)
|
||||
} else {
|
||||
(t, self.color)
|
||||
(t, &self.style)
|
||||
}) {
|
||||
x += 1;
|
||||
if x > tabs_area.right() {
|
||||
break;
|
||||
} else {
|
||||
buf.set_string(x, tabs_area.top(), title, color, self.background_color);
|
||||
buf.set_string(x, tabs_area.top(), title, style);
|
||||
x += title.width() as u16 + 1;
|
||||
if x >= tabs_area.right() {
|
||||
break;
|
||||
} else {
|
||||
buf.set_cell(x,
|
||||
tabs_area.top(),
|
||||
line::VERTICAL,
|
||||
self.color,
|
||||
self.background_color);
|
||||
buf.get_mut(x, tabs_area.top())
|
||||
.set_symbol(line::VERTICAL)
|
||||
.set_fg(self.style.fg)
|
||||
.set_bg(self.style.bg);
|
||||
x += 1;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue