mirror of
https://github.com/ratatui-org/ratatui
synced 2024-11-10 07:04:17 +00:00
feat: replace Rect::split with Layout::areas and spacers (#904)
In a recent commit we added Rec::split, but this feels more ergonomic as Layout::areas. This also adds Layout::spacers to get the spacers between the areas.
This commit is contained in:
parent
f8367fdfdd
commit
87bf1dd9df
30 changed files with 148 additions and 123 deletions
|
@ -152,8 +152,8 @@ fn run_app<B: Backend>(
|
|||
fn ui(frame: &mut Frame, app: &App) {
|
||||
let vertical = Layout::vertical([Constraint::Ratio(1, 3), Constraint::Ratio(2, 3)]);
|
||||
let horizontal = Layout::horizontal([Constraint::Percentage(50), Constraint::Percentage(50)]);
|
||||
let [top, bottom] = frame.size().split(&vertical);
|
||||
let [left, right] = bottom.split(&horizontal);
|
||||
let [top, bottom] = vertical.areas(frame.size());
|
||||
let [left, right] = horizontal.areas(bottom);
|
||||
|
||||
let barchart = BarChart::default()
|
||||
.block(Block::default().title("Data1").borders(Borders::ALL))
|
||||
|
|
|
@ -119,8 +119,8 @@ fn ui(frame: &mut Frame) {
|
|||
/// Returns a tuple of the title area and the main areas.
|
||||
fn calculate_layout(area: Rect) -> (Rect, Vec<Vec<Rect>>) {
|
||||
let main_layout = Layout::vertical([Constraint::Length(1), Constraint::Min(0)]);
|
||||
let block_layout = &Layout::vertical([Constraint::Max(4); 9]);
|
||||
let [title_area, main_area] = area.split(&main_layout);
|
||||
let block_layout = Layout::vertical([Constraint::Max(4); 9]);
|
||||
let [title_area, main_area] = main_layout.areas(area);
|
||||
let main_areas = block_layout
|
||||
.split(main_area)
|
||||
.iter()
|
||||
|
|
|
@ -125,8 +125,8 @@ impl App {
|
|||
let horizontal =
|
||||
Layout::horizontal([Constraint::Percentage(50), Constraint::Percentage(50)]);
|
||||
let vertical = Layout::vertical([Constraint::Percentage(50), Constraint::Percentage(50)]);
|
||||
let [map, right] = frame.size().split(&horizontal);
|
||||
let [pong, boxes] = right.split(&vertical);
|
||||
let [map, right] = horizontal.areas(frame.size());
|
||||
let [pong, boxes] = vertical.areas(right);
|
||||
|
||||
frame.render_widget(self.map_canvas(), map);
|
||||
frame.render_widget(self.pong_canvas(), pong);
|
||||
|
|
|
@ -152,8 +152,8 @@ fn ui(frame: &mut Frame, app: &App) {
|
|||
|
||||
let vertical = Layout::vertical([Constraint::Percentage(40), Constraint::Percentage(60)]);
|
||||
let horizontal = Layout::horizontal([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)]);
|
||||
let [chart1, bottom] = area.split(&vertical);
|
||||
let [line_chart, scatter] = bottom.split(&horizontal);
|
||||
let [chart1, bottom] = vertical.areas(area);
|
||||
let [line_chart, scatter] = horizontal.areas(bottom);
|
||||
|
||||
render_chart1(frame, chart1, app);
|
||||
render_line_chart(frame, line_chart);
|
||||
|
|
|
@ -141,8 +141,8 @@ impl App {
|
|||
impl Widget for &mut App {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
use Constraint::*;
|
||||
let [top, colors] = area.split(&Layout::vertical([Length(1), Min(0)]));
|
||||
let [title, fps] = top.split(&Layout::horizontal([Min(0), Length(8)]));
|
||||
let [top, colors] = Layout::vertical([Length(1), Min(0)]).areas(area);
|
||||
let [title, fps] = Layout::horizontal([Min(0), Length(8)]).areas(top);
|
||||
Text::from("colors_rgb example. Press q to quit")
|
||||
.centered()
|
||||
.render(title, buf);
|
||||
|
|
|
@ -258,13 +258,14 @@ impl From<Constraint> for ConstraintName {
|
|||
impl Widget for &App {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
let [header_area, instructions_area, swap_legend_area, _, blocks_area] =
|
||||
area.split(&Layout::vertical([
|
||||
Layout::vertical([
|
||||
Length(2), // header
|
||||
Length(2), // instructions
|
||||
Length(1), // swap key legend
|
||||
Length(1), // gap
|
||||
Fill(1), // blocks
|
||||
]));
|
||||
])
|
||||
.areas(area);
|
||||
|
||||
self.header().render(header_area, buf);
|
||||
self.instructions().render(instructions_area, buf);
|
||||
|
@ -334,13 +335,14 @@ impl App {
|
|||
}
|
||||
|
||||
fn render_layout_blocks(&self, area: Rect, buf: &mut Buffer) {
|
||||
let [user_constraints, area] =
|
||||
area.split(&Layout::vertical([Length(3), Fill(1)]).spacing(1));
|
||||
let [user_constraints, area] = Layout::vertical([Length(3), Fill(1)])
|
||||
.spacing(1)
|
||||
.areas(area);
|
||||
|
||||
self.render_user_constraints_legend(user_constraints, buf);
|
||||
|
||||
let [start, center, end, space_around, space_between] =
|
||||
area.split(&Layout::vertical([Length(7); 5]));
|
||||
Layout::vertical([Length(7); 5]).areas(area);
|
||||
|
||||
self.render_layout_block(Flex::Start, start, buf);
|
||||
self.render_layout_block(Flex::Center, center, buf);
|
||||
|
@ -366,7 +368,7 @@ impl App {
|
|||
|
||||
fn render_layout_block(&self, flex: Flex, area: Rect, buf: &mut Buffer) {
|
||||
let [label_area, axis_area, blocks_area] =
|
||||
area.split(&Layout::vertical([Length(1), Max(1), Length(4)]));
|
||||
Layout::vertical([Length(1), Max(1), Length(4)]).areas(area);
|
||||
|
||||
if label_area.height > 0 {
|
||||
format!("Flex::{:?}", flex).bold().render(label_area, buf);
|
||||
|
|
|
@ -159,11 +159,8 @@ impl App {
|
|||
|
||||
impl Widget for App {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
let [tabs, axis, demo] = area.split(&Layout::vertical([
|
||||
Constraint::Length(3),
|
||||
Constraint::Length(3),
|
||||
Fill(0),
|
||||
]));
|
||||
let [tabs, axis, demo] =
|
||||
Layout::vertical([Constraint::Length(3), Constraint::Length(3), Fill(0)]).areas(area);
|
||||
|
||||
self.render_tabs(tabs, buf);
|
||||
self.render_axis(axis, buf);
|
||||
|
@ -305,7 +302,7 @@ impl Widget for SelectedTab {
|
|||
impl SelectedTab {
|
||||
fn render_length_example(&self, area: Rect, buf: &mut Buffer) {
|
||||
let [example1, example2, example3, _] =
|
||||
area.split(&Layout::vertical([Length(EXAMPLE_HEIGHT); 4]));
|
||||
Layout::vertical([Length(EXAMPLE_HEIGHT); 4]).areas(area);
|
||||
|
||||
Example::new(&[Length(20), Length(20)]).render(example1, buf);
|
||||
Example::new(&[Length(20), Min(20)]).render(example2, buf);
|
||||
|
@ -314,7 +311,7 @@ impl SelectedTab {
|
|||
|
||||
fn render_percentage_example(&self, area: Rect, buf: &mut Buffer) {
|
||||
let [example1, example2, example3, example4, example5, _] =
|
||||
area.split(&Layout::vertical([Length(EXAMPLE_HEIGHT); 6]));
|
||||
Layout::vertical([Length(EXAMPLE_HEIGHT); 6]).areas(area);
|
||||
|
||||
Example::new(&[Percentage(75), Fill(0)]).render(example1, buf);
|
||||
Example::new(&[Percentage(25), Fill(0)]).render(example2, buf);
|
||||
|
@ -325,7 +322,7 @@ impl SelectedTab {
|
|||
|
||||
fn render_ratio_example(&self, area: Rect, buf: &mut Buffer) {
|
||||
let [example1, example2, example3, example4, _] =
|
||||
area.split(&Layout::vertical([Length(EXAMPLE_HEIGHT); 5]));
|
||||
Layout::vertical([Length(EXAMPLE_HEIGHT); 5]).areas(area);
|
||||
|
||||
Example::new(&[Ratio(1, 2); 2]).render(example1, buf);
|
||||
Example::new(&[Ratio(1, 4); 4]).render(example2, buf);
|
||||
|
@ -334,7 +331,7 @@ impl SelectedTab {
|
|||
}
|
||||
|
||||
fn render_fill_example(&self, area: Rect, buf: &mut Buffer) {
|
||||
let [example1, example2, _] = area.split(&Layout::vertical([Length(EXAMPLE_HEIGHT); 3]));
|
||||
let [example1, example2, _] = Layout::vertical([Length(EXAMPLE_HEIGHT); 3]).areas(area);
|
||||
|
||||
Example::new(&[Fill(1), Fill(2), Fill(3)]).render(example1, buf);
|
||||
Example::new(&[Fill(1), Percentage(50), Fill(1)]).render(example2, buf);
|
||||
|
@ -342,7 +339,7 @@ impl SelectedTab {
|
|||
|
||||
fn render_min_example(&self, area: Rect, buf: &mut Buffer) {
|
||||
let [example1, example2, example3, example4, example5, _] =
|
||||
area.split(&Layout::vertical([Length(EXAMPLE_HEIGHT); 6]));
|
||||
Layout::vertical([Length(EXAMPLE_HEIGHT); 6]).areas(area);
|
||||
|
||||
Example::new(&[Percentage(100), Min(0)]).render(example1, buf);
|
||||
Example::new(&[Percentage(100), Min(20)]).render(example2, buf);
|
||||
|
@ -353,7 +350,7 @@ impl SelectedTab {
|
|||
|
||||
fn render_max_example(&self, area: Rect, buf: &mut Buffer) {
|
||||
let [example1, example2, example3, example4, example5, _] =
|
||||
area.split(&Layout::vertical([Length(EXAMPLE_HEIGHT); 6]));
|
||||
Layout::vertical([Length(EXAMPLE_HEIGHT); 6]).areas(area);
|
||||
|
||||
Example::new(&[Percentage(0), Max(0)]).render(example1, buf);
|
||||
Example::new(&[Percentage(0), Max(20)]).render(example2, buf);
|
||||
|
@ -377,10 +374,8 @@ impl Example {
|
|||
|
||||
impl Widget for Example {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
let [area, _] = area.split(&Layout::vertical([
|
||||
Length(ILLUSTRATION_HEIGHT),
|
||||
Length(SPACER_HEIGHT),
|
||||
]));
|
||||
let [area, _] =
|
||||
Layout::vertical([Length(ILLUSTRATION_HEIGHT), Length(SPACER_HEIGHT)]).areas(area);
|
||||
let blocks = Layout::horizontal(&self.constraints).split(area);
|
||||
|
||||
for (block, constraint) in blocks.iter().zip(&self.constraints) {
|
||||
|
|
|
@ -192,7 +192,7 @@ fn ui(frame: &mut Frame, states: &[State; 3]) {
|
|||
Constraint::Length(1),
|
||||
Constraint::Min(0), // ignore remaining space
|
||||
]);
|
||||
let [title, buttons, help, _] = frame.size().split(&vertical);
|
||||
let [title, buttons, help, _] = vertical.areas(frame.size());
|
||||
|
||||
frame.render_widget(
|
||||
Paragraph::new("Custom Widget Example (mouse enabled)"),
|
||||
|
@ -209,7 +209,7 @@ fn render_buttons(frame: &mut Frame<'_>, area: Rect, states: &[State; 3]) {
|
|||
Constraint::Length(15),
|
||||
Constraint::Min(0), // ignore remaining space
|
||||
]);
|
||||
let [red, green, blue, _] = area.split(&horizontal);
|
||||
let [red, green, blue, _] = horizontal.areas(area);
|
||||
|
||||
frame.render_widget(Button::new("Red").theme(RED).state(states[0]), red);
|
||||
frame.render_widget(Button::new("Green").theme(GREEN).state(states[1]), green);
|
||||
|
|
|
@ -142,7 +142,7 @@ impl Widget for &App {
|
|||
Constraint::Min(0),
|
||||
Constraint::Length(1),
|
||||
]);
|
||||
let [title_bar, tab, bottom_bar] = area.split(&vertical);
|
||||
let [title_bar, tab, bottom_bar] = vertical.areas(area);
|
||||
|
||||
Block::new().style(THEME.root).render(area, buf);
|
||||
self.render_title_bar(title_bar, buf);
|
||||
|
@ -154,7 +154,7 @@ impl Widget for &App {
|
|||
impl App {
|
||||
fn render_title_bar(&self, area: Rect, buf: &mut Buffer) {
|
||||
let layout = Layout::horizontal([Constraint::Min(0), Constraint::Length(43)]);
|
||||
let [title, tabs] = area.split(&layout);
|
||||
let [title, tabs] = layout.areas(area);
|
||||
|
||||
Span::styled("Ratatui", THEME.app_title).render(title, buf);
|
||||
let titles = Tab::iter().map(|tab| tab.title());
|
||||
|
|
|
@ -125,7 +125,7 @@ fn blend(mask_color: Color, cell_color: Color, percentage: f64) -> Color {
|
|||
fn centered_rect(area: Rect, width: u16, height: u16) -> Rect {
|
||||
let horizontal = Layout::horizontal([width]).flex(Flex::Center);
|
||||
let vertical = Layout::vertical([height]).flex(Flex::Center);
|
||||
let [area] = area.split(&vertical);
|
||||
let [area] = area.split(&horizontal);
|
||||
let [area] = vertical.areas(area);
|
||||
let [area] = horizontal.areas(area);
|
||||
area
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@ impl Widget for AboutTab {
|
|||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
RgbSwatch.render(area, buf);
|
||||
let horizontal = Layout::horizontal([Constraint::Length(34), Constraint::Min(0)]);
|
||||
let [description, logo] = area.split(&horizontal);
|
||||
let [description, logo] = horizontal.areas(area);
|
||||
render_crate_description(description, buf);
|
||||
render_logo(self.row_index, logo, buf);
|
||||
}
|
||||
|
|
|
@ -65,14 +65,14 @@ impl Widget for EmailTab {
|
|||
});
|
||||
Clear.render(area, buf);
|
||||
let vertical = Layout::vertical([Constraint::Length(5), Constraint::Min(0)]);
|
||||
let [inbox, email] = area.split(&vertical);
|
||||
let [inbox, email] = vertical.areas(area);
|
||||
render_inbox(self.row_index, inbox, buf);
|
||||
render_email(self.row_index, email, buf);
|
||||
}
|
||||
}
|
||||
fn render_inbox(selected_index: usize, area: Rect, buf: &mut Buffer) {
|
||||
let vertical = Layout::vertical([Constraint::Length(1), Constraint::Min(0)]);
|
||||
let [tabs, inbox] = area.split(&vertical);
|
||||
let [tabs, inbox] = vertical.areas(area);
|
||||
let theme = THEME.email;
|
||||
Tabs::new(vec![" Inbox ", " Sent ", " Drafts "])
|
||||
.style(theme.tabs)
|
||||
|
@ -127,7 +127,7 @@ fn render_email(selected_index: usize, area: Rect, buf: &mut Buffer) {
|
|||
block.render(area, buf);
|
||||
if let Some(email) = email {
|
||||
let vertical = Layout::vertical([Constraint::Length(3), Constraint::Min(0)]);
|
||||
let [headers_area, body_area] = inner.split(&vertical);
|
||||
let [headers_area, body_area] = vertical.areas(inner);
|
||||
let headers = vec![
|
||||
Line::from(vec![
|
||||
"From: ".set_style(theme.header),
|
||||
|
|
|
@ -127,10 +127,8 @@ impl Widget for RecipeTab {
|
|||
horizontal: 2,
|
||||
vertical: 1,
|
||||
});
|
||||
let [recipe, ingredients] = area.split(&Layout::horizontal([
|
||||
Constraint::Length(44),
|
||||
Constraint::Min(0),
|
||||
]));
|
||||
let [recipe, ingredients] =
|
||||
Layout::horizontal([Constraint::Length(44), Constraint::Min(0)]).areas(area);
|
||||
|
||||
render_recipe(recipe, buf);
|
||||
render_ingredients(self.row_index, ingredients, buf);
|
||||
|
|
|
@ -34,8 +34,8 @@ impl Widget for TracerouteTab {
|
|||
Block::new().style(THEME.content).render(area, buf);
|
||||
let horizontal = Layout::horizontal([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)]);
|
||||
let vertical = Layout::vertical([Constraint::Min(0), Constraint::Length(3)]);
|
||||
let [left, map] = area.split(&horizontal);
|
||||
let [hops, pings] = left.split(&vertical);
|
||||
let [left, map] = horizontal.areas(area);
|
||||
let [hops, pings] = vertical.areas(left);
|
||||
|
||||
render_hops(self.row_index, hops, buf);
|
||||
render_ping(self.row_index, pings, buf);
|
||||
|
|
|
@ -39,19 +39,16 @@ impl Widget for WeatherTab {
|
|||
horizontal: 2,
|
||||
vertical: 1,
|
||||
});
|
||||
let [main, _, gauges] = area.split(&Layout::vertical([
|
||||
let [main, _, gauges] = Layout::vertical([
|
||||
Constraint::Min(0),
|
||||
Constraint::Length(1),
|
||||
Constraint::Length(1),
|
||||
]));
|
||||
let [calendar, charts] = main.split(&Layout::horizontal([
|
||||
Constraint::Length(23),
|
||||
Constraint::Min(0),
|
||||
]));
|
||||
let [simple, horizontal] = charts.split(&Layout::vertical([
|
||||
Constraint::Length(29),
|
||||
Constraint::Min(0),
|
||||
]));
|
||||
])
|
||||
.areas(area);
|
||||
let [calendar, charts] =
|
||||
Layout::horizontal([Constraint::Length(23), Constraint::Min(0)]).areas(main);
|
||||
let [simple, horizontal] =
|
||||
Layout::vertical([Constraint::Length(29), Constraint::Min(0)]).areas(charts);
|
||||
|
||||
render_calendar(calendar, buf);
|
||||
render_simple_barchart(simple, buf);
|
||||
|
|
|
@ -74,8 +74,8 @@ fn layout(frame: &mut Frame) {
|
|||
Constraint::Length(1),
|
||||
]);
|
||||
let horizontal = Layout::horizontal([Constraint::Ratio(1, 2); 2]);
|
||||
let [title_bar, main_area, status_bar] = frame.size().split(&vertical);
|
||||
let [left, right] = main_area.split(&horizontal);
|
||||
let [title_bar, main_area, status_bar] = vertical.areas(frame.size());
|
||||
let [left, right] = horizontal.areas(main_area);
|
||||
|
||||
frame.render_widget(
|
||||
Block::new().borders(Borders::TOP).title("Title Bar"),
|
||||
|
|
|
@ -256,7 +256,7 @@ fn example_height() -> u16 {
|
|||
impl Widget for App {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
let layout = Layout::vertical([Length(3), Length(1), Fill(0)]);
|
||||
let [tabs, axis, demo] = area.split(&layout);
|
||||
let [tabs, axis, demo] = layout.areas(area);
|
||||
self.tabs().render(tabs, buf);
|
||||
let scroll_needed = self.render_demo(demo, buf);
|
||||
let axis_width = if scroll_needed {
|
||||
|
@ -421,7 +421,7 @@ impl Widget for Example {
|
|||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
let title_height = get_description_height(&self.description);
|
||||
let layout = Layout::vertical([Length(title_height), Fill(0)]);
|
||||
let [title, illustrations] = area.split(&layout);
|
||||
let [title, illustrations] = layout.areas(area);
|
||||
|
||||
let (blocks, spacers) = Layout::horizontal(&self.constraints)
|
||||
.flex(self.flex)
|
||||
|
|
|
@ -122,10 +122,10 @@ impl Widget for &App {
|
|||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
use Constraint::*;
|
||||
let layout = Layout::vertical([Length(2), Min(0), Length(1)]);
|
||||
let [header_area, gauge_area, footer_area] = area.split(&layout);
|
||||
let [header_area, gauge_area, footer_area] = layout.areas(area);
|
||||
|
||||
let layout = Layout::vertical([Ratio(1, 4); 4]);
|
||||
let [gauge1_area, gauge2_area, gauge3_area, gauge4_area] = gauge_area.split(&layout);
|
||||
let [gauge1_area, gauge2_area, gauge3_area, gauge4_area] = layout.areas(gauge_area);
|
||||
|
||||
self.render_header(header_area, buf);
|
||||
self.render_footer(footer_area, buf);
|
||||
|
|
|
@ -237,8 +237,8 @@ fn ui(f: &mut Frame, downloads: &Downloads) {
|
|||
|
||||
let vertical = Layout::vertical([Constraint::Length(2), Constraint::Length(4)]).margin(1);
|
||||
let horizontal = Layout::horizontal([Constraint::Percentage(20), Constraint::Percentage(80)]);
|
||||
let [progress_area, main] = area.split(&vertical);
|
||||
let [list_area, gauge_area] = main.split(&horizontal);
|
||||
let [progress_area, main] = vertical.areas(area);
|
||||
let [list_area, gauge_area] = horizontal.areas(main);
|
||||
|
||||
// total progress
|
||||
let done = NUM_DOWNLOADS - downloads.pending.len() - downloads.in_progress.len();
|
||||
|
|
|
@ -68,7 +68,7 @@ fn ui(frame: &mut Frame) {
|
|||
Length(50), // examples
|
||||
Min(0), // fills remaining space
|
||||
]);
|
||||
let [text_area, examples_area, _] = frame.size().split(&vertical);
|
||||
let [text_area, examples_area, _] = vertical.areas(frame.size());
|
||||
|
||||
// title
|
||||
frame.render_widget(
|
||||
|
@ -205,7 +205,7 @@ fn render_single_example(frame: &mut Frame, area: Rect, constraints: Vec<Constra
|
|||
let blue = Paragraph::new(constraint_label(constraints[1])).on_blue();
|
||||
let green = Paragraph::new("·".repeat(12)).on_green();
|
||||
let horizontal = Layout::horizontal(constraints);
|
||||
let [r, b, g] = area.split(&horizontal);
|
||||
let [r, b, g] = horizontal.areas(area);
|
||||
frame.render_widget(red, r);
|
||||
frame.render_widget(blue, b);
|
||||
frame.render_widget(green, g);
|
||||
|
|
|
@ -170,12 +170,12 @@ impl Widget for &mut App<'_> {
|
|||
Constraint::Min(0),
|
||||
Constraint::Length(2),
|
||||
]);
|
||||
let [header_area, rest_area, footer_area] = area.split(&vertical);
|
||||
let [header_area, rest_area, footer_area] = vertical.areas(area);
|
||||
|
||||
// Create two chunks with equal vertical screen space. One for the list and the other for
|
||||
// the info block.
|
||||
let vertical = Layout::vertical([Constraint::Percentage(50), Constraint::Percentage(50)]);
|
||||
let [upper_item_list_area, lower_item_list_area] = rest_area.split(&vertical);
|
||||
let [upper_item_list_area, lower_item_list_area] = vertical.areas(rest_area);
|
||||
|
||||
self.render_title(header_area, buf);
|
||||
self.render_todo(upper_item_list_area, buf);
|
||||
|
|
|
@ -60,7 +60,7 @@ fn run_app<B: Backend>(terminal: &mut Terminal<B>) -> io::Result<()> {
|
|||
|
||||
fn ui(frame: &mut Frame) {
|
||||
let vertical = Layout::vertical([Constraint::Length(1), Constraint::Min(0)]);
|
||||
let [text_area, main_area] = frame.size().split(&vertical);
|
||||
let [text_area, main_area] = vertical.areas(frame.size());
|
||||
frame.render_widget(
|
||||
Paragraph::new("Note: not all terminals support all modifiers")
|
||||
.style(Style::default().fg(Color::Red).add_modifier(Modifier::BOLD)),
|
||||
|
|
|
@ -82,7 +82,7 @@ fn ui(f: &mut Frame, app: &App) {
|
|||
let area = f.size();
|
||||
|
||||
let vertical = Layout::vertical([Constraint::Percentage(20), Constraint::Percentage(80)]);
|
||||
let [instructions, content] = area.split(&vertical);
|
||||
let [instructions, content] = vertical.areas(area);
|
||||
|
||||
let text = if app.show_popup {
|
||||
"Press p to close the popup"
|
||||
|
|
|
@ -120,10 +120,10 @@ impl Widget for &App {
|
|||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
use Constraint::*;
|
||||
let vertical = Layout::vertical([Length(1), Min(0), Length(1)]);
|
||||
let [header_area, inner_area, footer_area] = area.split(&vertical);
|
||||
let [header_area, inner_area, footer_area] = vertical.areas(area);
|
||||
|
||||
let horizontal = Layout::horizontal([Min(0), Length(20)]);
|
||||
let [tabs_area, title_area] = header_area.split(&horizontal);
|
||||
let [tabs_area, title_area] = horizontal.areas(header_area);
|
||||
|
||||
self.render_title(title_area, buf);
|
||||
self.render_tabs(tabs_area, buf);
|
||||
|
|
|
@ -192,7 +192,7 @@ fn ui(f: &mut Frame, app: &App) {
|
|||
Constraint::Length(3),
|
||||
Constraint::Min(1),
|
||||
]);
|
||||
let [help_area, input_area, messages_area] = f.size().split(&vertical);
|
||||
let [help_area, input_area, messages_area] = vertical.areas(f.size());
|
||||
|
||||
let (msg, style) = match app.input_mode {
|
||||
InputMode::Normal => (
|
||||
|
|
|
@ -420,6 +420,71 @@ impl Layout {
|
|||
self
|
||||
}
|
||||
|
||||
/// Split the rect into a number of sub-rects according to the given [`Layout`]`.
|
||||
///
|
||||
/// An ergonomic wrapper around [`Layout::split`] that returns an array of `Rect`s instead of
|
||||
/// `Rc<[Rect]>`.
|
||||
///
|
||||
/// This method requires the number of constraints to be known at compile time. If you don't
|
||||
/// know the number of constraints at compile time, use [`Layout::split`] instead.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the number of constraints is not equal to the length of the returned array.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use ratatui::prelude::*;
|
||||
/// # fn render(frame: &mut Frame) {
|
||||
/// let area = frame.size();
|
||||
/// let layout = Layout::vertical([Constraint::Length(1), Constraint::Min(0)]);
|
||||
/// let [top, main] = layout.areas(area);
|
||||
///
|
||||
/// // or explicitly specify the number of constraints:
|
||||
/// let areas = layout.areas::<2>(area);
|
||||
/// # }
|
||||
pub fn areas<const N: usize>(&self, area: Rect) -> [Rect; N] {
|
||||
let (areas, _) = self.split_with_spacers(area);
|
||||
areas.to_vec().try_into().expect("invalid number of rects")
|
||||
}
|
||||
|
||||
/// Split the rect into a number of sub-rects according to the given [`Layout`]` and return just
|
||||
/// the spacers between the areas.
|
||||
///
|
||||
/// This method requires the number of constraints to be known at compile time. If you don't
|
||||
/// know the number of constraints at compile time, use [`Layout::split_with_spacers`] instead.
|
||||
///
|
||||
/// This method is similar to [`Layout::areas`], and can be called with the same parameters, but
|
||||
/// it returns just the spacers between the areas. The result of calling the `areas` method is
|
||||
/// cached, so this will generally not re-run the solver, but will just return the cached
|
||||
/// result.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the number of constraints + 1 is not equal to the length of the returned array.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use ratatui::prelude::*;
|
||||
/// # fn render(frame: &mut Frame) {
|
||||
/// let area = frame.size();
|
||||
/// let layout = Layout::vertical([Constraint::Length(1), Constraint::Min(0)]);
|
||||
/// let [top, main] = layout.areas(area);
|
||||
/// let [before, inbetween, after] = layout.spacers(area);
|
||||
///
|
||||
/// // or explicitly specify the number of constraints:
|
||||
/// let spacers = layout.spacers::<2>(area);
|
||||
/// # }
|
||||
pub fn spacers<const N: usize>(&self, area: Rect) -> [Rect; N] {
|
||||
let (_, spacers) = self.split_with_spacers(area);
|
||||
spacers
|
||||
.to_vec()
|
||||
.try_into()
|
||||
.expect("invalid number of rects")
|
||||
}
|
||||
|
||||
/// Wrapper function around the cassowary-rs solver to be able to split a given area into
|
||||
/// smaller ones based on the preferred widths or heights and the direction.
|
||||
///
|
||||
|
@ -433,10 +498,10 @@ impl Layout {
|
|||
/// LruCache, and grows until [`Self::DEFAULT_CACHE_SIZE`] is reached by default, if the cache
|
||||
/// is initialized with the [Layout::init_cache()] grows until the initialized cache size.
|
||||
///
|
||||
/// There is a helper method on Rect that can be used to split the whole area into smaller ones
|
||||
/// based on the layout: [`Rect::split()`]. That method is a shortcut for calling this method.
|
||||
/// It allows you to destructure the result directly into variables, which is useful when you
|
||||
/// know at compile time the number of areas that will be created.
|
||||
/// There is a helper method that can be used to split the whole area into smaller ones based on
|
||||
/// the layout: [`Layout::areas()`]. That method is a shortcut for calling this method. It
|
||||
/// allows you to destructure the result directly into variables, which is useful when you know
|
||||
/// at compile time the number of areas that will be created.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
|
|
@ -239,37 +239,6 @@ impl Rect {
|
|||
&& position.y < self.bottom()
|
||||
}
|
||||
|
||||
/// Split the rect into a number of sub-rects according to the given [`Layout`]`.
|
||||
///
|
||||
/// An ergonomic wrapper around [`Layout::split`] that returns an array of `Rect`s instead of
|
||||
/// `Rc<[Rect]>`.
|
||||
///
|
||||
/// This method requires the number of constraints to be known at compile time. If you don't
|
||||
/// know the number of constraints at compile time, use [`Layout::split`] instead.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the number of constraints is not equal to the length of the returned array.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use ratatui::prelude::*;
|
||||
/// # fn render(frame: &mut Frame) {
|
||||
/// let area = frame.size();
|
||||
/// let layout = Layout::vertical([Constraint::Length(1), Constraint::Min(0)]);
|
||||
/// let [top, main] = area.split(&layout);
|
||||
/// // or explicitly specify the number of constraints:
|
||||
/// let rects = area.split::<2>(&layout);
|
||||
/// # }
|
||||
pub fn split<const N: usize>(self, layout: &Layout) -> [Rect; N] {
|
||||
layout
|
||||
.split(self)
|
||||
.to_vec()
|
||||
.try_into()
|
||||
.expect("invalid number of rects")
|
||||
}
|
||||
|
||||
/// Clamp this rect to fit inside the other rect.
|
||||
///
|
||||
/// If the width or height of this rect is larger than the other rect, it will be clamped to the
|
||||
|
@ -590,8 +559,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn split() {
|
||||
let layout = Layout::horizontal([Constraint::Percentage(50), Constraint::Percentage(50)]);
|
||||
let [a, b] = Rect::new(0, 0, 2, 1).split(&layout);
|
||||
let [a, b] = Layout::horizontal([Constraint::Percentage(50), Constraint::Percentage(50)])
|
||||
.areas(Rect::new(0, 0, 2, 1));
|
||||
assert_eq!(a, Rect::new(0, 0, 1, 1));
|
||||
assert_eq!(b, Rect::new(1, 0, 1, 1));
|
||||
}
|
||||
|
@ -600,7 +569,7 @@ mod tests {
|
|||
#[should_panic(expected = "invalid number of rects")]
|
||||
fn split_invalid_number_of_recs() {
|
||||
let layout = Layout::horizontal([Constraint::Percentage(50), Constraint::Percentage(50)]);
|
||||
let [_a, _b, _c] = Rect::new(0, 0, 2, 1).split(&layout);
|
||||
let [_a, _b, _c] = layout.areas(Rect::new(0, 0, 2, 1));
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
|
|
|
@ -135,7 +135,7 @@ impl<DS: DateStyler> Monthly<'_, DS> {
|
|||
Constraint::Length(self.show_weekday.is_some().into()),
|
||||
Constraint::Fill(1),
|
||||
]);
|
||||
let [month_header, days_header, days_area] = area.split(&layout);
|
||||
let [month_header, days_header, days_area] = layout.areas(area);
|
||||
|
||||
// Draw the month name and year
|
||||
if let Some(style) = self.show_month {
|
||||
|
|
|
@ -745,12 +745,13 @@ impl<'a> Chart<'a> {
|
|||
let legend_width = inner_width + 2;
|
||||
let legend_height = legends.count() as u16 + 2;
|
||||
|
||||
let [max_legend_width] = layout.graph_area.split(
|
||||
&Layout::horizontal([self.hidden_legend_constraints.0]).flex(Flex::Start),
|
||||
);
|
||||
let [max_legend_height] = layout
|
||||
.graph_area
|
||||
.split(&Layout::vertical([self.hidden_legend_constraints.1]).flex(Flex::Start));
|
||||
let [max_legend_width] = Layout::horizontal([self.hidden_legend_constraints.0])
|
||||
.flex(Flex::Start)
|
||||
.areas(layout.graph_area);
|
||||
|
||||
let [max_legend_height] = Layout::vertical([self.hidden_legend_constraints.1])
|
||||
.flex(Flex::Start)
|
||||
.areas(layout.graph_area);
|
||||
|
||||
if inner_width > 0
|
||||
&& legend_width <= max_legend_width.width
|
||||
|
|
|
@ -733,10 +733,8 @@ impl Table<'_> {
|
|||
};
|
||||
// this will always allocate a selection area
|
||||
let [_selection_area, columns_area] =
|
||||
Rect::new(0, 0, max_width, 1).split(&Layout::horizontal([
|
||||
Constraint::Length(selection_width),
|
||||
Constraint::Fill(0),
|
||||
]));
|
||||
Layout::horizontal([Constraint::Length(selection_width), Constraint::Fill(0)])
|
||||
.areas(Rect::new(0, 0, max_width, 1));
|
||||
let rects = Layout::horizontal(widths)
|
||||
.flex(self.flex)
|
||||
.spacing(self.column_spacing)
|
||||
|
|
Loading…
Reference in a new issue