mirror of
https://github.com/ratatui-org/ratatui
synced 2024-11-22 20:53:19 +00:00
446efae185
This helps to keep the prelude small and less likely to conflict with other crates. - remove widgets module from prelude as the entire module can be just as easily imported with `use ratatui::widgets::*;` - move prelude module into its own file - update examples to import widgets module instead of just prelude - added several modules to prelude to make it possible to qualify imports that collide with other types that have similar names
428 lines
14 KiB
Rust
428 lines
14 KiB
Rust
use ratatui::{
|
|
prelude::*,
|
|
widgets::{canvas::*, *},
|
|
};
|
|
|
|
use crate::app::App;
|
|
|
|
pub fn draw<B: Backend>(f: &mut Frame<B>, app: &mut App) {
|
|
let chunks = Layout::default()
|
|
.constraints([Constraint::Length(3), Constraint::Min(0)].as_ref())
|
|
.split(f.size());
|
|
let titles = app
|
|
.tabs
|
|
.titles
|
|
.iter()
|
|
.map(|t| text::Line::from(Span::styled(*t, Style::default().fg(Color::Green))))
|
|
.collect();
|
|
let tabs = Tabs::new(titles)
|
|
.block(Block::default().borders(Borders::ALL).title(app.title))
|
|
.highlight_style(Style::default().fg(Color::Yellow))
|
|
.select(app.tabs.index);
|
|
f.render_widget(tabs, chunks[0]);
|
|
match app.tabs.index {
|
|
0 => draw_first_tab(f, app, chunks[1]),
|
|
1 => draw_second_tab(f, app, chunks[1]),
|
|
2 => draw_third_tab(f, app, chunks[1]),
|
|
_ => {}
|
|
};
|
|
}
|
|
|
|
fn draw_first_tab<B>(f: &mut Frame<B>, app: &mut App, area: Rect)
|
|
where
|
|
B: Backend,
|
|
{
|
|
let chunks = Layout::default()
|
|
.constraints(
|
|
[
|
|
Constraint::Length(9),
|
|
Constraint::Min(8),
|
|
Constraint::Length(7),
|
|
]
|
|
.as_ref(),
|
|
)
|
|
.split(area);
|
|
draw_gauges(f, app, chunks[0]);
|
|
draw_charts(f, app, chunks[1]);
|
|
draw_text(f, chunks[2]);
|
|
}
|
|
|
|
fn draw_gauges<B>(f: &mut Frame<B>, app: &mut App, area: Rect)
|
|
where
|
|
B: Backend,
|
|
{
|
|
let chunks = Layout::default()
|
|
.constraints(
|
|
[
|
|
Constraint::Length(2),
|
|
Constraint::Length(3),
|
|
Constraint::Length(1),
|
|
]
|
|
.as_ref(),
|
|
)
|
|
.margin(1)
|
|
.split(area);
|
|
let block = Block::default().borders(Borders::ALL).title("Graphs");
|
|
f.render_widget(block, area);
|
|
|
|
let label = format!("{:.2}%", app.progress * 100.0);
|
|
let gauge = Gauge::default()
|
|
.block(Block::default().title("Gauge:"))
|
|
.gauge_style(
|
|
Style::default()
|
|
.fg(Color::Magenta)
|
|
.bg(Color::Black)
|
|
.add_modifier(Modifier::ITALIC | Modifier::BOLD),
|
|
)
|
|
.use_unicode(app.enhanced_graphics)
|
|
.label(label)
|
|
.ratio(app.progress);
|
|
f.render_widget(gauge, chunks[0]);
|
|
|
|
let sparkline = Sparkline::default()
|
|
.block(Block::default().title("Sparkline:"))
|
|
.style(Style::default().fg(Color::Green))
|
|
.data(&app.sparkline.points)
|
|
.bar_set(if app.enhanced_graphics {
|
|
symbols::bar::NINE_LEVELS
|
|
} else {
|
|
symbols::bar::THREE_LEVELS
|
|
});
|
|
f.render_widget(sparkline, chunks[1]);
|
|
|
|
let line_gauge = LineGauge::default()
|
|
.block(Block::default().title("LineGauge:"))
|
|
.gauge_style(Style::default().fg(Color::Magenta))
|
|
.line_set(if app.enhanced_graphics {
|
|
symbols::line::THICK
|
|
} else {
|
|
symbols::line::NORMAL
|
|
})
|
|
.ratio(app.progress);
|
|
f.render_widget(line_gauge, chunks[2]);
|
|
}
|
|
|
|
fn draw_charts<B>(f: &mut Frame<B>, app: &mut App, area: Rect)
|
|
where
|
|
B: Backend,
|
|
{
|
|
let constraints = if app.show_chart {
|
|
vec![Constraint::Percentage(50), Constraint::Percentage(50)]
|
|
} else {
|
|
vec![Constraint::Percentage(100)]
|
|
};
|
|
let chunks = Layout::default()
|
|
.constraints(constraints)
|
|
.direction(Direction::Horizontal)
|
|
.split(area);
|
|
{
|
|
let chunks = Layout::default()
|
|
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
|
|
.split(chunks[0]);
|
|
{
|
|
let chunks = Layout::default()
|
|
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
|
|
.direction(Direction::Horizontal)
|
|
.split(chunks[0]);
|
|
|
|
// Draw tasks
|
|
let tasks: Vec<ListItem> = app
|
|
.tasks
|
|
.items
|
|
.iter()
|
|
.map(|i| ListItem::new(vec![text::Line::from(Span::raw(*i))]))
|
|
.collect();
|
|
let tasks = List::new(tasks)
|
|
.block(Block::default().borders(Borders::ALL).title("List"))
|
|
.highlight_style(Style::default().add_modifier(Modifier::BOLD))
|
|
.highlight_symbol("> ");
|
|
f.render_stateful_widget(tasks, chunks[0], &mut app.tasks.state);
|
|
|
|
// Draw logs
|
|
let info_style = Style::default().fg(Color::Blue);
|
|
let warning_style = Style::default().fg(Color::Yellow);
|
|
let error_style = Style::default().fg(Color::Magenta);
|
|
let critical_style = Style::default().fg(Color::Red);
|
|
let logs: Vec<ListItem> = app
|
|
.logs
|
|
.items
|
|
.iter()
|
|
.map(|&(evt, level)| {
|
|
let s = match level {
|
|
"ERROR" => error_style,
|
|
"CRITICAL" => critical_style,
|
|
"WARNING" => warning_style,
|
|
_ => info_style,
|
|
};
|
|
let content = vec![text::Line::from(vec![
|
|
Span::styled(format!("{level:<9}"), s),
|
|
Span::raw(evt),
|
|
])];
|
|
ListItem::new(content)
|
|
})
|
|
.collect();
|
|
let logs = List::new(logs).block(Block::default().borders(Borders::ALL).title("List"));
|
|
f.render_stateful_widget(logs, chunks[1], &mut app.logs.state);
|
|
}
|
|
|
|
let barchart = BarChart::default()
|
|
.block(Block::default().borders(Borders::ALL).title("Bar chart"))
|
|
.data(&app.barchart)
|
|
.bar_width(3)
|
|
.bar_gap(2)
|
|
.bar_set(if app.enhanced_graphics {
|
|
symbols::bar::NINE_LEVELS
|
|
} else {
|
|
symbols::bar::THREE_LEVELS
|
|
})
|
|
.value_style(
|
|
Style::default()
|
|
.fg(Color::Black)
|
|
.bg(Color::Green)
|
|
.add_modifier(Modifier::ITALIC),
|
|
)
|
|
.label_style(Style::default().fg(Color::Yellow))
|
|
.bar_style(Style::default().fg(Color::Green));
|
|
f.render_widget(barchart, chunks[1]);
|
|
}
|
|
if app.show_chart {
|
|
let x_labels = vec![
|
|
Span::styled(
|
|
format!("{}", app.signals.window[0]),
|
|
Style::default().add_modifier(Modifier::BOLD),
|
|
),
|
|
Span::raw(format!(
|
|
"{}",
|
|
(app.signals.window[0] + app.signals.window[1]) / 2.0
|
|
)),
|
|
Span::styled(
|
|
format!("{}", app.signals.window[1]),
|
|
Style::default().add_modifier(Modifier::BOLD),
|
|
),
|
|
];
|
|
let datasets = vec![
|
|
Dataset::default()
|
|
.name("data2")
|
|
.marker(symbols::Marker::Dot)
|
|
.style(Style::default().fg(Color::Cyan))
|
|
.data(&app.signals.sin1.points),
|
|
Dataset::default()
|
|
.name("data3")
|
|
.marker(if app.enhanced_graphics {
|
|
symbols::Marker::Braille
|
|
} else {
|
|
symbols::Marker::Dot
|
|
})
|
|
.style(Style::default().fg(Color::Yellow))
|
|
.data(&app.signals.sin2.points),
|
|
];
|
|
let chart = Chart::new(datasets)
|
|
.block(
|
|
Block::default()
|
|
.title(Span::styled(
|
|
"Chart",
|
|
Style::default()
|
|
.fg(Color::Cyan)
|
|
.add_modifier(Modifier::BOLD),
|
|
))
|
|
.borders(Borders::ALL),
|
|
)
|
|
.x_axis(
|
|
Axis::default()
|
|
.title("X Axis")
|
|
.style(Style::default().fg(Color::Gray))
|
|
.bounds(app.signals.window)
|
|
.labels(x_labels),
|
|
)
|
|
.y_axis(
|
|
Axis::default()
|
|
.title("Y Axis")
|
|
.style(Style::default().fg(Color::Gray))
|
|
.bounds([-20.0, 20.0])
|
|
.labels(vec![
|
|
Span::styled("-20", Style::default().add_modifier(Modifier::BOLD)),
|
|
Span::raw("0"),
|
|
Span::styled("20", Style::default().add_modifier(Modifier::BOLD)),
|
|
]),
|
|
);
|
|
f.render_widget(chart, chunks[1]);
|
|
}
|
|
}
|
|
|
|
fn draw_text<B>(f: &mut Frame<B>, area: Rect)
|
|
where
|
|
B: Backend,
|
|
{
|
|
let text = vec![
|
|
text::Line::from("This is a paragraph with several lines. You can change style your text the way you want"),
|
|
text::Line::from(""),
|
|
text::Line::from(vec![
|
|
Span::from("For example: "),
|
|
Span::styled("under", Style::default().fg(Color::Red)),
|
|
Span::raw(" "),
|
|
Span::styled("the", Style::default().fg(Color::Green)),
|
|
Span::raw(" "),
|
|
Span::styled("rainbow", Style::default().fg(Color::Blue)),
|
|
Span::raw("."),
|
|
]),
|
|
text::Line::from(vec![
|
|
Span::raw("Oh and if you didn't "),
|
|
Span::styled("notice", Style::default().add_modifier(Modifier::ITALIC)),
|
|
Span::raw(" you can "),
|
|
Span::styled("automatically", Style::default().add_modifier(Modifier::BOLD)),
|
|
Span::raw(" "),
|
|
Span::styled("wrap", Style::default().add_modifier(Modifier::REVERSED)),
|
|
Span::raw(" your "),
|
|
Span::styled("text", Style::default().add_modifier(Modifier::UNDERLINED)),
|
|
Span::raw(".")
|
|
]),
|
|
text::Line::from(
|
|
"One more thing is that it should display unicode characters: 10€"
|
|
),
|
|
];
|
|
let block = Block::default().borders(Borders::ALL).title(Span::styled(
|
|
"Footer",
|
|
Style::default()
|
|
.fg(Color::Magenta)
|
|
.add_modifier(Modifier::BOLD),
|
|
));
|
|
let paragraph = Paragraph::new(text).block(block).wrap(Wrap { trim: true });
|
|
f.render_widget(paragraph, area);
|
|
}
|
|
|
|
fn draw_second_tab<B>(f: &mut Frame<B>, app: &mut App, area: Rect)
|
|
where
|
|
B: Backend,
|
|
{
|
|
let chunks = Layout::default()
|
|
.constraints([Constraint::Percentage(30), Constraint::Percentage(70)].as_ref())
|
|
.direction(Direction::Horizontal)
|
|
.split(area);
|
|
let up_style = Style::default().fg(Color::Green);
|
|
let failure_style = Style::default()
|
|
.fg(Color::Red)
|
|
.add_modifier(Modifier::RAPID_BLINK | Modifier::CROSSED_OUT);
|
|
let rows = app.servers.iter().map(|s| {
|
|
let style = if s.status == "Up" {
|
|
up_style
|
|
} else {
|
|
failure_style
|
|
};
|
|
Row::new(vec![s.name, s.location, s.status]).style(style)
|
|
});
|
|
let table = Table::new(rows)
|
|
.header(
|
|
Row::new(vec!["Server", "Location", "Status"])
|
|
.style(Style::default().fg(Color::Yellow))
|
|
.bottom_margin(1),
|
|
)
|
|
.block(Block::default().title("Servers").borders(Borders::ALL))
|
|
.widths(&[
|
|
Constraint::Length(15),
|
|
Constraint::Length(15),
|
|
Constraint::Length(10),
|
|
]);
|
|
f.render_widget(table, chunks[0]);
|
|
|
|
let map = Canvas::default()
|
|
.block(Block::default().title("World").borders(Borders::ALL))
|
|
.paint(|ctx| {
|
|
ctx.draw(&Map {
|
|
color: Color::White,
|
|
resolution: MapResolution::High,
|
|
});
|
|
ctx.layer();
|
|
ctx.draw(&Rectangle {
|
|
x: 0.0,
|
|
y: 30.0,
|
|
width: 10.0,
|
|
height: 10.0,
|
|
color: Color::Yellow,
|
|
});
|
|
ctx.draw(&Circle {
|
|
x: app.servers[2].coords.1,
|
|
y: app.servers[2].coords.0,
|
|
radius: 10.0,
|
|
color: Color::Green,
|
|
});
|
|
for (i, s1) in app.servers.iter().enumerate() {
|
|
for s2 in &app.servers[i + 1..] {
|
|
ctx.draw(&canvas::Line {
|
|
x1: s1.coords.1,
|
|
y1: s1.coords.0,
|
|
y2: s2.coords.0,
|
|
x2: s2.coords.1,
|
|
color: Color::Yellow,
|
|
});
|
|
}
|
|
}
|
|
for server in &app.servers {
|
|
let color = if server.status == "Up" {
|
|
Color::Green
|
|
} else {
|
|
Color::Red
|
|
};
|
|
ctx.print(
|
|
server.coords.1,
|
|
server.coords.0,
|
|
Span::styled("X", Style::default().fg(color)),
|
|
);
|
|
}
|
|
})
|
|
.marker(if app.enhanced_graphics {
|
|
symbols::Marker::Braille
|
|
} else {
|
|
symbols::Marker::Dot
|
|
})
|
|
.x_bounds([-180.0, 180.0])
|
|
.y_bounds([-90.0, 90.0]);
|
|
f.render_widget(map, chunks[1]);
|
|
}
|
|
|
|
fn draw_third_tab<B>(f: &mut Frame<B>, _app: &mut App, area: Rect)
|
|
where
|
|
B: Backend,
|
|
{
|
|
let chunks = Layout::default()
|
|
.direction(Direction::Horizontal)
|
|
.constraints([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)])
|
|
.split(area);
|
|
let colors = [
|
|
Color::Reset,
|
|
Color::Black,
|
|
Color::Red,
|
|
Color::Green,
|
|
Color::Yellow,
|
|
Color::Blue,
|
|
Color::Magenta,
|
|
Color::Cyan,
|
|
Color::Gray,
|
|
Color::DarkGray,
|
|
Color::LightRed,
|
|
Color::LightGreen,
|
|
Color::LightYellow,
|
|
Color::LightBlue,
|
|
Color::LightMagenta,
|
|
Color::LightCyan,
|
|
Color::White,
|
|
];
|
|
let items: Vec<Row> = colors
|
|
.iter()
|
|
.map(|c| {
|
|
let cells = vec![
|
|
Cell::from(Span::raw(format!("{c:?}: "))),
|
|
Cell::from(Span::styled("Foreground", Style::default().fg(*c))),
|
|
Cell::from(Span::styled("Background", Style::default().bg(*c))),
|
|
];
|
|
Row::new(cells)
|
|
})
|
|
.collect();
|
|
let table = Table::new(items)
|
|
.block(Block::default().title("Colors").borders(Borders::ALL))
|
|
.widths(&[
|
|
Constraint::Ratio(1, 3),
|
|
Constraint::Ratio(1, 3),
|
|
Constraint::Ratio(1, 3),
|
|
]);
|
|
f.render_widget(table, chunks[0]);
|
|
}
|