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:
Josh McKinney 2024-02-01 20:26:35 -08:00 committed by GitHub
parent f8367fdfdd
commit 87bf1dd9df
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
30 changed files with 148 additions and 123 deletions

View file

@ -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))

View file

@ -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()

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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) {

View file

@ -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);

View file

@ -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());

View file

@ -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
}

View file

@ -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);
}

View file

@ -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),

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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"),

View file

@ -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)

View file

@ -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);

View file

@ -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();

View file

@ -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);

View file

@ -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);

View file

@ -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)),

View file

@ -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"

View file

@ -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);

View file

@ -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 => (

View file

@ -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
///

View file

@ -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]

View file

@ -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 {

View file

@ -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

View file

@ -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)