Layout Constraints Basics
Here’s the “hello world” example again:
pub fn render(app: &mut App, f: &mut Frame) {
f.render_widget(
Paragraph::new("Hello World!")
.block(Block::default().borders(Borders::ALL).border_type(BorderType::Rounded)),
f.size()
)
}
Here’s what the docs say for f.size()
:
ratatui::terminal::Frame
pub fn size(&self) -> Rect
────────────────────────────────────────────────────
Frame size, guaranteed not to change when rendering.
f.size()
returns a Rect
struct. A Rect
has the following struct
definition:
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Default)]
pub struct Rect {
pub x: u16,
pub y: u16,
pub width: u16,
pub height: u16,
}
That is to say, they have a x
and y
positional coordinates and width
and height
dimensional
values.
The coordinate system in ratatui
(and in terminals in general) starts at the top left of the
terminal or container widget. This point represents (0,0)
.
Here’s the “hello world” example from above rendered:
╭───────────────────────────────────╮
│Hello World! │
│ │
│ │
╰───────────────────────────────────╯
What if hypothetically we wanted to render this instead:
╭────────────────╮╭─────────────────╮
│Hello World! ││Hello World! │
│ ││ │
│ ││ │
╰────────────────╯╰─────────────────╯
We could integer divide the width
by 2, account of the borders calculate the x
position for
the second paragraph but that is cumbersome and error prone.
Now, that’s where layouts come in.
let rects = Layout::default()
.direction(Direction::Horizontal)
.constraints(
[
Constraint::Percentage(50),
Constraint::Percentage(50),
]
.as_ref(),
)
.split(f.size());
Here we created a layout and added two “constraints”. The constraints determine the size of the
resulting Rect
s. Calling split
on a Layout
splits the layout based on the constraints.
That is, rects
behaves as a Vec<Rect>
, whose length always matches the number of constraints.
So for the example above, we might want to do something like this:
pub fn render(app: &mut App, f: &mut Frame) {
let chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints(
[
Constraint::Percentage(50),
Constraint::Percentage(50),
]
.as_ref(),
)
.split(f.size());
f.render_widget(
Paragraph::new("Hello World!")
.block(Block::default().borders(Borders::ALL).border_type(BorderType::Rounded)),
chunks[0]
)
f.render_widget(
Paragraph::new("Hello World!")
.block(Block::default().borders(Borders::ALL).border_type(BorderType::Rounded)),
chunks[1]
)
}
Notice that we used the first chunk
for the first Paragraph
and the second chunk
for the
second Paragraph
.