Compare commits
5 Commits
v0.27.0-al
...
rect-offse
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ea840d87f | ||
|
|
26af65043e | ||
|
|
07da90a718 | ||
|
|
125ee929ee | ||
|
|
742a5ead06 |
7
FUNDING.json
Normal file
7
FUNDING.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"drips": {
|
||||
"ethereum": {
|
||||
"ownedBy": "0x6053C8984f4F214Ad12c4653F28514E1E09213B5"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,6 +40,16 @@ pub struct Offset {
|
||||
pub y: i32,
|
||||
}
|
||||
|
||||
impl<X: Into<i32>, Y: Into<i32>> From<(X, Y)> for Offset {
|
||||
/// Creates a new `Offset` from a tuple of (x, y).
|
||||
fn from((x, y): (X, Y)) -> Self {
|
||||
Self {
|
||||
x: x.into(),
|
||||
y: y.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Rect {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}x{}+{}+{}", self.width, self.height, self.x, self.y)
|
||||
@@ -135,9 +145,20 @@ impl Rect {
|
||||
/// - Positive `x` moves the whole `Rect` to the right, negative to the left.
|
||||
/// - Positive `y` moves the whole `Rect` to the bottom, negative to the top.
|
||||
///
|
||||
/// See [`Offset`] for details.
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use ratatui::{prelude::*, layout::Offset};
|
||||
/// let rect = Rect::new(1, 2, 3, 4);
|
||||
/// let rect = rect.offset(Offset { x: 10, y: 20 });
|
||||
/// assert_eq!(rect, Rect::new(11, 22, 3, 4));
|
||||
///
|
||||
/// // offset can also be called with a tuple of (x, y)
|
||||
/// let rect = rect.offset((10, 20));
|
||||
/// ```
|
||||
#[must_use = "method returns the modified value"]
|
||||
pub fn offset(self, offset: Offset) -> Self {
|
||||
pub fn offset<T: Into<Offset>>(self, offset: T) -> Self {
|
||||
let offset = offset.into();
|
||||
Self {
|
||||
x: i32::from(self.x)
|
||||
.saturating_add(offset.x)
|
||||
@@ -423,6 +444,11 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn offset_from_tuple() {
|
||||
assert_eq!(Rect::new(1, 2, 3, 4).offset((5, 6)), Rect::new(6, 8, 3, 4));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn union() {
|
||||
assert_eq!(
|
||||
|
||||
@@ -48,6 +48,7 @@ use crate::prelude::*;
|
||||
/// - [`Line::reset_style`] resets the style of the line.
|
||||
/// - [`Line::width`] returns the unicode width of the content held by this line.
|
||||
/// - [`Line::styled_graphemes`] returns an iterator over the graphemes held by this line.
|
||||
/// - [`Line::push_span`] adds a span to the line.
|
||||
///
|
||||
/// # Compatibility Notes
|
||||
///
|
||||
@@ -451,6 +452,23 @@ impl<'a> Line<'a> {
|
||||
pub fn iter_mut(&mut self) -> std::slice::IterMut<Span<'a>> {
|
||||
self.spans.iter_mut()
|
||||
}
|
||||
|
||||
/// Adds a span to the line.
|
||||
///
|
||||
/// `span` can be any type that is convertible into a `Span`. For example, you can pass a
|
||||
/// `&str`, a `String`, or a `Span`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use ratatui::prelude::*;
|
||||
/// let mut line = Line::from("Hello, ");
|
||||
/// line.push_span(Span::raw("world!"));
|
||||
/// line.push_span(" How are you?");
|
||||
/// ```
|
||||
pub fn push_span<T: Into<Span<'a>>>(&mut self, span: T) {
|
||||
self.spans.push(span.into());
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for Line<'a> {
|
||||
@@ -587,6 +605,11 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[fixture]
|
||||
fn small_buf() -> Buffer {
|
||||
Buffer::empty(Rect::new(0, 0, 10, 1))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn raw_str() {
|
||||
let line = Line::raw("test content");
|
||||
@@ -825,6 +848,35 @@ mod tests {
|
||||
assert_eq!(format!("{line_from_styled_span}"), "Hello, world!");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn left_aligned() {
|
||||
let line = Line::from("Hello, world!").left_aligned();
|
||||
assert_eq!(line.alignment, Some(Alignment::Left));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn centered() {
|
||||
let line = Line::from("Hello, world!").centered();
|
||||
assert_eq!(line.alignment, Some(Alignment::Center));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn right_aligned() {
|
||||
let line = Line::from("Hello, world!").right_aligned();
|
||||
assert_eq!(line.alignment, Some(Alignment::Right));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn push_span() {
|
||||
let mut line = Line::from("A");
|
||||
line.push_span(Span::raw("B"));
|
||||
line.push_span("C");
|
||||
assert_eq!(
|
||||
line.spans,
|
||||
vec![Span::raw("A"), Span::raw("B"), Span::raw("C")]
|
||||
);
|
||||
}
|
||||
|
||||
mod widget {
|
||||
use super::*;
|
||||
use crate::assert_buffer_eq;
|
||||
@@ -832,6 +884,7 @@ mod tests {
|
||||
const GREEN: Style = Style::new().fg(Color::Green);
|
||||
const ITALIC: Style = Style::new().add_modifier(Modifier::ITALIC);
|
||||
|
||||
#[fixture]
|
||||
fn hello_world() -> Line<'static> {
|
||||
Line::from(vec![
|
||||
Span::styled("Hello ", BLUE),
|
||||
@@ -851,6 +904,13 @@ mod tests {
|
||||
assert_buffer_eq!(buf, expected);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn render_out_of_bounds(hello_world: Line<'static>, mut small_buf: Buffer) {
|
||||
let out_of_bounds = Rect::new(20, 20, 10, 1);
|
||||
hello_world.render(out_of_bounds, &mut small_buf);
|
||||
assert_buffer_eq!(small_buf, Buffer::empty(small_buf.area));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn render_only_styles_line_area() {
|
||||
let mut buf = Buffer::empty(Rect::new(0, 0, 20, 1));
|
||||
@@ -895,24 +955,6 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn left_aligned() {
|
||||
let line = Line::from("Hello, world!").left_aligned();
|
||||
assert_eq!(line.alignment, Some(Alignment::Left));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn centered() {
|
||||
let line = Line::from("Hello, world!").centered();
|
||||
assert_eq!(line.alignment, Some(Alignment::Center));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn right_aligned() {
|
||||
let line = Line::from("Hello, world!").right_aligned();
|
||||
assert_eq!(line.alignment, Some(Alignment::Right));
|
||||
}
|
||||
|
||||
mod iterators {
|
||||
use super::*;
|
||||
|
||||
|
||||
@@ -363,6 +363,7 @@ impl Widget for Span<'_> {
|
||||
|
||||
impl WidgetRef for Span<'_> {
|
||||
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||||
let area = area.intersection(buf.area);
|
||||
let Rect {
|
||||
x: mut current_x,
|
||||
y,
|
||||
@@ -402,8 +403,15 @@ impl std::fmt::Display for Span<'_> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rstest::fixture;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[fixture]
|
||||
fn small_buf() -> Buffer {
|
||||
Buffer::empty(Rect::new(0, 0, 10, 1))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default() {
|
||||
let span = Span::default();
|
||||
@@ -532,7 +540,30 @@ mod tests {
|
||||
assert_eq!(format!("{stylized_span}"), "stylized test content");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn left_aligned() {
|
||||
let span = Span::styled("Test Content", Style::new().green().italic());
|
||||
let line = span.into_left_aligned_line();
|
||||
assert_eq!(line.alignment, Some(Alignment::Left));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn centered() {
|
||||
let span = Span::styled("Test Content", Style::new().green().italic());
|
||||
let line = span.into_centered_line();
|
||||
assert_eq!(line.alignment, Some(Alignment::Center));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn right_aligned() {
|
||||
let span = Span::styled("Test Content", Style::new().green().italic());
|
||||
let line = span.into_right_aligned_line();
|
||||
assert_eq!(line.alignment, Some(Alignment::Right));
|
||||
}
|
||||
|
||||
mod widget {
|
||||
use rstest::rstest;
|
||||
|
||||
use super::*;
|
||||
use crate::assert_buffer_eq;
|
||||
|
||||
@@ -550,6 +581,13 @@ mod tests {
|
||||
assert_buffer_eq!(buf, expected);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn render_out_of_bounds(mut small_buf: Buffer) {
|
||||
let out_of_bounds = Rect::new(20, 20, 10, 1);
|
||||
Span::raw("Hello, World!").render(out_of_bounds, &mut small_buf);
|
||||
assert_eq!(small_buf, Buffer::empty(small_buf.area));
|
||||
}
|
||||
|
||||
/// When the content of the span is longer than the area passed to render, the content
|
||||
/// should be truncated
|
||||
#[test]
|
||||
@@ -632,25 +670,4 @@ mod tests {
|
||||
assert_buffer_eq!(buf, expected);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn left_aligned() {
|
||||
let span = Span::styled("Test Content", Style::new().green().italic());
|
||||
let line = span.into_left_aligned_line();
|
||||
assert_eq!(line.alignment, Some(Alignment::Left));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn centered() {
|
||||
let span = Span::styled("Test Content", Style::new().green().italic());
|
||||
let line = span.into_centered_line();
|
||||
assert_eq!(line.alignment, Some(Alignment::Center));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn right_aligned() {
|
||||
let span = Span::styled("Test Content", Style::new().green().italic());
|
||||
let line = span.into_right_aligned_line();
|
||||
assert_eq!(line.alignment, Some(Alignment::Right));
|
||||
}
|
||||
}
|
||||
|
||||
137
src/text/text.rs
137
src/text/text.rs
@@ -50,6 +50,8 @@ use crate::prelude::*;
|
||||
/// - [`Text::height`] returns the height.
|
||||
/// - [`Text::patch_style`] patches the style of this `Text`, adding modifiers from the given style.
|
||||
/// - [`Text::reset_style`] resets the style of the `Text`.
|
||||
/// - [`Text::push_line`] adds a line to the text.
|
||||
/// - [`Text::push_span`] adds a span to the last line of the text.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -441,6 +443,46 @@ impl<'a> Text<'a> {
|
||||
pub fn iter_mut(&mut self) -> std::slice::IterMut<Line<'a>> {
|
||||
self.lines.iter_mut()
|
||||
}
|
||||
|
||||
/// Adds a line to the text.
|
||||
///
|
||||
/// `line` can be any type that can be converted into a `Line`. For example, you can pass a
|
||||
/// `&str`, a `String`, a `Span`, or a `Line`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use ratatui::prelude::*;
|
||||
/// let mut text = Text::from("Hello, world!");
|
||||
/// text.push_line(Line::from("How are you?"));
|
||||
/// text.push_line(Span::from("How are you?"));
|
||||
/// text.push_line("How are you?");
|
||||
/// ```
|
||||
pub fn push_line<T: Into<Line<'a>>>(&mut self, line: T) {
|
||||
self.lines.push(line.into());
|
||||
}
|
||||
|
||||
/// Adds a span to the last line of the text.
|
||||
///
|
||||
/// `span` can be any type that is convertible into a `Span`. For example, you can pass a
|
||||
/// `&str`, a `String`, or a `Span`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use ratatui::prelude::*;
|
||||
/// let mut text = Text::from("Hello, world!");
|
||||
/// text.push_span(Span::from("How are you?"));
|
||||
/// text.push_span("How are you?");
|
||||
/// ```
|
||||
pub fn push_span<T: Into<Span<'a>>>(&mut self, span: T) {
|
||||
let span = span.into();
|
||||
if let Some(last) = self.lines.last_mut() {
|
||||
last.push_span(span);
|
||||
} else {
|
||||
self.lines.push(Line::from(span));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for Text<'a> {
|
||||
@@ -559,6 +601,7 @@ impl Widget for Text<'_> {
|
||||
|
||||
impl WidgetRef for Text<'_> {
|
||||
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
|
||||
let area = area.intersection(buf.area);
|
||||
buf.set_style(area, self.style);
|
||||
for (line, row) in self.iter().zip(area.rows()) {
|
||||
let line_width = line.width() as u16;
|
||||
@@ -601,6 +644,11 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[fixture]
|
||||
fn small_buf() -> Buffer {
|
||||
Buffer::empty(Rect::new(0, 0, 10, 1))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn raw() {
|
||||
let text = Text::raw("The first line\nThe second line");
|
||||
@@ -848,6 +896,70 @@ mod tests {
|
||||
assert_eq!(Text::default().italic().style, Modifier::ITALIC.into());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn left_aligned() {
|
||||
let text = Text::from("Hello, world!").left_aligned();
|
||||
assert_eq!(text.alignment, Some(Alignment::Left));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn centered() {
|
||||
let text = Text::from("Hello, world!").centered();
|
||||
assert_eq!(text.alignment, Some(Alignment::Center));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn right_aligned() {
|
||||
let text = Text::from("Hello, world!").right_aligned();
|
||||
assert_eq!(text.alignment, Some(Alignment::Right));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn push_line() {
|
||||
let mut text = Text::from("A");
|
||||
text.push_line(Line::from("B"));
|
||||
text.push_line(Span::from("C"));
|
||||
text.push_line("D");
|
||||
assert_eq!(
|
||||
text.lines,
|
||||
vec![
|
||||
Line::raw("A"),
|
||||
Line::raw("B"),
|
||||
Line::raw("C"),
|
||||
Line::raw("D")
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn push_line_empty() {
|
||||
let mut text = Text::default();
|
||||
text.push_line(Line::from("Hello, world!"));
|
||||
assert_eq!(text.lines, vec![Line::from("Hello, world!")]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn push_span() {
|
||||
let mut text = Text::from("A");
|
||||
text.push_span(Span::raw("B"));
|
||||
text.push_span("C");
|
||||
assert_eq!(
|
||||
text.lines,
|
||||
vec![Line::from(vec![
|
||||
Span::raw("A"),
|
||||
Span::raw("B"),
|
||||
Span::raw("C")
|
||||
])],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn push_span_empty() {
|
||||
let mut text = Text::default();
|
||||
text.push_span(Span::raw("Hello, world!"));
|
||||
assert_eq!(text.lines, vec![Line::from(Span::raw("Hello, world!"))],);
|
||||
}
|
||||
|
||||
mod widget {
|
||||
use super::*;
|
||||
use crate::assert_buffer_eq;
|
||||
@@ -865,6 +977,13 @@ mod tests {
|
||||
assert_buffer_eq!(buf, expected_buf);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn render_out_of_bounds(mut small_buf: Buffer) {
|
||||
let out_of_bounds_area = Rect::new(20, 20, 10, 1);
|
||||
Text::from("Hello, world!").render(out_of_bounds_area, &mut small_buf);
|
||||
assert_eq!(small_buf, Buffer::empty(small_buf.area));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn render_right_aligned() {
|
||||
let text = Text::from("foo").alignment(Alignment::Right);
|
||||
@@ -945,24 +1064,6 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn left_aligned() {
|
||||
let text = Text::from("Hello, world!").left_aligned();
|
||||
assert_eq!(text.alignment, Some(Alignment::Left));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn centered() {
|
||||
let text = Text::from("Hello, world!").centered();
|
||||
assert_eq!(text.alignment, Some(Alignment::Center));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn right_aligned() {
|
||||
let text = Text::from("Hello, world!").right_aligned();
|
||||
assert_eq!(text.alignment, Some(Alignment::Right));
|
||||
}
|
||||
|
||||
mod iterators {
|
||||
use super::*;
|
||||
|
||||
|
||||
@@ -355,7 +355,7 @@ impl<W: WidgetRef> WidgetRef for Option<W> {
|
||||
/// to a stateful widget and render it later. It also allows you to render boxed stateful widgets.
|
||||
///
|
||||
/// This trait was introduced in Ratatui 0.26.0 and is implemented for all the internal stateful
|
||||
/// widgets. Implemetors should prefer to implement this over the `StatefulWidget` trait and add an
|
||||
/// widgets. Implementors should prefer to implement this over the `StatefulWidget` trait and add an
|
||||
/// implementation of `StatefulWidget` that calls `StatefulWidgetRef::render_ref` where backwards
|
||||
/// compatibility is required.
|
||||
///
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
//!
|
||||
//!
|
||||
//!
|
||||
//! The [`Monthly`] widget will display a calendar for the monh provided in `display_date`. Days are
|
||||
//! styled using the default style unless:
|
||||
//! The [`Monthly`] widget will display a calendar for the month provided in `display_date`. Days
|
||||
//! are styled using the default style unless:
|
||||
//! * `show_surrounding` is set, then days not in the `display_date` month will use that style.
|
||||
//! * a style is returned by the [`DateStyler`] for the day
|
||||
//!
|
||||
|
||||
@@ -330,7 +330,7 @@ impl<'a> Dataset<'a> {
|
||||
|
||||
/// Sets the data points of this dataset
|
||||
///
|
||||
/// Points will then either be rendered as scrattered points or with lines between them
|
||||
/// Points will then either be rendered as scattered points or with lines between them
|
||||
/// depending on [`Dataset::graph_type`].
|
||||
///
|
||||
/// Data consist in an array of `f64` tuples (`(f64, f64)`), the first element being X and the
|
||||
@@ -493,7 +493,7 @@ pub struct Chart<'a> {
|
||||
style: Style,
|
||||
/// Constraints used to determine whether the legend should be shown or not
|
||||
hidden_legend_constraints: (Constraint, Constraint),
|
||||
/// The position detnermine where the legenth is shown or hide regaurdless of
|
||||
/// The position determine where the length is shown or hide regardless of
|
||||
/// `hidden_legend_constraints`
|
||||
legend_position: Option<LegendPosition>,
|
||||
}
|
||||
@@ -636,7 +636,7 @@ impl<'a> Chart<'a> {
|
||||
/// let chart = Chart::new(vec![]).hidden_legend_constraints(constraints);
|
||||
/// ```
|
||||
///
|
||||
/// Always hide the legend. Note this can be accomplished more exclicitely by passing `None` to
|
||||
/// Always hide the legend. Note this can be accomplished more explicitly by passing `None` to
|
||||
/// [`Chart::legend_position`].
|
||||
///
|
||||
/// ```
|
||||
|
||||
@@ -643,7 +643,7 @@ impl<'a> List<'a> {
|
||||
/// - [`HighlightSpacing::Always`] will always allocate the spacing, regardless of whether an
|
||||
/// item is selected or not. This means that the table will never change size, regardless of
|
||||
/// if an item is selected or not.
|
||||
/// - [`HighlightSpacing::WhenSelected`] will only allocate the spacing if an itemis selected.
|
||||
/// - [`HighlightSpacing::WhenSelected`] will only allocate the spacing if an item is selected.
|
||||
/// This means that the table will shift when an item is selected. This is the default setting
|
||||
/// for backwards compatibility, but it is recommended to use `HighlightSpacing::Always` for a
|
||||
/// better user experience.
|
||||
@@ -2127,9 +2127,9 @@ mod tests {
|
||||
terminal.backend().assert_buffer(&expected);
|
||||
}
|
||||
|
||||
/// If there isnt enough room for the selected item and the requested padding the list can jump
|
||||
/// up and down every frame if something isnt done about it. This code tests to make sure that
|
||||
/// isnt currently happening
|
||||
/// If there isn't enough room for the selected item and the requested padding the list can jump
|
||||
/// up and down every frame if something isn't done about it. This code tests to make sure that
|
||||
/// isn't currently happening
|
||||
#[test]
|
||||
fn test_padding_flicker() {
|
||||
let backend = backend::TestBackend::new(10, 5);
|
||||
|
||||
Reference in New Issue
Block a user