Simpler layout and cleanup api

pull/3/head
Florian Dehau 8 years ago
parent b411690fdd
commit ea485b5439

@ -23,7 +23,7 @@ use log4rs::config::{Appender, Config, Root};
use tui::Terminal;
use tui::widgets::{Widget, Block, List, Gauge, Sparkline, Text, border, Chart, Axis, Dataset,
BarChart};
use tui::layout::{Group, Direction, Alignment, Size};
use tui::layout::{Group, Direction, Size};
use tui::style::Color;
#[derive(Clone)]
@ -234,44 +234,41 @@ fn draw(t: &mut Terminal, app: &App) {
Group::default()
.direction(Direction::Vertical)
.alignment(Alignment::Left)
.chunks(&[Size::Fixed(7), Size::Min(5), Size::Fixed(7)])
.sizes(&[Size::Fixed(7), Size::Min(7), Size::Fixed(7)])
.render(t, &size, |t, chunks| {
Block::default().borders(border::ALL).title("Graphs").render(&chunks[0], t);
Group::default()
.direction(Direction::Vertical)
.alignment(Alignment::Left)
.margin(1)
.chunks(&[Size::Fixed(2), Size::Fixed(3)])
.sizes(&[Size::Fixed(2), Size::Fixed(3)])
.render(t, &chunks[0], |t, chunks| {
Gauge::default()
.block(Block::default().title("Gauge:"))
.bg(Color::Magenta)
.background_color(Color::Magenta)
.percent(app.progress)
.render(&chunks[0], t);
Sparkline::default()
.block(Block::default().title("Sparkline:"))
.fg(Color::Green)
.color(Color::Green)
.data(&app.data)
.render(&chunks[1], t);
});
let sizes = if app.show_chart {
vec![Size::Max(40), Size::Min(20)]
vec![Size::Percent(50), Size::Percent(50)]
} else {
vec![Size::Max(40)]
vec![Size::Percent(100)]
};
Group::default()
.direction(Direction::Horizontal)
.alignment(Alignment::Left)
.chunks(&sizes)
.sizes(&sizes)
.render(t, &chunks[1], |t, chunks| {
Group::default()
.direction(Direction::Vertical)
.chunks(&[Size::Min(20), Size::Max(40)])
.sizes(&[Size::Percent(50), Size::Percent(50)])
.render(t, &chunks[0], |t, chunks| {
Group::default()
.direction(Direction::Horizontal)
.chunks(&[Size::Max(20), Size::Min(0)])
.sizes(&[Size::Percent(50), Size::Percent(50)])
.render(t, &chunks[0], |t, chunks| {
List::default()
.block(Block::default().borders(border::ALL).title("List"))

@ -3,21 +3,12 @@ use std::collections::HashMap;
use cassowary::{Solver, Variable, Constraint};
use cassowary::WeightedRelation::*;
use cassowary::strength::{WEAK, REQUIRED};
use cassowary::strength::{REQUIRED, WEAK};
use terminal::Terminal;
use util::hash;
#[derive(Hash)]
pub enum Alignment {
Top,
Left,
Center,
Bottom,
Right,
}
#[derive(Hash)]
#[derive(Hash, PartialEq)]
pub enum Direction {
Horizontal,
Vertical,
@ -104,6 +95,7 @@ impl Rect {
#[derive(Debug, Clone, Hash)]
pub enum Size {
Fixed(u16),
Percent(u16),
Max(u16),
Min(u16),
}
@ -123,12 +115,7 @@ pub enum Size {
///
/// ```
#[allow(unused_variables)]
pub fn split(area: &Rect,
dir: &Direction,
align: &Alignment,
margin: u16,
sizes: &[Size])
-> Vec<Rect> {
pub fn split(area: &Rect, dir: &Direction, margin: u16, sizes: &[Size]) -> Vec<Rect> {
let mut solver = Solver::new();
let mut vars: HashMap<Variable, (usize, usize)> = HashMap::new();
let elements = sizes.iter().map(|_| Element::new()).collect::<Vec<Element>>();
@ -145,15 +132,15 @@ pub fn split(area: &Rect,
constraints.push(match *dir {
Direction::Horizontal => first.x | EQ(REQUIRED) | dest_area.x as f64,
Direction::Vertical => first.y | EQ(REQUIRED) | dest_area.y as f64,
})
});
}
if let Some(last) = elements.last() {
constraints.push(match *dir {
Direction::Horizontal => {
(last.x + last.width) | EQ(WEAK) | (dest_area.x + dest_area.width) as f64
(last.x + last.width) | EQ(REQUIRED) | (dest_area.x + dest_area.width) as f64
}
Direction::Vertical => {
(last.y + last.height) | EQ(WEAK) | (dest_area.y + dest_area.height) as f64
(last.y + last.height) | EQ(REQUIRED) | (dest_area.y + dest_area.height) as f64
}
})
}
@ -163,14 +150,16 @@ pub fn split(area: &Rect,
constraints.push((pair[0].x + pair[0].width) | EQ(REQUIRED) | pair[1].x);
}
for (i, size) in sizes.iter().enumerate() {
let cs = [elements[i].y | EQ(REQUIRED) | dest_area.y as f64,
elements[i].height | EQ(REQUIRED) | dest_area.height as f64,
match *size {
Size::Fixed(v) => elements[i].width | EQ(REQUIRED) | v as f64,
Size::Min(v) => elements[i].width | GE(REQUIRED) | v as f64,
Size::Max(v) => elements[i].width | LE(REQUIRED) | v as f64,
}];
constraints.extend_from_slice(&cs);
constraints.push(elements[i].y | EQ(REQUIRED) | dest_area.y as f64);
constraints.push(elements[i].height | EQ(REQUIRED) | dest_area.height as f64);
constraints.push(match *size {
Size::Fixed(v) => elements[i].width | EQ(WEAK) | v as f64,
Size::Percent(v) => {
elements[i].width | EQ(WEAK) | ((v * dest_area.width) as f64 / 100.0)
}
Size::Min(v) => elements[i].width | GE(WEAK) | v as f64,
Size::Max(v) => elements[i].width | LE(WEAK) | v as f64,
});
}
}
Direction::Vertical => {
@ -178,14 +167,16 @@ pub fn split(area: &Rect,
constraints.push((pair[0].y + pair[0].height) | EQ(REQUIRED) | pair[1].y);
}
for (i, size) in sizes.iter().enumerate() {
let cs = [elements[i].x | EQ(REQUIRED) | dest_area.x as f64,
elements[i].width | EQ(REQUIRED) | dest_area.width as f64,
match *size {
Size::Fixed(v) => elements[i].height | EQ(REQUIRED) | v as f64,
Size::Min(v) => elements[i].height | GE(REQUIRED) | v as f64,
Size::Max(v) => elements[i].height | LE(REQUIRED) | v as f64,
}];
constraints.extend_from_slice(&cs);
constraints.push(elements[i].x | EQ(REQUIRED) | dest_area.x as f64);
constraints.push(elements[i].width | EQ(REQUIRED) | dest_area.width as f64);
constraints.push(match *size {
Size::Fixed(v) => elements[i].height | EQ(WEAK) | v as f64,
Size::Percent(v) => {
elements[i].height | EQ(WEAK) | ((v * dest_area.height) as f64 / 100.0)
}
Size::Min(v) => elements[i].height | GE(WEAK) | v as f64,
Size::Max(v) => elements[i].height | LE(WEAK) | v as f64,
});
}
}
}
@ -193,30 +184,32 @@ pub fn split(area: &Rect,
// TODO: Find a better way to handle overflow error
for &(var, value) in solver.fetch_changes() {
let (index, attr) = vars[&var];
let value = value as u16;
match attr {
0 => {
results[index].x = value as u16;
if value <= area.width {
results[index].x = value;
}
}
1 => {
results[index].y = value as u16;
if value <= area.height {
results[index].y = value;
}
}
2 => {
let mut v = value as u16;
if v > area.width {
v = 0;
if value <= area.width {
results[index].width = value;
}
results[index].width = v;
}
3 => {
let mut v = value as u16;
if v > area.height {
v = 0;
if value <= area.height {
results[index].height = value;
}
results[index].height = v;
}
_ => {}
}
}
info!("{:?}, {:?}", results, dest_area);
results
}
@ -241,18 +234,16 @@ impl Element {
#[derive(Hash)]
pub struct Group {
direction: Direction,
alignment: Alignment,
margin: u16,
chunks: Vec<Size>,
sizes: Vec<Size>,
}
impl Default for Group {
fn default() -> Group {
Group {
direction: Direction::Horizontal,
alignment: Alignment::Left,
margin: 0,
chunks: Vec::new(),
sizes: Vec::new(),
}
}
}
@ -263,18 +254,13 @@ impl Group {
self
}
pub fn alignment(&mut self, alignment: Alignment) -> &mut Group {
self.alignment = alignment;
self
}
pub fn margin(&mut self, margin: u16) -> &mut Group {
self.margin = margin;
self
}
pub fn chunks(&mut self, chunks: &[Size]) -> &mut Group {
self.chunks = Vec::from(chunks);
pub fn sizes(&mut self, sizes: &[Size]) -> &mut Group {
self.sizes = Vec::from(sizes);
self
}
pub fn render<F>(&self, t: &mut Terminal, area: &Rect, mut f: F)
@ -283,14 +269,7 @@ impl Group {
let hash = hash(self, area);
let (cache_update, chunks) = match t.get_layout(hash) {
Some(chs) => (false, chs.to_vec()),
None => {
(true,
split(area,
&self.direction,
&self.alignment,
self.margin,
&self.chunks))
}
None => (true, split(area, &self.direction, self.margin, &self.sizes)),
};
f(t, &chunks);

@ -160,8 +160,11 @@ impl<'a> Chart<'a> {
fn layout(&self, inner: &Rect, outer: &Rect) -> ChartLayout {
let mut layout = ChartLayout::default();
if inner.height == 0 || inner.width == 0 {
return layout;
}
let mut x = inner.x - outer.x;
let mut y = inner.height - 1 + (inner.y - outer.y);
let mut y = inner.height + (inner.y - outer.y) - 1;
if self.x_axis.labels.is_some() && y > 1 {
layout.label_x = Some(y);
@ -215,6 +218,9 @@ impl<'a> Widget<'a> for Chart<'a> {
};
let layout = self.layout(&chart_area, area);
if layout.graph_area.width == 0 || layout.graph_area.height == 0 {
return buf;
}
let width = layout.graph_area.width;
let height = layout.graph_area.height;
let margin_x = layout.graph_area.x - area.x;

@ -23,8 +23,8 @@ pub struct Gauge<'a> {
block: Option<Block<'a>>,
percent: u16,
percent_string: String,
fg: Color,
bg: Color,
color: Color,
background_color: Color,
}
impl<'a> Default for Gauge<'a> {
@ -33,8 +33,8 @@ impl<'a> Default for Gauge<'a> {
block: None,
percent: 0,
percent_string: String::from("0%"),
bg: Color::Reset,
fg: Color::Reset,
color: Color::Reset,
background_color: Color::Reset,
}
}
}
@ -51,13 +51,13 @@ impl<'a> Gauge<'a> {
self
}
pub fn bg(&mut self, bg: Color) -> &mut Gauge<'a> {
self.bg = bg;
pub fn color(&mut self, color: Color) -> &mut Gauge<'a> {
self.color = color;
self
}
pub fn fg(&mut self, fg: Color) -> &mut Gauge<'a> {
self.fg = fg;
pub fn background_color(&mut self, color: Color) -> &mut Gauge<'a> {
self.background_color = color;
self
}
}
@ -76,15 +76,23 @@ impl<'a> Widget<'a> for Gauge<'a> {
// Gauge
let width = (gauge_area.width * self.percent) / 100;
for i in 0..width {
buf.update_cell(margin_x + i, margin_y, " ", self.fg, self.bg);
buf.update_cell(margin_x + i,
margin_y,
" ",
self.color,
self.background_color);
}
// Label
let len = self.percent_string.len() as u16;
let middle = gauge_area.width / 2 - len / 2;
buf.set_string(middle, margin_y, &self.percent_string, self.bg, self.fg);
buf.set_string(middle,
margin_y,
&self.percent_string,
self.background_color,
self.color);
let bound = max(middle, min(middle + len, width));
for i in middle..bound {
buf.update_colors(margin_x + i, margin_y, self.fg, self.bg);
buf.update_colors(margin_x + i, margin_y, self.color, self.background_color);
}
}
buf

@ -12,8 +12,8 @@ pub struct List<'a> {
selected: usize,
selection_symbol: Option<&'a str>,
selection_color: Color,
text_color: Color,
bg: Color,
color: Color,
background_color: Color,
items: &'a [&'a str],
}
@ -24,8 +24,8 @@ impl<'a> Default for List<'a> {
selected: 0,
selection_symbol: None,
selection_color: Color::Reset,
text_color: Color::Reset,
bg: Color::Reset,
color: Color::Reset,
background_color: Color::Reset,
items: &[],
}
}
@ -42,13 +42,13 @@ impl<'a> List<'a> {
self
}
pub fn text_color(&'a mut self, text_color: Color) -> &mut List<'a> {
self.text_color = text_color;
pub fn color(&'a mut self, color: Color) -> &mut List<'a> {
self.color = color;
self
}
pub fn bg(&'a mut self, bg: Color) -> &mut List<'a> {
self.bg = bg;
pub fn background_color(&'a mut self, color: Color) -> &mut List<'a> {
self.background_color = color;
self
}
@ -93,16 +93,16 @@ impl<'a> Widget<'a> for List<'a> {
let color = if index == self.selected {
self.selection_color
} else {
self.text_color
self.color
};
buf.set_string(x, 1 + i as u16, item, color, self.bg);
buf.set_string(x, 1 + i as u16, item, color, self.background_color);
}
if let Some(s) = self.selection_symbol {
buf.set_string(1,
(1 + self.selected - offset) as u16,
s,
self.selection_color,
self.bg);
self.background_color);
}
buf
}

@ -8,9 +8,9 @@ use symbols::bar;
pub struct Sparkline<'a> {
block: Option<Block<'a>>,
fg: Color,
bg: Color,
data: Vec<u64>,
color: Color,
background_color: Color,
data: &'a [u64],
max: Option<u64>,
}
@ -18,9 +18,9 @@ impl<'a> Default for Sparkline<'a> {
fn default() -> Sparkline<'a> {
Sparkline {
block: None,
fg: Color::Reset,
bg: Color::Reset,
data: Vec::new(),
color: Color::Reset,
background_color: Color::Reset,
data: &[],
max: None,
}
}
@ -32,19 +32,19 @@ impl<'a> Sparkline<'a> {
self
}
pub fn fg(&mut self, fg: Color) -> &mut Sparkline<'a> {
self.fg = fg;
pub fn color(&mut self, color: Color) -> &mut Sparkline<'a> {
self.color = color;
self
}
pub fn bg(&mut self, bg: Color) -> &mut Sparkline<'a> {
self.bg = bg;
pub fn background_color(&mut self, color: Color) -> &mut Sparkline<'a> {
self.background_color = color;
self
}
pub fn data(&mut self, data: &[u64]) -> &mut Sparkline<'a> {
self.data = data.to_vec();
pub fn data(&mut self, data: &'a [u64]) -> &mut Sparkline<'a> {
self.data = data;
self
}
@ -88,7 +88,11 @@ impl<'a> Widget<'a> for Sparkline<'a> {
7 => bar::SEVEN_EIGHTHS,
_ => bar::FULL,
};
buf.update_cell(margin_x + i as u16, margin_y + j, symbol, self.fg, self.bg);
buf.update_cell(margin_x + i as u16,
margin_y + j,
symbol,
self.color,
self.background_color);
if *d > 8 {
*d -= 8;

Loading…
Cancel
Save