mirror of
https://github.com/ratatui-org/ratatui
synced 2025-02-16 22:18:51 +00:00
Cache layout and performance fixes
This commit is contained in:
parent
07ff2b08eb
commit
d7131ead11
5 changed files with 61 additions and 31 deletions
|
@ -82,7 +82,7 @@ struct App {
|
||||||
progress: u16,
|
progress: u16,
|
||||||
data: Vec<u64>,
|
data: Vec<u64>,
|
||||||
data2: Vec<(f64, f64)>,
|
data2: Vec<(f64, f64)>,
|
||||||
window: [f64; 2],
|
data3: Vec<(f64, f64)>,
|
||||||
colors: [Color; 2],
|
colors: [Color; 2],
|
||||||
color_index: usize,
|
color_index: usize,
|
||||||
}
|
}
|
||||||
|
@ -109,6 +109,7 @@ fn main() {
|
||||||
|
|
||||||
let mut rand_signal = RandomSignal::new(Range::new(0, 100));
|
let mut rand_signal = RandomSignal::new(Range::new(0, 100));
|
||||||
let mut sin_signal = SinSignal::new(4.0, 20.0);
|
let mut sin_signal = SinSignal::new(4.0, 20.0);
|
||||||
|
let mut sin_signal2 = SinSignal::new(2.0, 10.0);
|
||||||
|
|
||||||
let mut app = App {
|
let mut app = App {
|
||||||
name: String::from("Test app"),
|
name: String::from("Test app"),
|
||||||
|
@ -119,7 +120,7 @@ fn main() {
|
||||||
progress: 0,
|
progress: 0,
|
||||||
data: rand_signal.clone().take(100).collect(),
|
data: rand_signal.clone().take(100).collect(),
|
||||||
data2: sin_signal.clone().take(100).collect(),
|
data2: sin_signal.clone().take(100).collect(),
|
||||||
window: [0.0, 100.0],
|
data3: sin_signal2.clone().take(100).collect(),
|
||||||
colors: [Color::Magenta, Color::Red],
|
colors: [Color::Magenta, Color::Red],
|
||||||
color_index: 0,
|
color_index: 0,
|
||||||
};
|
};
|
||||||
|
@ -141,7 +142,7 @@ fn main() {
|
||||||
let tx = tx.clone();
|
let tx = tx.clone();
|
||||||
loop {
|
loop {
|
||||||
tx.send(Event::Tick).unwrap();
|
tx.send(Event::Tick).unwrap();
|
||||||
thread::sleep(time::Duration::from_millis(500));
|
thread::sleep(time::Duration::from_millis(1000));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -184,8 +185,8 @@ fn main() {
|
||||||
app.data.pop();
|
app.data.pop();
|
||||||
app.data2.remove(0);
|
app.data2.remove(0);
|
||||||
app.data2.push(sin_signal.next().unwrap());
|
app.data2.push(sin_signal.next().unwrap());
|
||||||
app.window[0] += 1.0;
|
app.data3.remove(0);
|
||||||
app.window[1] += 1.0;
|
app.data3.push(sin_signal2.next().unwrap());
|
||||||
app.selected += 1;
|
app.selected += 1;
|
||||||
if app.selected >= app.items.len() {
|
if app.selected >= app.items.len() {
|
||||||
app.selected = 0;
|
app.selected = 0;
|
||||||
|
@ -206,14 +207,14 @@ fn draw(t: &mut Terminal, app: &App) {
|
||||||
.direction(Direction::Vertical)
|
.direction(Direction::Vertical)
|
||||||
.alignment(Alignment::Left)
|
.alignment(Alignment::Left)
|
||||||
.chunks(&[Size::Fixed(7), Size::Min(5), Size::Fixed(3)])
|
.chunks(&[Size::Fixed(7), Size::Min(5), Size::Fixed(3)])
|
||||||
.render(&Terminal::size().unwrap(), |chunks| {
|
.render(t, &Terminal::size().unwrap(), |t, chunks| {
|
||||||
Block::default().borders(border::ALL).title("Graphs").render(&chunks[0], t);
|
Block::default().borders(border::ALL).title("Graphs").render(&chunks[0], t);
|
||||||
Group::default()
|
Group::default()
|
||||||
.direction(Direction::Vertical)
|
.direction(Direction::Vertical)
|
||||||
.alignment(Alignment::Left)
|
.alignment(Alignment::Left)
|
||||||
.margin(1)
|
.margin(1)
|
||||||
.chunks(&[Size::Fixed(2), Size::Fixed(3)])
|
.chunks(&[Size::Fixed(2), Size::Fixed(3)])
|
||||||
.render(&chunks[0], |chunks| {
|
.render(t, &chunks[0], |t, chunks| {
|
||||||
Gauge::default()
|
Gauge::default()
|
||||||
.block(Block::default().title("Gauge:"))
|
.block(Block::default().title("Gauge:"))
|
||||||
.bg(Color::Yellow)
|
.bg(Color::Yellow)
|
||||||
|
@ -234,7 +235,7 @@ fn draw(t: &mut Terminal, app: &App) {
|
||||||
.direction(Direction::Horizontal)
|
.direction(Direction::Horizontal)
|
||||||
.alignment(Alignment::Left)
|
.alignment(Alignment::Left)
|
||||||
.chunks(&sizes)
|
.chunks(&sizes)
|
||||||
.render(&chunks[1], |chunks| {
|
.render(t, &chunks[1], |t, chunks| {
|
||||||
List::default()
|
List::default()
|
||||||
.block(Block::default().borders(border::ALL).title("List"))
|
.block(Block::default().borders(border::ALL).title("List"))
|
||||||
.render(&chunks[0], t);
|
.render(&chunks[0], t);
|
||||||
|
@ -243,9 +244,10 @@ fn draw(t: &mut Terminal, app: &App) {
|
||||||
.block(Block::default()
|
.block(Block::default()
|
||||||
.borders(border::ALL)
|
.borders(border::ALL)
|
||||||
.title("Chart"))
|
.title("Chart"))
|
||||||
.x_axis(Axis::default().title("X").bounds(app.window))
|
.x_axis(Axis::default().title("X").bounds([0.0, 100.0]))
|
||||||
.y_axis(Axis::default().title("Y").bounds([0.0, 40.0]))
|
.y_axis(Axis::default().title("Y").bounds([0.0, 40.0]))
|
||||||
.datasets(&[Dataset::default().color(Color::Cyan).data(&app.data2)])
|
.datasets(&[Dataset::default().color(Color::Cyan).data(&app.data2),
|
||||||
|
Dataset::default().color(Color::Yellow).data(&app.data3)])
|
||||||
.render(&chunks[1], t);
|
.render(&chunks[1], t);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,7 +6,10 @@ use cassowary::WeightedRelation::*;
|
||||||
use cassowary::strength::{WEAK, REQUIRED};
|
use cassowary::strength::{WEAK, REQUIRED};
|
||||||
|
|
||||||
use buffer::Buffer;
|
use buffer::Buffer;
|
||||||
|
use terminal::Terminal;
|
||||||
|
use util::hash;
|
||||||
|
|
||||||
|
#[derive(Hash)]
|
||||||
pub enum Alignment {
|
pub enum Alignment {
|
||||||
Top,
|
Top,
|
||||||
Left,
|
Left,
|
||||||
|
@ -15,12 +18,13 @@ pub enum Alignment {
|
||||||
Right,
|
Right,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Hash)]
|
||||||
pub enum Direction {
|
pub enum Direction {
|
||||||
Horizontal,
|
Horizontal,
|
||||||
Vertical,
|
Vertical,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
|
||||||
pub struct Rect {
|
pub struct Rect {
|
||||||
pub x: u16,
|
pub x: u16,
|
||||||
pub y: u16,
|
pub y: u16,
|
||||||
|
@ -235,6 +239,7 @@ impl Element {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Hash)]
|
||||||
pub struct Group {
|
pub struct Group {
|
||||||
direction: Direction,
|
direction: Direction,
|
||||||
alignment: Alignment,
|
alignment: Alignment,
|
||||||
|
@ -273,14 +278,26 @@ impl Group {
|
||||||
self.chunks = Vec::from(chunks);
|
self.chunks = Vec::from(chunks);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
pub fn render<F>(&self, area: &Rect, mut f: F)
|
pub fn render<F>(&self, t: &mut Terminal, area: &Rect, mut f: F)
|
||||||
where F: FnMut(&[Rect])
|
where F: FnMut(&mut Terminal, &[Rect])
|
||||||
{
|
{
|
||||||
let chunks = split(area,
|
let hash = hash(self, area);
|
||||||
&self.direction,
|
let (cache_update, chunks) = match t.get_layout(hash) {
|
||||||
&self.alignment,
|
Some(chs) => (false, chs.to_vec()),
|
||||||
self.margin,
|
None => {
|
||||||
&self.chunks);
|
(true,
|
||||||
f(&chunks);
|
split(area,
|
||||||
|
&self.direction,
|
||||||
|
&self.alignment,
|
||||||
|
self.margin,
|
||||||
|
&self.chunks))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
f(t, &chunks);
|
||||||
|
|
||||||
|
if cache_update {
|
||||||
|
t.set_layout(hash, chunks);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,16 +8,19 @@ use termion::raw::{IntoRawMode, RawTerminal};
|
||||||
use buffer::Buffer;
|
use buffer::Buffer;
|
||||||
use widgets::Widget;
|
use widgets::Widget;
|
||||||
use layout::Rect;
|
use layout::Rect;
|
||||||
use util::hash;
|
|
||||||
|
|
||||||
pub struct Terminal {
|
pub struct Terminal {
|
||||||
stdout: RawTerminal<io::Stdout>,
|
stdout: RawTerminal<io::Stdout>,
|
||||||
|
layout_cache: HashMap<u64, Vec<Rect>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Terminal {
|
impl Terminal {
|
||||||
pub fn new() -> Result<Terminal, io::Error> {
|
pub fn new() -> Result<Terminal, io::Error> {
|
||||||
let stdout = try!(io::stdout().into_raw_mode());
|
let stdout = try!(io::stdout().into_raw_mode());
|
||||||
Ok(Terminal { stdout: stdout })
|
Ok(Terminal {
|
||||||
|
stdout: stdout,
|
||||||
|
layout_cache: HashMap::new(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn size() -> Result<Rect, io::Error> {
|
pub fn size() -> Result<Rect, io::Error> {
|
||||||
|
@ -30,20 +33,29 @@ impl Terminal {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: Clean cache to prevent memory leak
|
||||||
|
pub fn get_layout(&self, hash: u64) -> Option<&Vec<Rect>> {
|
||||||
|
self.layout_cache.get(&hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_layout(&mut self, hash: u64, chunks: Vec<Rect>) {
|
||||||
|
self.layout_cache.insert(hash, chunks);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn render_buffer(&mut self, buffer: Buffer) {
|
pub fn render_buffer(&mut self, buffer: Buffer) {
|
||||||
|
let mut string = String::with_capacity(buffer.area().area() as usize);
|
||||||
for (i, cell) in buffer.content().iter().enumerate() {
|
for (i, cell) in buffer.content().iter().enumerate() {
|
||||||
let (lx, ly) = buffer.pos_of(i).unwrap();
|
let (lx, ly) = buffer.pos_of(i).unwrap();
|
||||||
let (x, y) = (lx + buffer.area().x, ly + buffer.area().y);
|
let (x, y) = (lx + buffer.area().x, ly + buffer.area().y);
|
||||||
if cell.symbol != "" {
|
if cell.symbol != "" {
|
||||||
write!(self.stdout,
|
string.push_str(&format!("{}{}{}{}",
|
||||||
"{}{}{}{}",
|
termion::cursor::Goto(x + 1, y + 1),
|
||||||
termion::cursor::Goto(x + 1, y + 1),
|
cell.fg.fg(),
|
||||||
cell.fg.fg(),
|
cell.bg.bg(),
|
||||||
cell.bg.bg(),
|
cell.symbol))
|
||||||
cell.symbol)
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
write!(self.stdout, "{}", string);
|
||||||
self.stdout.flush().unwrap();
|
self.stdout.flush().unwrap();
|
||||||
}
|
}
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
|
|
|
@ -3,9 +3,10 @@ use std::hash::{Hash, Hasher, BuildHasher};
|
||||||
|
|
||||||
use layout::Rect;
|
use layout::Rect;
|
||||||
|
|
||||||
pub fn hash<T: Hash>(t: &T) -> u64 {
|
pub fn hash<T: Hash>(t: &T, area: &Rect) -> u64 {
|
||||||
let state = RandomState::new();
|
let state = RandomState::new();
|
||||||
let mut hasher = state.build_hasher();
|
let mut hasher = state.build_hasher();
|
||||||
t.hash(&mut hasher);
|
t.hash(&mut hasher);
|
||||||
|
area.hash(&mut hasher);
|
||||||
hasher.finish()
|
hasher.finish()
|
||||||
}
|
}
|
||||||
|
|
|
@ -138,7 +138,6 @@ impl<'a> Widget<'a> for Chart<'a> {
|
||||||
|
|
||||||
let margin_x = chart_area.x - area.x;
|
let margin_x = chart_area.x - area.x;
|
||||||
let margin_y = chart_area.y - area.y;
|
let margin_y = chart_area.y - area.y;
|
||||||
// info!("{:?}", self.datasets[0].data[0]);
|
|
||||||
|
|
||||||
for dataset in self.datasets {
|
for dataset in self.datasets {
|
||||||
for &(x, y) in dataset.data.iter() {
|
for &(x, y) in dataset.data.iter() {
|
||||||
|
@ -150,7 +149,6 @@ impl<'a> Widget<'a> for Chart<'a> {
|
||||||
(self.y_axis.bounds[1] - self.y_axis.bounds[0]);
|
(self.y_axis.bounds[1] - self.y_axis.bounds[0]);
|
||||||
let dx = (self.x_axis.bounds[1] - x) * (chart_area.width - 1) as f64 /
|
let dx = (self.x_axis.bounds[1] - x) * (chart_area.width - 1) as f64 /
|
||||||
(self.x_axis.bounds[1] - self.x_axis.bounds[0]);
|
(self.x_axis.bounds[1] - self.x_axis.bounds[0]);
|
||||||
info!("{} {}", dx, dy);
|
|
||||||
buf.update_cell(dx as u16 + margin_x, dy as u16 + margin_y, |c| {
|
buf.update_cell(dx as u16 + margin_x, dy as u16 + margin_y, |c| {
|
||||||
c.symbol = symbols::DOT;
|
c.symbol = symbols::DOT;
|
||||||
c.fg = dataset.color;
|
c.fg = dataset.color;
|
||||||
|
|
Loading…
Add table
Reference in a new issue