Skip to content

Commit

Permalink
feat(layout)!: add parameters to Layout::new()
Browse files Browse the repository at this point in the history
Adds a convenience function to create a layout with a direction and a
list of constraints which are the most common parameters that would be
generally configured using the builder pattern. The constraints can be
passed in as any iterator of constraints.

```rust
let layout = Layout::new(Direction::Horizontal, [
    Constraint::Percentage(50),
    Constraint::Percentage(50),
]);
```

BREAKING CHANGE:
Layout::new() now takes a direction and a list of constraints instead of
no arguments. This is a breaking change because it changes the signature
of the function. Layout::new() is also no longer const because it takes
an iterator of constraints.
  • Loading branch information
joshka committed Oct 18, 2023
1 parent fbf1a45 commit d81cbef
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 60 deletions.
19 changes: 19 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,25 @@ This is a quick summary of the sections below:

## Unreleased (0.24.0)

### Layout::new() now takes direction and constraint parameters ([#557])

[#557]: https://github.com/ratatui-org/ratatui/pull/557

Previously layout new took no parameters. Existing code should either use `Layout::default()` or
the new constructor.

```rust
let layout = layout::new()
.direction(Direction::Vertical)
.constraints([Constraint::Min(1), Constraint::Max(2)]);
// becomes either
let layout = layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Min(1), Constraint::Max(2)]);
// or
let layout = layout::new(Direction::Vertical, [Constraint::Min(1), Constraint::Max(2)]);
```

### ScrollbarState field type changed from `u16` to `usize` ([#456])

[#456]: https://github.com/ratatui-org/ratatui/pull/456
Expand Down
33 changes: 18 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,14 +188,15 @@ section of the [Ratatui Book] for more info.
use ratatui::{prelude::*, widgets::*};

fn ui(frame: &mut Frame) {
let main_layout = Layout::default()
.direction(Direction::Vertical)
.constraints([
let main_layout = Layout::new(
Direction::Vertical,
[
Constraint::Length(1),
Constraint::Min(0),
Constraint::Length(1),
])
.split(frame.size());
]
)
.split(frame.size());
frame.render_widget(
Block::new().borders(Borders::TOP).title("Title Bar"),
main_layout[0],
Expand All @@ -205,10 +206,11 @@ fn ui(frame: &mut Frame) {
main_layout[2],
);

let inner_layout = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
.split(main_layout[1]);
let inner_layout = Layout::new(
Direction::Horizontal,
[Constraint::Percentage(50), Constraint::Percentage(50)]
)
.split(main_layout[1]);
frame.render_widget(
Block::default().borders(Borders::ALL).title("Left"),
inner_layout[0],
Expand Down Expand Up @@ -240,16 +242,17 @@ short-hand syntax to apply a style to widgets and text. See the [Styling Text] s
use ratatui::{prelude::*, widgets::*};

fn ui(frame: &mut Frame) {
let areas = Layout::default()
.direction(Direction::Vertical)
.constraints([
let areas = Layout::new(
Direction::Vertical,
[
Constraint::Length(1),
Constraint::Length(1),
Constraint::Length(1),
Constraint::Length(1),
Constraint::Min(0),
])
.split(frame.size());
]
)
.split(frame.size());

let span1 = Span::raw("Hello ");
let span2 = Span::styled(
Expand Down Expand Up @@ -293,7 +296,7 @@ Running this example produces the following output:
[Backends]: https://ratatui.rs/concepts/backends/index.html
[Widgets]: https://ratatui.rs/how-to/widgets/index.html
[Handling Events]: https://ratatui.rs/concepts/event_handling.html
[Layout]: https://ratatui.rs/how-to/layout/index.html
[Layout]: https://ratatui.rs/concepts/layout/index.html
[Styling Text]: https://ratatui.rs/how-to/render/style-text.html
[rust-tui-template]: https://github.com/ratatui-org/rust-tui-template
[ratatui-async-template]: https://ratatui-org.github.io/ratatui-async-template/
Expand Down
153 changes: 123 additions & 30 deletions src/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,10 +196,11 @@ pub(crate) enum SegmentSize {
/// use ratatui::{prelude::*, widgets::*};
///
/// fn render(frame: &mut Frame, area: Rect) {
/// let layout = Layout::default()
/// .direction(Direction::Vertical)
/// .constraints([Constraint::Length(5), Constraint::Min(0)])
/// .split(Rect::new(0, 0, 10, 10));
/// let layout = Layout::new(
/// Direction::Vertical,
/// [Constraint::Length(5), Constraint::Min(0)],
/// )
/// .split(Rect::new(0, 0, 10, 10));
/// frame.render_widget(Paragraph::new("foo"), layout[0]);
/// frame.render_widget(Paragraph::new("bar"), layout[1]);
/// }
Expand All @@ -223,26 +224,21 @@ pub struct Layout {

impl Default for Layout {
fn default() -> Layout {
Layout::new()
Layout::new(Direction::Vertical, [])
}
}

impl Layout {
pub const DEFAULT_CACHE_SIZE: usize = 16;
/// Creates a new layout with default values.
///
/// - direction: [Direction::Vertical]
/// - margin: 0, 0
/// - constraints: empty
/// - segment_size: SegmentSize::LastTakesRemainder
pub const fn new() -> Layout {
pub fn new<C: AsRef<[Constraint]>>(direction: Direction, constraints: C) -> Layout {
Layout {
direction: Direction::Vertical,
margin: Margin {
horizontal: 0,
vertical: 0,
},
constraints: Vec::new(),
direction,
margin: Margin::new(0, 0),
constraints: constraints.as_ref().to_vec(),
segment_size: SegmentSize::LastTakesRemainder,
}
}
Expand Down Expand Up @@ -292,11 +288,8 @@ impl Layout {
/// Rect::new(0, 8, 10, 2),
/// ]);
/// ```
pub fn constraints<C>(mut self, constraints: C) -> Layout
where
C: Into<Vec<Constraint>>,
{
self.constraints = constraints.into();
pub fn constraints<C: AsRef<[Constraint]>>(mut self, constraints: C) -> Layout {
self.constraints = constraints.as_ref().to_vec();
self
}

Expand Down Expand Up @@ -620,6 +613,117 @@ mod tests {
})
}

#[test]
fn layout_new() {
// array
let fixed_size_array = [Constraint::Min(0)];
let layout = Layout::new(Direction::Horizontal, fixed_size_array);
assert_eq!(layout.direction, Direction::Horizontal);
assert_eq!(layout.constraints, [Constraint::Min(0)]);

// slice of a fixed size array
let slice_of_fixed_size_array = &[Constraint::Min(0)];
let layout = Layout::new(Direction::Horizontal, slice_of_fixed_size_array);
assert_eq!(layout.direction, Direction::Horizontal);
assert_eq!(layout.constraints, [Constraint::Min(0)]);

// slice of vec
let vec = &[Constraint::Min(0)].to_vec();
let constraints = vec.as_slice();
let layout = Layout::new(Direction::Horizontal, constraints);
assert_eq!(layout.direction, Direction::Horizontal);
assert_eq!(layout.constraints, [Constraint::Min(0)]);

// vec
let layout = Layout::new(Direction::Horizontal, vec);
assert_eq!(layout.direction, Direction::Horizontal);
assert_eq!(layout.constraints, [Constraint::Min(0)]);
}

#[test]
#[allow(clippy::needless_borrow, clippy::unnecessary_to_owned)]
fn layout_constraints() {
const CONSTRAINTS: [Constraint; 2] = [Constraint::Min(0), Constraint::Max(10)];
let fixed_size_array = CONSTRAINTS;
assert_eq!(
Layout::default().constraints(fixed_size_array).constraints,
CONSTRAINTS,
"constraints should be settable with an array"

Check warning on line 651 in src/layout.rs

View check run for this annotation

Codecov / codecov/patch

src/layout.rs#L651

Added line #L651 was not covered by tests
);

let slice_of_fixed_size_array = &CONSTRAINTS;
assert_eq!(
Layout::default()
.constraints(slice_of_fixed_size_array)
.constraints,
CONSTRAINTS,
"constraints should be settable with a slice"

Check warning on line 660 in src/layout.rs

View check run for this annotation

Codecov / codecov/patch

src/layout.rs#L660

Added line #L660 was not covered by tests
);

let vec = CONSTRAINTS.to_vec();
let slice_of_vec = vec.as_slice();
assert_eq!(
Layout::default().constraints(slice_of_vec).constraints,
CONSTRAINTS,
"constraints should be settable with a slice"

Check warning on line 668 in src/layout.rs

View check run for this annotation

Codecov / codecov/patch

src/layout.rs#L668

Added line #L668 was not covered by tests
);

assert_eq!(
Layout::default().constraints(vec).constraints,
CONSTRAINTS,
"constraints should be settable with a Vec"

Check warning on line 674 in src/layout.rs

View check run for this annotation

Codecov / codecov/patch

src/layout.rs#L674

Added line #L674 was not covered by tests
);
}

#[test]
fn layout_direction() {
assert_eq!(
Layout::default().direction(Direction::Horizontal).direction,
Direction::Horizontal
);
assert_eq!(
Layout::default().direction(Direction::Vertical).direction,
Direction::Vertical
);
}

#[test]
fn layout_margins() {
assert_eq!(Layout::default().margin(10).margin, Margin::new(10, 10));
assert_eq!(
Layout::default().horizontal_margin(10).margin,
Margin::new(10, 0)
);
assert_eq!(
Layout::default().vertical_margin(10).margin,
Margin::new(0, 10)
);
assert_eq!(
Layout::default()
.horizontal_margin(10)
.vertical_margin(20)
.margin,
Margin::new(10, 20)
);
}

#[test]
fn layout_segment_size() {
assert_eq!(
Layout::default()
.segment_size(EvenDistribution)
.segment_size,
EvenDistribution
);
assert_eq!(
Layout::default()
.segment_size(LastTakesRemainder)
.segment_size,
LastTakesRemainder
);
assert_eq!(Layout::default().segment_size(None).segment_size, None);
}

#[test]
fn corner_to_string() {
assert_eq!(Corner::BottomLeft.to_string(), "BottomLeft");
Expand Down Expand Up @@ -843,17 +947,6 @@ mod tests {
assert_eq!(Constraint::Min(u16::MAX).apply(100), u16::MAX);
}

#[test]
fn layout_can_be_const() {
const _LAYOUT: Layout = Layout::new();
const _DEFAULT_LAYOUT: Layout = Layout::new()
.direction(Direction::Horizontal)
.margin(1)
.segment_size(SegmentSize::LastTakesRemainder);
const _HORIZONTAL_LAYOUT: Layout = Layout::new().horizontal_margin(1);
const _VERTICAL_LAYOUT: Layout = Layout::new().vertical_margin(1);
}

/// Tests for the `Layout::split()` function.
///
/// There are many tests in this as the number of edge cases that are caused by the interaction
Expand Down
33 changes: 18 additions & 15 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,14 +167,15 @@
//! use ratatui::{prelude::*, widgets::*};
//!
//! fn ui(frame: &mut Frame) {
//! let main_layout = Layout::default()
//! .direction(Direction::Vertical)
//! .constraints([
//! let main_layout = Layout::new(
//! Direction::Vertical,
//! [
//! Constraint::Length(1),
//! Constraint::Min(0),
//! Constraint::Length(1),
//! ])
//! .split(frame.size());
//! ]
//! )
//! .split(frame.size());
//! frame.render_widget(
//! Block::new().borders(Borders::TOP).title("Title Bar"),
//! main_layout[0],
Expand All @@ -184,10 +185,11 @@
//! main_layout[2],
//! );
//!
//! let inner_layout = Layout::default()
//! .direction(Direction::Horizontal)
//! .constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
//! .split(main_layout[1]);
//! let inner_layout = Layout::new(
//! Direction::Horizontal,
//! [Constraint::Percentage(50), Constraint::Percentage(50)]
//! )
//! .split(main_layout[1]);
//! frame.render_widget(
//! Block::default().borders(Borders::ALL).title("Left"),
//! inner_layout[0],
Expand Down Expand Up @@ -219,16 +221,17 @@
//! use ratatui::{prelude::*, widgets::*};
//!
//! fn ui(frame: &mut Frame) {
//! let areas = Layout::default()
//! .direction(Direction::Vertical)
//! .constraints([
//! let areas = Layout::new(
//! Direction::Vertical,
//! [
//! Constraint::Length(1),
//! Constraint::Length(1),
//! Constraint::Length(1),
//! Constraint::Length(1),
//! Constraint::Min(0),
//! ])
//! .split(frame.size());
//! ]
//! )
//! .split(frame.size());
//!
//! let span1 = Span::raw("Hello ");
//! let span2 = Span::styled(
Expand Down Expand Up @@ -290,7 +293,7 @@
//! [Backends]: https://ratatui.rs/concepts/backends/index.html
//! [Widgets]: https://ratatui.rs/how-to/widgets/index.html
//! [Handling Events]: https://ratatui.rs/concepts/event_handling.html
//! [Layout]: https://ratatui.rs/how-to/layout/index.html
//! [Layout]: https://ratatui.rs/concepts/layout/index.html
//! [Styling Text]: https://ratatui.rs/how-to/render/style-text.html
//! [rust-tui-template]: https://github.com/ratatui-org/rust-tui-template
//! [ratatui-async-template]: https://ratatui-org.github.io/ratatui-async-template/
Expand Down

0 comments on commit d81cbef

Please sign in to comment.