refactor: modularize

WIP - this is just a PoC to understand whether this would work fine, termion and termwiz disabled for now
This commit is contained in:
Josh McKinney 2024-09-27 13:19:43 -07:00
parent bc10af5931
commit 26163effdf
No known key found for this signature in database
GPG key ID: 722287396A903BC5
202 changed files with 1906 additions and 1244 deletions

View file

@ -1,54 +1,41 @@
[package] [workspace]
name = "ratatui" resolver = "2"
version = "0.28.1" # crate version members = ["ratatui*"]
authors = ["Florian Dehau <work@fdehau.com>", "The Ratatui Developers"] # disabled for now because of the orphan rule on conversions
description = "A library that's all about cooking up terminal user interfaces" # <https://github.com/ratatui/ratatui/issues/1388#issuecomment-2379895747>
documentation = "https://docs.rs/ratatui/latest/ratatui/" exclude = ["ratatui-termion", "ratatui-termwiz"]
repository = "https://github.com/ratatui/ratatui"
homepage = "https://ratatui.rs" [workspace.dependencies]
keywords = ["tui", "terminal", "dashboard"] ratatui = { path = "ratatui" }
categories = ["command-line-interface"] ratatui-core = { path = "ratatui-core" }
readme = "README.md" ratatui-crossterm = { path = "ratatui-crossterm" }
license = "MIT" ratatui-termion = { path = "ratatui-termion" }
exclude = [ ratatui-termwiz = { path = "ratatui-termwiz" }
"assets/*", ratatui-widgets = { path = "ratatui-widgets" }
".github",
"Makefile.toml",
"CONTRIBUTING.md",
"*.log",
"tags",
]
edition = "2021"
rust-version = "1.74.0"
[dependencies]
bitflags = "2.3" bitflags = "2.3"
cassowary = "0.3" cassowary = "0.3"
compact_str = "0.8.0" compact_str = "0.8.0"
crossterm = { version = "0.28.1", optional = true } crossterm = { version = "0.28.1" }
document-features = { version = "0.2.7", optional = true } document-features = { version = "0.2.7" }
instability = "0.3.1" instability = "0.3.1"
itertools = "0.13" itertools = "0.13"
lru = "0.12.0" lru = "0.12.0"
paste = "1.0.2" paste = "1.0.2"
palette = { version = "0.7.6", optional = true } palette = { version = "0.7.6" }
serde = { version = "1", optional = true, features = ["derive"] } serde = { version = "1", features = ["derive"] }
strum = { version = "0.26.3", features = ["derive"] } strum = { version = "0.26.3", features = ["derive"] }
termwiz = { version = "0.22.0", optional = true } termion = { version = "4.0.0" }
time = { version = "0.3.11", optional = true, features = ["local-offset"] } termwiz = { version = "0.22.0" }
time = { version = "0.3.11", features = ["local-offset"] }
unicode-segmentation = "1.10" unicode-segmentation = "1.10"
unicode-truncate = "1" unicode-truncate = "1"
unicode-width = "=0.1.13" unicode-width = "=0.1.13"
[target.'cfg(not(windows))'.dependencies] # dev-dependencies
# termion is not supported on Windows
termion = { version = "4.0.0", optional = true }
[dev-dependencies]
argh = "0.1.12" argh = "0.1.12"
color-eyre = "0.6.2" color-eyre = "0.6.2"
criterion = { version = "0.5.1", features = ["html_reports"] } criterion = { version = "0.5.1", features = ["html_reports"] }
crossterm = { version = "0.28.1", features = ["event-stream"] }
fakeit = "1.1" fakeit = "1.1"
font8x8 = "0.3.1" font8x8 = "0.3.1"
futures = "0.3.30" futures = "0.3.30"
@ -68,313 +55,3 @@ tokio = { version = "1.39.2", features = [
tracing = "0.1.40" tracing = "0.1.40"
tracing-appender = "0.2.3" tracing-appender = "0.2.3"
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
[lints.rust]
unsafe_code = "forbid"
[lints.clippy]
cargo = { level = "warn", priority = -1 }
pedantic = { level = "warn", priority = -1 }
cast_possible_truncation = "allow"
cast_possible_wrap = "allow"
cast_precision_loss = "allow"
cast_sign_loss = "allow"
missing_errors_doc = "allow"
missing_panics_doc = "allow"
module_name_repetitions = "allow"
must_use_candidate = "allow"
# we often split up a module into multiple files with the main type in a file named after the
# module, so we want to allow this pattern
module_inception = "allow"
# nursery or restricted
as_underscore = "warn"
deref_by_slicing = "warn"
else_if_without_else = "warn"
empty_line_after_doc_comments = "warn"
equatable_if_let = "warn"
fn_to_numeric_cast_any = "warn"
format_push_string = "warn"
map_err_ignore = "warn"
missing_const_for_fn = "warn"
mixed_read_write_in_expression = "warn"
mod_module_files = "warn"
needless_pass_by_ref_mut = "warn"
needless_raw_strings = "warn"
or_fun_call = "warn"
redundant_type_annotations = "warn"
rest_pat_in_fully_bound_structs = "warn"
string_lit_chars_any = "warn"
string_slice = "warn"
string_to_string = "warn"
unnecessary_self_imports = "warn"
use_self = "warn"
[features]
#! The crate provides a set of optional features that can be enabled in your `cargo.toml` file.
#!
## By default, we enable the crossterm backend as this is a reasonable choice for most applications
## as it is supported on Linux/Mac/Windows systems. We also enable the `underline-color` feature
## which allows you to set the underline color of text.
default = ["crossterm", "underline-color"]
#! Generally an application will only use one backend, so you should only enable one of the following features:
## enables the [`CrosstermBackend`](backend::CrosstermBackend) backend and adds a dependency on [`crossterm`].
crossterm = ["dep:crossterm"]
## enables the [`TermionBackend`](backend::TermionBackend) backend and adds a dependency on [`termion`].
termion = ["dep:termion"]
## enables the [`TermwizBackend`](backend::TermwizBackend) backend and adds a dependency on [`termwiz`].
termwiz = ["dep:termwiz"]
#! The following optional features are available for all backends:
## enables serialization and deserialization of style and color types using the [`serde`] crate.
## This is useful if you want to save themes to a file.
serde = ["dep:serde", "bitflags/serde", "compact_str/serde"]
## enables the [`border!`] macro.
macros = []
## enables conversions from colors in the [`palette`] crate to [`Color`](crate::style::Color).
palette = ["dep:palette"]
## enables all widgets.
all-widgets = ["widget-calendar"]
#! Widgets that add dependencies are gated behind feature flags to prevent unused transitive
#! dependencies. The available features are:
## enables the [`calendar`](widgets::calendar) widget module and adds a dependency on [`time`].
widget-calendar = ["dep:time"]
#! The following optional features are only available for some backends:
## enables the backend code that sets the underline color.
## Underline color is only supported by the [`CrosstermBackend`](backend::CrosstermBackend) backend,
## and is not supported on Windows 7.
underline-color = ["dep:crossterm"]
#! The following features are unstable and may change in the future:
## Enable all unstable features.
unstable = [
"unstable-rendered-line-info",
"unstable-widget-ref",
"unstable-backend-writer",
]
## Enables the [`Paragraph::line_count`](widgets::Paragraph::line_count)
## [`Paragraph::line_width`](widgets::Paragraph::line_width) methods
## which are experimental and may change in the future.
## See [Issue 293](https://github.com/ratatui/ratatui/issues/293) for more details.
unstable-rendered-line-info = []
## Enables the [`WidgetRef`](widgets::WidgetRef) and [`StatefulWidgetRef`](widgets::StatefulWidgetRef) traits which are experimental and may change in
## the future.
unstable-widget-ref = []
## Enables getting access to backends' writers.
unstable-backend-writer = []
[package.metadata.docs.rs]
all-features = true
# see https://doc.rust-lang.org/nightly/rustdoc/scraped-examples.html
cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"]
rustdoc-args = ["--cfg", "docsrs"]
# Improve benchmark consistency
[profile.bench]
codegen-units = 1
lto = true
[lib]
bench = false
[[bench]]
name = "main"
harness = false
[[example]]
name = "async"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "barchart"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "barchart-grouped"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "block"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "calendar"
required-features = ["crossterm", "widget-calendar"]
doc-scrape-examples = true
[[example]]
name = "canvas"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "chart"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "colors"
required-features = ["crossterm"]
# this example is a bit verbose, so we don't want to include it in the docs
doc-scrape-examples = false
[[example]]
name = "colors_rgb"
required-features = ["crossterm", "palette"]
doc-scrape-examples = true
[[example]]
name = "constraint-explorer"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "constraints"
required-features = ["crossterm"]
doc-scrape-examples = false
[[example]]
name = "custom_widget"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "demo"
# this runs for all of the terminal backends, so it can't be built using --all-features or scraped
doc-scrape-examples = false
[[example]]
name = "demo2"
required-features = ["crossterm", "palette", "widget-calendar"]
doc-scrape-examples = true
[[example]]
name = "docsrs"
required-features = ["crossterm"]
doc-scrape-examples = false
[[example]]
name = "flex"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "gauge"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "hello_world"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "inline"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "layout"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "line_gauge"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "hyperlink"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "list"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "minimal"
required-features = ["crossterm"]
# prefer to show the more featureful examples in the docs
doc-scrape-examples = false
[[example]]
name = "modifiers"
required-features = ["crossterm"]
# this example is a bit verbose, so we don't want to include it in the docs
doc-scrape-examples = false
[[example]]
name = "panic"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "paragraph"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "popup"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "ratatui-logo"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "scrollbar"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "sparkline"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "table"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "tabs"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "tracing"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "user_input"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "widget_impl"
required-features = ["crossterm", "unstable-widget-ref"]
doc-scrape-examples = true
[[test]]
name = "state_serde"
required-features = ["serde"]

50
ratatui-core/Cargo.toml Normal file
View file

@ -0,0 +1,50 @@
[package]
name = "ratatui-core"
version = "0.1.0"
edition = "2021"
[features]
underline-color = []
unstable-widget-ref = []
[dependencies]
bitflags.workspace = true
cassowary.workspace = true
compact_str.workspace = true
document-features.workspace = true
instability.workspace = true
itertools.workspace = true
lru.workspace = true
paste.workspace = true
palette = { workspace = true, optional = true }
serde = { workspace = true, optional = true, features = ["derive"] }
strum = { workspace = true, features = ["derive"] }
termwiz = { workspace = true, optional = true }
unicode-segmentation.workspace = true
unicode-truncate.workspace = true
unicode-width.workspace = true
[dev-dependencies]
argh = "0.1.12"
color-eyre = "0.6.2"
criterion = { version = "0.5.1", features = ["html_reports"] }
crossterm = { version = "0.28.1", features = ["event-stream"] }
fakeit = "1.1"
font8x8 = "0.3.1"
futures = "0.3.30"
indoc = "2"
octocrab = "0.40.0"
pretty_assertions = "1.4.0"
rand = "0.8.5"
rand_chacha = "0.3.1"
rstest = "0.22.0"
serde_json = "1.0.109"
tokio = { version = "1.39.2", features = [
"rt",
"macros",
"time",
"rt-multi-thread",
] }
tracing = "0.1.40"
tracing-appender = "0.2.3"
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }

View file

@ -109,21 +109,6 @@ use crate::{
layout::{Position, Size}, layout::{Position, Size},
}; };
#[cfg(all(not(windows), feature = "termion"))]
mod termion;
#[cfg(all(not(windows), feature = "termion"))]
pub use self::termion::TermionBackend;
#[cfg(feature = "crossterm")]
mod crossterm;
#[cfg(feature = "crossterm")]
pub use self::crossterm::CrosstermBackend;
#[cfg(feature = "termwiz")]
mod termwiz;
#[cfg(feature = "termwiz")]
pub use self::termwiz::TermwizBackend;
mod test; mod test;
pub use self::test::TestBackend; pub use self::test::TestBackend;

View file

@ -1295,7 +1295,7 @@ mod tests {
use crate::{ use crate::{
layout::flex::Flex, layout::flex::Flex,
prelude::{Constraint::*, *}, prelude::{Constraint::*, *},
widgets::Paragraph, // widgets::Paragraph, // TODO
}; };
/// Test that the given constraints applied to the given area result in the expected layout. /// Test that the given constraints applied to the given area result in the expected layout.
@ -1317,7 +1317,7 @@ mod tests {
let mut buffer = Buffer::empty(area); let mut buffer = Buffer::empty(area);
for (c, &area) in ('a'..='z').take(constraints.len()).zip(layout.iter()) { for (c, &area) in ('a'..='z').take(constraints.len()).zip(layout.iter()) {
let s = c.to_string().repeat(area.width as usize); let s = c.to_string().repeat(area.width as usize);
Paragraph::new(s).render(area, &mut buffer); // Paragraph::new(s).render(area, &mut buffer); // TODO
} }
assert_eq!(buffer, Buffer::with_lines([expected])); assert_eq!(buffer, Buffer::with_lines([expected]));
} }

View file

@ -0,0 +1,41 @@
//! A prelude for conveniently writing applications using this library.
//!
//! ```rust,no_run
//! use ratatui::prelude::*;
//! ```
//!
//! Aside from the main types that are used in the library, this prelude also re-exports several
//! modules to make it easy to qualify types that would otherwise collide. E.g.:
//!
//! ```rust
//! use ratatui::{prelude::*, widgets::*};
//!
//! #[derive(Debug, Default, PartialEq, Eq)]
//! struct Line;
//!
//! assert_eq!(Line::default(), Line);
//! assert_eq!(text::Line::default(), ratatui::text::Line::from(vec![]));
//! ```
// TODO: re-export the following modules:
// #[cfg(feature = "crossterm")]
// pub use crate::backend::CrosstermBackend;
// #[cfg(all(not(windows), feature = "termion"))]
// pub use crate::backend::TermionBackend;
// #[cfg(feature = "termwiz")]
// pub use crate::backend::TermwizBackend;
pub(crate) use crate::widgets::{StatefulWidgetRef, WidgetRef};
pub use crate::{
backend::{self, Backend},
buffer::{self, Buffer},
layout::{self, Alignment, Constraint, Direction, Layout, Margin, Position, Rect, Size},
style::{self, Color, Modifier, Style, Stylize},
symbols::{self},
text::{self, Line, Masked, Span, Text},
widgets::{
// block::BlockExt, // TODO
StatefulWidget,
Widget,
},
Frame, Terminal,
};

View file

@ -32,15 +32,9 @@
//! [`Buffer`]: crate::buffer::Buffer //! [`Buffer`]: crate::buffer::Buffer
mod frame; mod frame;
#[cfg(feature = "crossterm")]
mod init;
mod terminal; mod terminal;
mod viewport; mod viewport;
pub use frame::{CompletedFrame, Frame}; pub use frame::{CompletedFrame, Frame};
#[cfg(feature = "crossterm")]
pub use init::{
init, init_with_options, restore, try_init, try_init_with_options, try_restore, DefaultTerminal,
};
pub use terminal::{Options as TerminalOptions, Terminal}; pub use terminal::{Options as TerminalOptions, Terminal};
pub use viewport::Viewport; pub use viewport::Viewport;

View file

@ -26,7 +26,7 @@ impl<'a> StyledGrapheme<'a> {
} }
} }
pub(crate) fn is_whitespace(&self) -> bool { pub fn is_whitespace(&self) -> bool {
let symbol = self.symbol; let symbol = self.symbol;
symbol == ZWSP || symbol.chars().all(char::is_whitespace) && symbol != NBSP symbol == ZWSP || symbol.chars().all(char::is_whitespace) && symbol != NBSP
} }

View file

@ -21,37 +21,7 @@
//! - [`Tabs`]: displays a tab bar and allows selection. //! - [`Tabs`]: displays a tab bar and allows selection.
//! //!
//! [`Canvas`]: crate::widgets::canvas::Canvas //! [`Canvas`]: crate::widgets::canvas::Canvas
mod barchart;
pub mod block;
mod borders;
#[cfg(feature = "widget-calendar")]
pub mod calendar;
pub mod canvas;
mod chart;
mod clear;
mod gauge;
mod list;
mod paragraph;
mod reflow;
mod scrollbar;
mod sparkline;
mod table;
mod tabs;
pub use self::{
barchart::{Bar, BarChart, BarGroup},
block::{Block, BorderType, Padding},
borders::*,
chart::{Axis, Chart, Dataset, GraphType, LegendPosition},
clear::Clear,
gauge::{Gauge, LineGauge},
list::{List, ListDirection, ListItem, ListState},
paragraph::{Paragraph, Wrap},
scrollbar::{ScrollDirection, Scrollbar, ScrollbarOrientation, ScrollbarState},
sparkline::{RenderDirection, Sparkline},
table::{Cell, HighlightSpacing, Row, Table, TableState},
tabs::Tabs,
};
use crate::{buffer::Buffer, layout::Rect, style::Style}; use crate::{buffer::Buffer, layout::Rect, style::Style};
/// A `Widget` is a type that can be drawn on a [`Buffer`] in a given [`Rect`]. /// A `Widget` is a type that can be drawn on a [`Buffer`] in a given [`Rect`].

View file

@ -0,0 +1,20 @@
[package]
name = "ratatui-crossterm"
version = "0.1.0"
edition = "2021"
[features]
default = ["underline-color"]
## enables the backend code that sets the underline color.
## Underline color is only supported by the [`CrosstermBackend`](backend::CrosstermBackend) backend,
## and is not supported on Windows 7.
underline-color = []
[dependencies]
ratatui-core = { workspace = true }
crossterm.workspace = true
instability.workspace = true
[dev-dependencies]
rstest.workspace = true

View file

@ -0,0 +1,681 @@
//! This module provides the [`CrosstermBackend`] implementation for the [`Backend`] trait. It uses
//! the [Crossterm] crate to interact with the terminal.
//!
//! [Crossterm]: https://crates.io/crates/crossterm
use std::io::{self, Write};
#[cfg(feature = "underline-color")]
use crossterm::style::SetUnderlineColor;
use crossterm::{
cursor::{Hide, MoveTo, Show},
execute, queue,
style::{
Attribute as CrosstermAttribute, Attributes as CrosstermAttributes,
Color as CrosstermColor, Colors, ContentStyle, Print, SetAttribute, SetBackgroundColor,
SetColors, SetForegroundColor,
},
terminal::{self, Clear},
};
use ratatui_core::{
backend::{Backend, ClearType, WindowSize},
buffer::Cell,
layout::{Position, Size},
style::{Color, Modifier, Style},
};
/// A [`Backend`] implementation that uses [Crossterm] to render to the terminal.
///
/// The `CrosstermBackend` struct is a wrapper around a writer implementing [`Write`], which is
/// used to send commands to the terminal. It provides methods for drawing content, manipulating
/// the cursor, and clearing the terminal screen.
///
/// Most applications should not call the methods on `CrosstermBackend` directly, but will instead
/// use the [`Terminal`] struct, which provides a more ergonomic interface.
///
/// Usually applications will enable raw mode and switch to alternate screen mode after creating
/// a `CrosstermBackend`. This is done by calling [`crossterm::terminal::enable_raw_mode`] and
/// [`crossterm::terminal::EnterAlternateScreen`] (and the corresponding disable/leave functions
/// when the application exits). This is not done automatically by the backend because it is
/// possible that the application may want to use the terminal for other purposes (like showing
/// help text) before entering alternate screen mode.
///
/// # Example
///
/// ```rust,no_run
/// use std::io::{stderr, stdout};
///
/// use ratatui::{
/// crossterm::{
/// terminal::{
/// disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen,
/// },
/// ExecutableCommand,
/// },
/// prelude::*,
/// };
///
/// let mut backend = CrosstermBackend::new(stdout());
/// // or
/// let backend = CrosstermBackend::new(stderr());
/// let mut terminal = Terminal::new(backend)?;
///
/// enable_raw_mode()?;
/// stdout().execute(EnterAlternateScreen)?;
///
/// terminal.clear()?;
/// terminal.draw(|frame| {
/// // -- snip --
/// })?;
///
/// stdout().execute(LeaveAlternateScreen)?;
/// disable_raw_mode()?;
///
/// # std::io::Result::Ok(())
/// ```
///
/// See the the [Examples] directory for more examples. See the [`backend`] module documentation
/// for more details on raw mode and alternate screen.
///
/// [`Write`]: std::io::Write
/// [`Terminal`]: crate::terminal::Terminal
/// [`backend`]: crate::backend
/// [Crossterm]: https://crates.io/crates/crossterm
/// [Examples]: https://github.com/ratatui/ratatui/tree/main/examples/README.md
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
pub struct CrosstermBackend<W: Write> {
/// The writer used to send commands to the terminal.
writer: W,
}
impl<W> CrosstermBackend<W>
where
W: Write,
{
/// Creates a new `CrosstermBackend` with the given writer.
///
/// Most applications will use either [`stdout`](std::io::stdout) or
/// [`stderr`](std::io::stderr) as writer. See the [FAQ] to determine which one to use.
///
/// [FAQ]: https://ratatui.rs/faq/#should-i-use-stdout-or-stderr
///
/// # Example
///
/// ```rust,no_run
/// # use std::io::stdout;
/// # use ratatui::prelude::*;
/// let backend = CrosstermBackend::new(stdout());
/// ```
pub const fn new(writer: W) -> Self {
Self { writer }
}
/// Gets the writer.
#[instability::unstable(
feature = "backend-writer",
issue = "https://github.com/ratatui/ratatui/pull/991"
)]
pub const fn writer(&self) -> &W {
&self.writer
}
/// Gets the writer as a mutable reference.
///
/// Note: writing to the writer may cause incorrect output after the write. This is due to the
/// way that the Terminal implements diffing Buffers.
#[instability::unstable(
feature = "backend-writer",
issue = "https://github.com/ratatui/ratatui/pull/991"
)]
pub fn writer_mut(&mut self) -> &mut W {
&mut self.writer
}
}
impl<W> Write for CrosstermBackend<W>
where
W: Write,
{
/// Writes a buffer of bytes to the underlying buffer.
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.writer.write(buf)
}
/// Flushes the underlying buffer.
fn flush(&mut self) -> io::Result<()> {
self.writer.flush()
}
}
impl<W> Backend for CrosstermBackend<W>
where
W: Write,
{
fn draw<'a, I>(&mut self, content: I) -> io::Result<()>
where
I: Iterator<Item = (u16, u16, &'a Cell)>,
{
let mut fg = Color::Reset;
let mut bg = Color::Reset;
#[cfg(feature = "underline-color")]
let mut underline_color = Color::Reset;
let mut modifier = Modifier::empty();
let mut last_pos: Option<Position> = None;
for (x, y, cell) in content {
// Move the cursor if the previous location was not (x - 1, y)
if !matches!(last_pos, Some(p) if x == p.x + 1 && y == p.y) {
queue!(self.writer, MoveTo(x, y))?;
}
last_pos = Some(Position { x, y });
if cell.modifier != modifier {
let diff = ModifierDiff {
from: modifier,
to: cell.modifier,
};
diff.queue(&mut self.writer)?;
modifier = cell.modifier;
}
if cell.fg != fg || cell.bg != bg {
queue!(
self.writer,
SetColors(Colors::new(
from_ratatui_color(cell.fg),
from_ratatui_color(cell.bg)
))
)?;
fg = cell.fg;
bg = cell.bg;
}
#[cfg(feature = "underline-color")]
if cell.underline_color != underline_color {
let color = from_ratatui_color(cell.underline_color);
queue!(self.writer, SetUnderlineColor(color))?;
underline_color = cell.underline_color;
}
queue!(self.writer, Print(cell.symbol()))?;
}
#[cfg(feature = "underline-color")]
return queue!(
self.writer,
SetForegroundColor(CrosstermColor::Reset),
SetBackgroundColor(CrosstermColor::Reset),
SetUnderlineColor(CrosstermColor::Reset),
SetAttribute(CrosstermAttribute::Reset),
);
#[cfg(not(feature = "underline-color"))]
return queue!(
self.writer,
SetForegroundColor(CrosstermColor::Reset),
SetBackgroundColor(CrosstermColor::Reset),
SetAttribute(CrosstermAttribute::Reset),
);
}
fn hide_cursor(&mut self) -> io::Result<()> {
execute!(self.writer, Hide)
}
fn show_cursor(&mut self) -> io::Result<()> {
execute!(self.writer, Show)
}
fn get_cursor_position(&mut self) -> io::Result<Position> {
crossterm::cursor::position()
.map(|(x, y)| Position { x, y })
.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))
}
fn set_cursor_position<P: Into<Position>>(&mut self, position: P) -> io::Result<()> {
let Position { x, y } = position.into();
execute!(self.writer, MoveTo(x, y))
}
fn clear(&mut self) -> io::Result<()> {
self.clear_region(ClearType::All)
}
fn clear_region(&mut self, clear_type: ClearType) -> io::Result<()> {
execute!(
self.writer,
Clear(match clear_type {
ClearType::All => crossterm::terminal::ClearType::All,
ClearType::AfterCursor => crossterm::terminal::ClearType::FromCursorDown,
ClearType::BeforeCursor => crossterm::terminal::ClearType::FromCursorUp,
ClearType::CurrentLine => crossterm::terminal::ClearType::CurrentLine,
ClearType::UntilNewLine => crossterm::terminal::ClearType::UntilNewLine,
})
)
}
fn append_lines(&mut self, n: u16) -> io::Result<()> {
for _ in 0..n {
queue!(self.writer, Print("\n"))?;
}
self.writer.flush()
}
fn size(&self) -> io::Result<Size> {
let (width, height) = terminal::size()?;
Ok(Size { width, height })
}
fn window_size(&mut self) -> io::Result<WindowSize> {
let crossterm::terminal::WindowSize {
columns,
rows,
width,
height,
} = terminal::window_size()?;
Ok(WindowSize {
columns_rows: Size {
width: columns,
height: rows,
},
pixels: Size { width, height },
})
}
fn flush(&mut self) -> io::Result<()> {
self.writer.flush()
}
}
fn from_ratatui_color(color: Color) -> CrosstermColor {
match color {
Color::Reset => CrosstermColor::Reset,
Color::Black => CrosstermColor::Black,
Color::Red => CrosstermColor::DarkRed,
Color::Green => CrosstermColor::DarkGreen,
Color::Yellow => CrosstermColor::DarkYellow,
Color::Blue => CrosstermColor::DarkBlue,
Color::Magenta => CrosstermColor::DarkMagenta,
Color::Cyan => CrosstermColor::DarkCyan,
Color::Gray => CrosstermColor::Grey,
Color::DarkGray => CrosstermColor::DarkGrey,
Color::LightRed => CrosstermColor::Red,
Color::LightGreen => CrosstermColor::Green,
Color::LightBlue => CrosstermColor::Blue,
Color::LightYellow => CrosstermColor::Yellow,
Color::LightMagenta => CrosstermColor::Magenta,
Color::LightCyan => CrosstermColor::Cyan,
Color::White => CrosstermColor::White,
Color::Indexed(i) => CrosstermColor::AnsiValue(i),
Color::Rgb(r, g, b) => CrosstermColor::Rgb { r, g, b },
}
}
fn from_crossterm_color(value: CrosstermColor) -> Color {
match value {
CrosstermColor::Reset => Color::Reset,
CrosstermColor::Black => Color::Black,
CrosstermColor::DarkRed => Color::Red,
CrosstermColor::DarkGreen => Color::Green,
CrosstermColor::DarkYellow => Color::Yellow,
CrosstermColor::DarkBlue => Color::Blue,
CrosstermColor::DarkMagenta => Color::Magenta,
CrosstermColor::DarkCyan => Color::Cyan,
CrosstermColor::Grey => Color::Gray,
CrosstermColor::DarkGrey => Color::DarkGray,
CrosstermColor::Red => Color::LightRed,
CrosstermColor::Green => Color::LightGreen,
CrosstermColor::Blue => Color::LightBlue,
CrosstermColor::Yellow => Color::LightYellow,
CrosstermColor::Magenta => Color::LightMagenta,
CrosstermColor::Cyan => Color::LightCyan,
CrosstermColor::White => Color::White,
CrosstermColor::Rgb { r, g, b } => Color::Rgb(r, g, b),
CrosstermColor::AnsiValue(v) => Color::Indexed(v),
}
}
/// The `ModifierDiff` struct is used to calculate the difference between two `Modifier`
/// values. This is useful when updating the terminal display, as it allows for more
/// efficient updates by only sending the necessary changes.
struct ModifierDiff {
pub from: Modifier,
pub to: Modifier,
}
impl ModifierDiff {
fn queue<W>(self, mut w: W) -> io::Result<()>
where
W: io::Write,
{
//use crossterm::Attribute;
let removed = self.from - self.to;
if removed.contains(Modifier::REVERSED) {
queue!(w, SetAttribute(CrosstermAttribute::NoReverse))?;
}
if removed.contains(Modifier::BOLD) {
queue!(w, SetAttribute(CrosstermAttribute::NormalIntensity))?;
if self.to.contains(Modifier::DIM) {
queue!(w, SetAttribute(CrosstermAttribute::Dim))?;
}
}
if removed.contains(Modifier::ITALIC) {
queue!(w, SetAttribute(CrosstermAttribute::NoItalic))?;
}
if removed.contains(Modifier::UNDERLINED) {
queue!(w, SetAttribute(CrosstermAttribute::NoUnderline))?;
}
if removed.contains(Modifier::DIM) {
queue!(w, SetAttribute(CrosstermAttribute::NormalIntensity))?;
}
if removed.contains(Modifier::CROSSED_OUT) {
queue!(w, SetAttribute(CrosstermAttribute::NotCrossedOut))?;
}
if removed.contains(Modifier::SLOW_BLINK) || removed.contains(Modifier::RAPID_BLINK) {
queue!(w, SetAttribute(CrosstermAttribute::NoBlink))?;
}
let added = self.to - self.from;
if added.contains(Modifier::REVERSED) {
queue!(w, SetAttribute(CrosstermAttribute::Reverse))?;
}
if added.contains(Modifier::BOLD) {
queue!(w, SetAttribute(CrosstermAttribute::Bold))?;
}
if added.contains(Modifier::ITALIC) {
queue!(w, SetAttribute(CrosstermAttribute::Italic))?;
}
if added.contains(Modifier::UNDERLINED) {
queue!(w, SetAttribute(CrosstermAttribute::Underlined))?;
}
if added.contains(Modifier::DIM) {
queue!(w, SetAttribute(CrosstermAttribute::Dim))?;
}
if added.contains(Modifier::CROSSED_OUT) {
queue!(w, SetAttribute(CrosstermAttribute::CrossedOut))?;
}
if added.contains(Modifier::SLOW_BLINK) {
queue!(w, SetAttribute(CrosstermAttribute::SlowBlink))?;
}
if added.contains(Modifier::RAPID_BLINK) {
queue!(w, SetAttribute(CrosstermAttribute::RapidBlink))?;
}
Ok(())
}
}
fn from_crossterm_attribute(value: CrosstermAttribute) -> Modifier {
// `Attribute*s*` (note the *s*) contains multiple `Attribute`
// We convert `Attribute` to `Attribute*s*` (containing only 1 value) to avoid implementing
// the conversion again
from_crossterm_attributes(CrosstermAttributes::from(value))
}
fn from_crossterm_attributes(value: CrosstermAttributes) -> Modifier {
let mut res = Modifier::empty();
if value.has(CrosstermAttribute::Bold) {
res |= Modifier::BOLD;
}
if value.has(CrosstermAttribute::Dim) {
res |= Modifier::DIM;
}
if value.has(CrosstermAttribute::Italic) {
res |= Modifier::ITALIC;
}
if value.has(CrosstermAttribute::Underlined)
|| value.has(CrosstermAttribute::DoubleUnderlined)
|| value.has(CrosstermAttribute::Undercurled)
|| value.has(CrosstermAttribute::Underdotted)
|| value.has(CrosstermAttribute::Underdashed)
{
res |= Modifier::UNDERLINED;
}
if value.has(CrosstermAttribute::SlowBlink) {
res |= Modifier::SLOW_BLINK;
}
if value.has(CrosstermAttribute::RapidBlink) {
res |= Modifier::RAPID_BLINK;
}
if value.has(CrosstermAttribute::Reverse) {
res |= Modifier::REVERSED;
}
if value.has(CrosstermAttribute::Hidden) {
res |= Modifier::HIDDEN;
}
if value.has(CrosstermAttribute::CrossedOut) {
res |= Modifier::CROSSED_OUT;
}
res
}
fn from_crossterm_style(value: ContentStyle) -> Style {
let mut sub_modifier = Modifier::empty();
if value.attributes.has(CrosstermAttribute::NoBold) {
sub_modifier |= Modifier::BOLD;
}
if value.attributes.has(CrosstermAttribute::NoItalic) {
sub_modifier |= Modifier::ITALIC;
}
if value.attributes.has(CrosstermAttribute::NotCrossedOut) {
sub_modifier |= Modifier::CROSSED_OUT;
}
if value.attributes.has(CrosstermAttribute::NoUnderline) {
sub_modifier |= Modifier::UNDERLINED;
}
if value.attributes.has(CrosstermAttribute::NoHidden) {
sub_modifier |= Modifier::HIDDEN;
}
if value.attributes.has(CrosstermAttribute::NoBlink) {
sub_modifier |= Modifier::RAPID_BLINK | Modifier::SLOW_BLINK;
}
if value.attributes.has(CrosstermAttribute::NoReverse) {
sub_modifier |= Modifier::REVERSED;
}
Style {
fg: value.foreground_color.map(from_crossterm_color),
bg: value.background_color.map(from_crossterm_color),
#[cfg(feature = "underline-color")]
underline_color: value.underline_color.map(from_crossterm_color),
add_modifier: from_crossterm_attributes(value.attributes),
sub_modifier,
}
}
#[cfg(test)]
mod tests {
use rstest::rstest;
use super::*;
#[rstest]
#[case(CrosstermColor::Reset, Color::Reset)]
#[case(CrosstermColor::Black, Color::Black)]
#[case(CrosstermColor::DarkGrey, Color::DarkGray)]
#[case(CrosstermColor::Red, Color::LightRed)]
#[case(CrosstermColor::DarkRed, Color::Red)]
#[case(CrosstermColor::Green, Color::LightGreen)]
#[case(CrosstermColor::DarkGreen, Color::Green)]
#[case(CrosstermColor::Yellow, Color::LightYellow)]
#[case(CrosstermColor::DarkYellow, Color::Yellow)]
#[case(CrosstermColor::Blue, Color::LightBlue)]
#[case(CrosstermColor::DarkBlue, Color::Blue)]
#[case(CrosstermColor::Magenta, Color::LightMagenta)]
#[case(CrosstermColor::DarkMagenta, Color::Magenta)]
#[case(CrosstermColor::Cyan, Color::LightCyan)]
#[case(CrosstermColor::DarkCyan, Color::Cyan)]
#[case(CrosstermColor::White, Color::White)]
#[case(CrosstermColor::Grey, Color::Gray)]
#[case(CrosstermColor::Rgb { r: 0, g: 0, b: 0 }, Color::Rgb(0, 0, 0))]
#[case(CrosstermColor::Rgb { r: 10, g: 20, b: 30 }, Color::Rgb(10, 20, 30))]
#[case(CrosstermColor::AnsiValue(32), Color::Indexed(32))]
#[case(CrosstermColor::AnsiValue(37), Color::Indexed(37))]
fn convert_from_crossterm_color(#[case] value: CrosstermColor, #[case] expected: Color) {
assert_eq!(from_crossterm_color(value), expected);
}
mod modifier {
use super::*;
#[rstest]
#[rstest]
#[case(CrosstermAttribute::Reset, Modifier::empty())]
#[case(CrosstermAttribute::Bold, Modifier::BOLD)]
#[case(CrosstermAttribute::Italic, Modifier::ITALIC)]
#[case(CrosstermAttribute::Underlined, Modifier::UNDERLINED)]
#[case(CrosstermAttribute::DoubleUnderlined, Modifier::UNDERLINED)]
#[case(CrosstermAttribute::Underdotted, Modifier::UNDERLINED)]
#[case(CrosstermAttribute::Dim, Modifier::DIM)]
#[case(CrosstermAttribute::NormalIntensity, Modifier::empty())]
#[case(CrosstermAttribute::CrossedOut, Modifier::CROSSED_OUT)]
#[case(CrosstermAttribute::NoUnderline, Modifier::empty())]
#[case(CrosstermAttribute::OverLined, Modifier::empty())]
#[case(CrosstermAttribute::SlowBlink, Modifier::SLOW_BLINK)]
#[case(CrosstermAttribute::RapidBlink, Modifier::RAPID_BLINK)]
#[case(CrosstermAttribute::Hidden, Modifier::HIDDEN)]
#[case(CrosstermAttribute::NoHidden, Modifier::empty())]
#[case(CrosstermAttribute::Reverse, Modifier::REVERSED)]
fn convert_from_crossterm_attribute(
#[case] value: CrosstermAttribute,
#[case] expected: Modifier,
) {
assert_eq!(from_crossterm_attribute(value), expected);
}
#[rstest]
#[case(&[CrosstermAttribute::Bold], Modifier::BOLD)]
#[case(
&[CrosstermAttribute::Bold, CrosstermAttribute::Italic],
Modifier::BOLD | Modifier::ITALIC
)]
#[case(
&[CrosstermAttribute::Bold, CrosstermAttribute::NotCrossedOut],
Modifier::BOLD
)]
#[case(
&[CrosstermAttribute::Dim, CrosstermAttribute::Underdotted],
Modifier::DIM | Modifier::UNDERLINED
)]
#[case(
&[CrosstermAttribute::Dim, CrosstermAttribute::SlowBlink, CrosstermAttribute::Italic],
Modifier::DIM | Modifier::SLOW_BLINK | Modifier::ITALIC
)]
#[case(
&[CrosstermAttribute::Hidden, CrosstermAttribute::NoUnderline, CrosstermAttribute::NotCrossedOut],
Modifier::HIDDEN
)]
#[case(
&[CrosstermAttribute::Reverse],
Modifier::REVERSED
)]
#[case(
&[CrosstermAttribute::Reset],
Modifier::empty()
)]
#[case(
&[CrosstermAttribute::RapidBlink, CrosstermAttribute::CrossedOut],
Modifier::RAPID_BLINK | Modifier::CROSSED_OUT
)]
#[case(
&[CrosstermAttribute::DoubleUnderlined, CrosstermAttribute::OverLined],
Modifier::UNDERLINED
)]
#[case(
&[CrosstermAttribute::Undercurled, CrosstermAttribute::Underdashed],
Modifier::UNDERLINED
)]
#[case(
&[CrosstermAttribute::NoBold, CrosstermAttribute::NoItalic],
Modifier::empty()
)]
#[case(
&[CrosstermAttribute::NoBlink, CrosstermAttribute::NoReverse],
Modifier::empty()
)]
fn convert_from_crossterm_attributes(
#[case] value: &[CrosstermAttribute],
#[case] expected: Modifier,
) {
assert_eq!(
from_crossterm_attributes(CrosstermAttributes::from(value)),
expected
);
}
}
#[rstest]
#[case(ContentStyle::default(), Style::default())]
#[case(
ContentStyle {
foreground_color: Some(CrosstermColor::DarkYellow),
..Default::default()
},
Style::default().fg(Color::Yellow)
)]
#[case(
ContentStyle {
background_color: Some(CrosstermColor::DarkYellow),
..Default::default()
},
Style::default().bg(Color::Yellow)
)]
#[case(
ContentStyle {
attributes: CrosstermAttributes::from(CrosstermAttribute::Bold),
..Default::default()
},
Style::default().add_modifier(Modifier::BOLD)
)]
#[case(
ContentStyle {
attributes: CrosstermAttributes::from(CrosstermAttribute::NoBold),
..Default::default()
},
Style::default().remove_modifier(Modifier::BOLD)
)]
#[case(
ContentStyle {
attributes: CrosstermAttributes::from(CrosstermAttribute::Italic),
..Default::default()
},
Style::default().add_modifier(Modifier::ITALIC)
)]
#[case(
ContentStyle {
attributes: CrosstermAttributes::from(CrosstermAttribute::NoItalic),
..Default::default()
},
Style::default().remove_modifier(Modifier::ITALIC)
)]
#[case(
ContentStyle {
attributes: CrosstermAttributes::from(
[CrosstermAttribute::Bold, CrosstermAttribute::Italic].as_ref()
),
..Default::default()
},
Style::default()
.add_modifier(Modifier::BOLD)
.add_modifier(Modifier::ITALIC)
)]
#[case(
ContentStyle {
attributes: CrosstermAttributes::from(
[CrosstermAttribute::NoBold, CrosstermAttribute::NoItalic].as_ref()
),
..Default::default()
},
Style::default()
.remove_modifier(Modifier::BOLD)
.remove_modifier(Modifier::ITALIC)
)]
#[cfg(feature = "underline-color")]
#[case(
ContentStyle {
underline_color: Some(CrosstermColor::DarkRed),
..Default::default()
},
Style::default().underline_color(Color::Red)
)]
fn convert_from_crossterm_content_style(#[case] value: ContentStyle, #[case] expected: Style) {
assert_eq!(from_crossterm_style(value), expected);
}
}

View file

@ -0,0 +1,9 @@
[package]
name = "ratatui-termion"
version = "0.1.0"
edition = "2021"
[dependencies]
instability = { workspace = true }
ratatui-core = { workspace = true }
termion.workspace = true

View file

@ -9,13 +9,13 @@ use std::{
io::{self, Write}, io::{self, Write},
}; };
use crate::{ use ratatui_core::{
backend::{Backend, ClearType, WindowSize}, backend::{Backend, ClearType, WindowSize},
buffer::Cell, buffer::Cell,
layout::{Position, Size}, layout::{Position, Size},
style::{Color, Modifier, Style}, style::{Color, Modifier, Style},
termion::{self, color as tcolor, color::Color as _, style as tstyle},
}; };
use termion::{color as tcolor, color::Color as _, style as tstyle};
/// A [`Backend`] implementation that uses [Termion] to render to the terminal. /// A [`Backend`] implementation that uses [Termion] to render to the terminal.
/// ///

View file

@ -0,0 +1,7 @@
[package]
name = "ratatui-termwiz"
version = "0.1.0"
edition = "2021"
[dependencies]
ratatui-core = { workspace = true }

View file

@ -0,0 +1,40 @@
[package]
name = "ratatui-widgets"
version = "0.1.0"
edition = "2021"
[features]
# TODO: remove unstable-widget-ref, consider whether to keep all-widgets
default = ["all-widgets", "unstable-widget-ref"]
## enables serialization and deserialization of style and color types using the [`serde`] crate.
## This is useful if you want to save themes to a file.
serde = ["dep:serde", "bitflags/serde"]
## enables all widgets.
all-widgets = ["widget-calendar"]
#! Widgets that add dependencies are gated behind feature flags to prevent unused transitive
#! dependencies. The available features are:
## enables the [`calendar`](widgets::calendar) widget module and adds a dependency on [`time`].
widget-calendar = ["dep:time"]
unstable-widget-ref = ["ratatui-core/unstable-widget-ref"]
[dependencies]
bitflags.workspace = true
instability.workspace = true
itertools.workspace = true
ratatui-core.workspace = true
serde = { workspace = true, optional = true }
strum.workspace = true
time = { workspace = true, optional = true }
unicode-segmentation.workspace = true
unicode-width.workspace = true
[dev-dependencies]
rstest.workspace = true
indoc.workspace = true
pretty_assertions.workspace = true
time.workspace = true

View file

@ -1,4 +1,8 @@
use crate::{prelude::*, style::Styled, widgets::Block}; use ratatui_core::{
prelude::{symbols, Buffer, Direction, Line, Rect, Style, Stylize, Widget},
style::Styled,
widgets::WidgetRef,
};
mod bar; mod bar;
mod bar_group; mod bar_group;
@ -6,6 +10,8 @@ mod bar_group;
pub use bar::Bar; pub use bar::Bar;
pub use bar_group::BarGroup; pub use bar_group::BarGroup;
use crate::{block::BlockExt, Block};
/// A chart showing values as [bars](Bar). /// A chart showing values as [bars](Bar).
/// ///
/// Here is a possible `BarChart` output. /// Here is a possible `BarChart` output.
@ -613,9 +619,14 @@ impl<'a> Styled for BarChart<'a> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use itertools::iproduct; use itertools::iproduct;
use ratatui_core::{
layout::Alignment,
style::{Color, Modifier},
text::Span,
};
use super::*; use super::*;
use crate::widgets::BorderType; use crate::BorderType;
#[test] #[test]
fn default() { fn default() {

View file

@ -1,8 +1,7 @@
use ratatui_core::prelude::{Buffer, Line, Rect, Style, Widget};
use unicode_width::UnicodeWidthStr; use unicode_width::UnicodeWidthStr;
use crate::prelude::*; /// A bar to be shown by the [`BarChart`](crate::BarChart) widget.
/// A bar to be shown by the [`BarChart`](crate::widgets::BarChart) widget.
/// ///
/// Here is an explanation of a `Bar`'s components. /// Here is an explanation of a `Bar`'s components.
/// ```plain /// ```plain
@ -61,7 +60,7 @@ impl<'a> Bar<'a> {
/// display the label **under** the bar. /// display the label **under** the bar.
/// For [`Horizontal`](crate::layout::Direction::Horizontal) bars, /// For [`Horizontal`](crate::layout::Direction::Horizontal) bars,
/// display the label **in** the bar. /// display the label **in** the bar.
/// See [`BarChart::direction`](crate::widgets::BarChart::direction) to set the direction. /// See [`BarChart::direction`](crate::BarChart::direction) to set the direction.
#[must_use = "method moves the value of self and returns the modified value"] #[must_use = "method moves the value of self and returns the modified value"]
pub fn label(mut self, label: Line<'a>) -> Self { pub fn label(mut self, label: Line<'a>) -> Self {
self.label = Some(label); self.label = Some(label);

View file

@ -1,5 +1,6 @@
use ratatui_core::prelude::*;
use super::Bar; use super::Bar;
use crate::prelude::*;
/// A group of bars to be shown by the Barchart. /// A group of bars to be shown by the Barchart.
/// ///

View file

@ -6,15 +6,23 @@
//! [title](Block::title) and [padding](Block::padding). //! [title](Block::title) and [padding](Block::padding).
use itertools::Itertools; use itertools::Itertools;
use ratatui_core::{
prelude::{Alignment, Buffer, Line, Rect, Style, Stylize, Widget},
style::Styled,
symbols::border,
widgets::WidgetRef,
};
use strum::{Display, EnumString}; use strum::{Display, EnumString};
use crate::{prelude::*, style::Styled, symbols::border, widgets::Borders}; use self::title::Position;
mod padding; mod padding;
pub mod title; pub mod title;
pub use padding::Padding; pub use padding::Padding;
pub use title::{Position, Title}; pub use title::Title;
use crate::Borders;
/// Base widget to be used to display a box border around all [upper level ones](crate::widgets). /// Base widget to be used to display a box border around all [upper level ones](crate::widgets).
/// ///
@ -495,7 +503,7 @@ impl<'a> Block<'a> {
/// .style(Style::new().white().not_bold()); // will be white, and italic /// .style(Style::new().white().not_bold()); // will be white, and italic
/// ``` /// ```
/// ///
/// [`Paragraph`]: crate::widgets::Paragraph /// [`Paragraph`]: crate::Paragraph
#[must_use = "method moves the value of self and returns the modified value"] #[must_use = "method moves the value of self and returns the modified value"]
pub fn style<S: Into<Style>>(mut self, style: S) -> Self { pub fn style<S: Into<Style>>(mut self, style: S) -> Self {
self.style = style.into(); self.style = style.into();
@ -986,6 +994,7 @@ impl<'a> Styled for Block<'a> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use ratatui_core::style::{Color, Modifier};
use rstest::rstest; use rstest::rstest;
use strum::ParseError; use strum::ParseError;
@ -1244,7 +1253,7 @@ mod tests {
// .border_style(_DEFAULT_STYLE) // no longer const // .border_style(_DEFAULT_STYLE) // no longer const
// .title_style(_DEFAULT_STYLE) // no longer const // .title_style(_DEFAULT_STYLE) // no longer const
.title_alignment(Alignment::Left) .title_alignment(Alignment::Left)
.title_position(Position::Top) .title_position(crate::block::Position::Top)
.padding(_DEFAULT_PADDING); .padding(_DEFAULT_PADDING);
} }

View file

@ -19,8 +19,8 @@
/// Padding::symmetric(5, 6); /// Padding::symmetric(5, 6);
/// ``` /// ```
/// ///
/// [`Block`]: crate::widgets::Block /// [`Block`]: crate::Block
/// [`padding`]: crate::widgets::Block::padding /// [`padding`]: crate::Block::padding
/// [CSS padding]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding /// [CSS padding]: https://developer.mozilla.org/en-US/docs/Web/CSS/padding
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] #[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Padding { pub struct Padding {

View file

@ -1,11 +1,10 @@
//! This module holds the [`Title`] element and its related configuration types. //! This module holds the [`Title`] element and its related configuration types.
//! A title is a piece of [`Block`](crate::widgets::Block) configuration. //! A title is a piece of [`Block`](crate::Block) configuration.
use ratatui_core::{layout::Alignment, text::Line};
use strum::{Display, EnumString}; use strum::{Display, EnumString};
use crate::{layout::Alignment, text::Line}; /// A [`Block`](crate::Block) title.
/// A [`Block`](crate::widgets::Block) title.
/// ///
/// It can be aligned (see [`Alignment`]) and positioned (see [`Position`]). /// It can be aligned (see [`Alignment`]) and positioned (see [`Position`]).
/// ///
@ -17,8 +16,8 @@ use crate::{layout::Alignment, text::Line};
/// <https://github.com/ratatui/ratatui/issues/738>. /// <https://github.com/ratatui/ratatui/issues/738>.
/// ///
/// Use [`Line`] instead, when the position is not defined as part of the title. When a specific /// Use [`Line`] instead, when the position is not defined as part of the title. When a specific
/// position is needed, use [`Block::title_top`](crate::widgets::Block::title_top) or /// position is needed, use [`Block::title_top`](crate::Block::title_top) or
/// [`Block::title_bottom`](crate::widgets::Block::title_bottom) instead. /// [`Block::title_bottom`](crate::Block::title_bottom) instead.
/// ///
/// # Example /// # Example
/// ///
@ -64,19 +63,19 @@ pub struct Title<'a> {
/// Title alignment /// Title alignment
/// ///
/// If [`None`], defaults to the alignment defined with /// If [`None`], defaults to the alignment defined with
/// [`Block::title_alignment`](crate::widgets::Block::title_alignment) in the associated /// [`Block::title_alignment`](crate::Block::title_alignment) in the associated
/// [`Block`](crate::widgets::Block). /// [`Block`](crate::Block).
pub alignment: Option<Alignment>, pub alignment: Option<Alignment>,
/// Title position /// Title position
/// ///
/// If [`None`], defaults to the position defined with /// If [`None`], defaults to the position defined with
/// [`Block::title_position`](crate::widgets::Block::title_position) in the associated /// [`Block::title_position`](crate::Block::title_position) in the associated
/// [`Block`](crate::widgets::Block). /// [`Block`](crate::Block).
pub position: Option<Position>, pub position: Option<Position>,
} }
/// Defines the [title](crate::widgets::block::Title) position. /// Defines the [title](crate::block::Title) position.
/// ///
/// The title can be positioned on top or at the bottom of the block. /// The title can be positioned on top or at the bottom of the block.
/// Defaults to [`Position::Top`]. /// Defaults to [`Position::Top`].

View file

@ -55,7 +55,7 @@ impl fmt::Debug for Borders {
/// and RIGHT. /// and RIGHT.
/// ///
/// When used with NONE you should consider omitting this completely. For ALL you should consider /// When used with NONE you should consider omitting this completely. For ALL you should consider
/// [`Block::bordered()`](crate::widgets::Block::bordered) instead. /// [`Block::bordered()`](crate::Block::bordered) instead.
/// ///
/// ## Examples /// ## Examples
/// ///

View file

@ -10,9 +10,13 @@
//! [`Monthly`] has several controls for what should be displayed //! [`Monthly`] has several controls for what should be displayed
use std::collections::HashMap; use std::collections::HashMap;
use ratatui_core::{
prelude::{Alignment, Buffer, Color, Constraint, Layout, Line, Rect, Span, Style, Widget},
widgets::WidgetRef,
};
use time::{Date, Duration, OffsetDateTime}; use time::{Date, Duration, OffsetDateTime};
use crate::{prelude::*, widgets::Block}; use crate::{block::BlockExt, Block};
/// Display a month calendar for the month containing `display_date` /// Display a month calendar for the month containing `display_date`
#[derive(Debug, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Clone, Eq, PartialEq, Hash)]

View file

@ -22,6 +22,12 @@ mod world;
use std::{fmt, iter::zip}; use std::{fmt, iter::zip};
use itertools::Itertools; use itertools::Itertools;
use ratatui_core::{
prelude::{Buffer, Color, Rect, Style, Widget},
symbols::{self, Marker},
text::Line as TextLine,
widgets::WidgetRef,
};
pub use self::{ pub use self::{
circle::Circle, circle::Circle,
@ -30,7 +36,7 @@ pub use self::{
points::Points, points::Points,
rectangle::Rectangle, rectangle::Rectangle,
}; };
use crate::{prelude::*, symbols::Marker, text::Line as TextLine, widgets::Block}; use crate::block::{Block, BlockExt};
/// Something that can be drawn on a [`Canvas`]. /// Something that can be drawn on a [`Canvas`].
/// ///
@ -425,7 +431,7 @@ impl<'a, 'b> From<&'a mut Context<'b>> for Painter<'a, 'b> {
/// This is used by the [`Canvas`] widget to draw shapes on the grid. It can be useful to think of /// This is used by the [`Canvas`] widget to draw shapes on the grid. It can be useful to think of
/// this as similar to the [`Frame`] struct that is used to draw widgets on the terminal. /// this as similar to the [`Frame`] struct that is used to draw widgets on the terminal.
/// ///
/// [`Frame`]: crate::prelude::Frame /// [`Frame`]: ratatui_core::prelude::Frame
#[derive(Debug)] #[derive(Debug)]
pub struct Context<'a> { pub struct Context<'a> {
x_bounds: [f64; 2], x_bounds: [f64; 2],
@ -809,9 +815,9 @@ where
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use indoc::indoc; use indoc::indoc;
use ratatui_core::buffer::Cell;
use super::*; use super::*;
use crate::buffer::Cell;
// helper to test the canvas checks that drawing a vertical and horizontal line // helper to test the canvas checks that drawing a vertical and horizontal line
// results in the expected output // results in the expected output

View file

@ -1,7 +1,6 @@
use crate::{ use ratatui_core::style::Color;
style::Color,
widgets::canvas::{Painter, Shape}, use crate::canvas::{Painter, Shape};
};
/// A circle with a given center and radius and with a given color /// A circle with a given center and radius and with a given color
#[derive(Debug, Default, Clone, PartialEq)] #[derive(Debug, Default, Clone, PartialEq)]
@ -31,17 +30,12 @@ impl Shape for Circle {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use ratatui_core::{
buffer::Buffer, buffer::Buffer, layout::Rect, style::Color, symbols::Marker, widgets::Widget,
layout::Rect,
style::Color,
symbols::Marker,
widgets::{
canvas::{Canvas, Circle},
Widget,
},
}; };
use crate::canvas::{Canvas, Circle};
#[test] #[test]
fn test_it_draws_a_circle() { fn test_it_draws_a_circle() {
let mut buffer = Buffer::empty(Rect::new(0, 0, 10, 5)); let mut buffer = Buffer::empty(Rect::new(0, 0, 10, 5));

View file

@ -1,7 +1,6 @@
use crate::{ use ratatui_core::style::Color;
style::Color,
widgets::canvas::{Painter, Shape}, use crate::canvas::{Painter, Shape};
};
/// A line from `(x1, y1)` to `(x2, y2)` with the given color /// A line from `(x1, y1)` to `(x2, y2)` with the given color
#[derive(Debug, Default, Clone, PartialEq)] #[derive(Debug, Default, Clone, PartialEq)]
@ -112,16 +111,18 @@ fn draw_line_high(painter: &mut Painter, x1: usize, y1: usize, x2: usize, y2: us
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use rstest::rstest; use ratatui_core::{
use super::*;
use crate::{
buffer::Buffer, buffer::Buffer,
layout::Rect, layout::Rect,
style::{Style, Stylize}, style::{Style, Stylize},
symbols::Marker, symbols::Marker,
widgets::{canvas::Canvas, Widget}, text,
widgets::Widget,
}; };
use rstest::rstest;
use super::*;
use crate::canvas::Canvas;
#[rstest] #[rstest]
#[case::off_grid(&Line::new(-1.0, -1.0, 10.0, 10.0, Color::Red), [" "; 10])] #[case::off_grid(&Line::new(-1.0, -1.0, 10.0, 10.0, Color::Red), [" "; 10])]
@ -207,7 +208,7 @@ mod tests {
fn tests<'expected_line, ExpectedLines>(#[case] line: &Line, #[case] expected: ExpectedLines) fn tests<'expected_line, ExpectedLines>(#[case] line: &Line, #[case] expected: ExpectedLines)
where where
ExpectedLines: IntoIterator, ExpectedLines: IntoIterator,
ExpectedLines::Item: Into<crate::text::Line<'expected_line>>, ExpectedLines::Item: Into<text::Line<'expected_line>>,
{ {
let mut buffer = Buffer::empty(Rect::new(0, 0, 10, 10)); let mut buffer = Buffer::empty(Rect::new(0, 0, 10, 10));
let canvas = Canvas::default() let canvas = Canvas::default()

View file

@ -1,13 +1,10 @@
use ratatui_core::style::Color;
use strum::{Display, EnumString}; use strum::{Display, EnumString};
use crate::{ use crate::canvas::{
style::Color,
widgets::canvas::{
world::{WORLD_HIGH_RESOLUTION, WORLD_LOW_RESOLUTION}, world::{WORLD_HIGH_RESOLUTION, WORLD_LOW_RESOLUTION},
Painter, Shape, Painter, Shape,
},
}; };
/// Defines how many points are going to be used to draw a [`Map`]. /// Defines how many points are going to be used to draw a [`Map`].
/// ///
/// You generally want a [high](MapResolution::High) resolution map. /// You generally want a [high](MapResolution::High) resolution map.
@ -62,10 +59,14 @@ impl Shape for Map {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use ratatui_core::{
prelude::{Buffer, Color, Rect, Widget},
symbols::Marker,
};
use strum::ParseError; use strum::ParseError;
use super::*; use super::*;
use crate::{prelude::*, symbols::Marker, widgets::canvas::Canvas}; use crate::canvas::Canvas;
#[test] #[test]
fn map_resolution_to_string() { fn map_resolution_to_string() {

View file

@ -1,7 +1,6 @@
use crate::{ use ratatui_core::style::Color;
style::Color,
widgets::canvas::{Painter, Shape}, use crate::canvas::{Painter, Shape};
};
/// A group of points with a given color /// A group of points with a given color
#[derive(Debug, Default, Clone, PartialEq)] #[derive(Debug, Default, Clone, PartialEq)]

View file

@ -1,7 +1,6 @@
use crate::{ use ratatui_core::style::Color;
style::Color,
widgets::canvas::{Line, Painter, Shape}, use crate::canvas::{Line, Painter, Shape};
};
/// A rectangle to draw on a [`Canvas`](super::Canvas) /// A rectangle to draw on a [`Canvas`](super::Canvas)
/// ///
@ -65,8 +64,10 @@ impl Shape for Rectangle {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use ratatui_core::{prelude::*, symbols::Marker};
use super::*; use super::*;
use crate::{prelude::*, symbols::Marker, widgets::canvas::Canvas}; use crate::canvas::Canvas;
#[test] #[test]
fn draw_block_lines() { fn draw_block_lines() {

View file

@ -1,17 +1,13 @@
use std::{cmp::max, ops::Not}; use std::{cmp::max, ops::Not};
use ratatui_core::{layout::Flex, prelude::*, style::Styled, widgets::WidgetRef};
use strum::{Display, EnumString}; use strum::{Display, EnumString};
use crate::{ use crate::{
layout::Flex, block::BlockExt,
prelude::*,
style::Styled,
widgets::{
canvas::{Canvas, Line as CanvasLine, Points}, canvas::{Canvas, Line as CanvasLine, Points},
Block, Block,
},
}; };
/// An X or Y axis for the [`Chart`] widget /// An X or Y axis for the [`Chart`] widget
/// ///
/// An axis can have a [title](Axis::title) which will be displayed at the end of the axis. For an /// An axis can have a [title](Axis::title) which will be displayed at the end of the axis. For an
@ -1074,7 +1070,7 @@ impl WidgetRef for Chart<'_> {
for (i, (dataset_name, dataset_style)) in self for (i, (dataset_name, dataset_style)) in self
.datasets .datasets
.iter() .iter()
.filter_map(|ds| Some((ds.name.as_ref()?, ds.style()))) .filter_map(|ds| Some((ds.name.as_ref()?, ds.style)))
.enumerate() .enumerate()
{ {
let name = dataset_name.clone().patch_style(dataset_style); let name = dataset_name.clone().patch_style(dataset_style);

View file

@ -1,4 +1,4 @@
use crate::prelude::*; use ratatui_core::{prelude::*, widgets::WidgetRef};
/// A widget to clear/reset a certain area to allow overdrawing (e.g. for popups). /// A widget to clear/reset a certain area to allow overdrawing (e.g. for popups).
/// ///

View file

@ -1,4 +1,6 @@
use crate::{prelude::*, style::Styled, widgets::Block}; use ratatui_core::{prelude::*, style::Styled, widgets::WidgetRef};
use crate::{block::BlockExt, Block};
/// A widget to display a progress bar. /// A widget to display a progress bar.
/// ///

View file

@ -0,0 +1,31 @@
mod barchart;
pub mod block;
mod borders;
#[cfg(feature = "widget-calendar")]
pub mod calendar;
pub mod canvas;
mod chart;
mod clear;
mod gauge;
mod list;
mod paragraph;
mod reflow;
mod scrollbar;
mod sparkline;
mod table;
mod tabs;
pub use self::{
barchart::{Bar, BarChart, BarGroup},
block::{Block, BorderType, Padding},
borders::*,
chart::{Axis, Chart, Dataset, GraphType, LegendPosition},
clear::Clear,
gauge::{Gauge, LineGauge},
list::{List, ListDirection, ListItem, ListState},
paragraph::{Paragraph, Wrap},
scrollbar::{ScrollDirection, Scrollbar, ScrollbarOrientation, ScrollbarState},
sparkline::{RenderDirection, Sparkline},
table::{Cell, HighlightSpacing, Row, Table, TableState},
tabs::Tabs,
};

View file

@ -1,4 +1,4 @@
use crate::prelude::*; use ratatui_core::prelude::*;
/// A single item in a [`List`] /// A single item in a [`List`]
/// ///
@ -55,7 +55,7 @@ use crate::prelude::*;
/// ListItem::new(Text::from("foo").alignment(Alignment::Right)); /// ListItem::new(Text::from("foo").alignment(Alignment::Right));
/// ``` /// ```
/// ///
/// [`List`]: crate::widgets::List /// [`List`]: crate::List
/// [`Stylize`]: crate::style::Stylize /// [`Stylize`]: crate::style::Stylize
#[derive(Debug, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct ListItem<'a> { pub struct ListItem<'a> {
@ -94,8 +94,8 @@ impl<'a> ListItem<'a> {
/// ///
/// # See also /// # See also
/// ///
/// - [`List::new`](crate::widgets::List::new) to create a list of items that can be converted /// - [`List::new`](crate::List::new) to create a list of items that can be converted to
/// to [`ListItem`] /// [`ListItem`]
pub fn new<T>(content: T) -> Self pub fn new<T>(content: T) -> Self
where where
T: Into<Text<'a>>, T: Into<Text<'a>>,
@ -132,7 +132,7 @@ impl<'a> ListItem<'a> {
/// ``` /// ```
/// ///
/// [`Styled`]: crate::style::Styled /// [`Styled`]: crate::style::Styled
/// [`ListState`]: crate::widgets::list::ListState /// [`ListState`]: crate::list::ListState
#[must_use = "method moves the value of self and returns the modified value"] #[must_use = "method moves the value of self and returns the modified value"]
pub fn style<S: Into<Style>>(mut self, style: S) -> Self { pub fn style<S: Into<Style>>(mut self, style: S) -> Self {
self.style = style.into(); self.style = style.into();

View file

@ -1,11 +1,8 @@
use ratatui_core::{prelude::Style, style::Styled};
use strum::{Display, EnumString}; use strum::{Display, EnumString};
use super::ListItem; use super::ListItem;
use crate::{ use crate::{Block, HighlightSpacing};
prelude::*,
style::Styled,
widgets::{Block, HighlightSpacing},
};
/// A widget to display several items among which one can be selected (optional) /// A widget to display several items among which one can be selected (optional)
/// ///
@ -15,7 +12,7 @@ use crate::{
/// the item's height is automatically determined. A `List` can also be put in reverse order (i.e. /// the item's height is automatically determined. A `List` can also be put in reverse order (i.e.
/// *bottom to top*) whereas a [`Table`] cannot. /// *bottom to top*) whereas a [`Table`] cannot.
/// ///
/// [`Table`]: crate::widgets::Table /// [`Table`]: crate::Table
/// ///
/// List items can be aligned using [`Text::alignment`], for more details see [`ListItem`]. /// List items can be aligned using [`Text::alignment`], for more details see [`ListItem`].
/// ///
@ -85,9 +82,9 @@ use crate::{
/// (0..5).map(|i| format!("Item{i}")).collect::<List>(); /// (0..5).map(|i| format!("Item{i}")).collect::<List>();
/// ``` /// ```
/// ///
/// [`ListState`]: crate::widgets::list::ListState /// [`ListState`]: crate::list::ListState
/// [scroll]: crate::widgets::list::ListState::offset /// [scroll]: crate::list::ListState::offset
/// [select]: crate::widgets::list::ListState::select /// [select]: crate::list::ListState::select
#[derive(Debug, Clone, Eq, PartialEq, Hash, Default)] #[derive(Debug, Clone, Eq, PartialEq, Hash, Default)]
pub struct List<'a> { pub struct List<'a> {
/// An optional block to wrap the widget in /// An optional block to wrap the widget in
@ -421,6 +418,7 @@ where
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
use ratatui_core::style::{Color, Modifier, Stylize};
use super::*; use super::*;

View file

@ -1,9 +1,10 @@
use ratatui_core::{
prelude::{Buffer, Rect, StatefulWidget, Widget},
widgets::{StatefulWidgetRef, WidgetRef},
};
use unicode_width::UnicodeWidthStr; use unicode_width::UnicodeWidthStr;
use crate::{ use crate::{block::BlockExt, List, ListDirection, ListState};
prelude::{Buffer, Rect, StatefulWidget, StatefulWidgetRef, Widget, WidgetRef},
widgets::{block::BlockExt, List, ListDirection, ListState},
};
impl Widget for List<'_> { impl Widget for List<'_> {
fn render(self, area: Rect, buf: &mut Buffer) { fn render(self, area: Rect, buf: &mut Buffer) {
@ -269,13 +270,11 @@ impl List<'_> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
use ratatui_core::prelude::*;
use rstest::{fixture, rstest}; use rstest::{fixture, rstest};
use super::*; use super::*;
use crate::{ use crate::{Block, HighlightSpacing, ListItem};
prelude::*,
widgets::{Block, HighlightSpacing, ListItem},
};
#[fixture] #[fixture]
fn single_line_buf() -> Buffer { fn single_line_buf() -> Buffer {

View file

@ -36,7 +36,7 @@
/// # } /// # }
/// ``` /// ```
/// ///
/// [`List`]: crate::widgets::List /// [`List`]: crate::List
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ListState { pub struct ListState {
@ -258,7 +258,7 @@ impl ListState {
mod tests { mod tests {
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
use crate::widgets::ListState; use crate::ListState;
#[test] #[test]
fn selected() { fn selected() {

View file

@ -1,13 +1,10 @@
use ratatui_core::{prelude::*, style::Styled, text::StyledGrapheme, widgets::WidgetRef};
use unicode_width::UnicodeWidthStr; use unicode_width::UnicodeWidthStr;
use crate::{ use crate::{
prelude::*, block::BlockExt,
style::Styled,
text::StyledGrapheme,
widgets::{
reflow::{LineComposer, LineTruncator, WordWrapper, WrappedLine}, reflow::{LineComposer, LineTruncator, WordWrapper, WrappedLine},
Block, Block,
},
}; };
const fn get_line_offset(line_width: u16, text_area_width: u16, alignment: Alignment) -> u16 { const fn get_line_offset(line_width: u16, text_area_width: u16, alignment: Alignment) -> u16 {
@ -470,11 +467,10 @@ impl<'a> Styled for Paragraph<'a> {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use ratatui_core::backend::TestBackend;
use super::*; use super::*;
use crate::{ use crate::{block::title::Position, Borders};
backend::TestBackend,
widgets::{block::Position, Borders},
};
/// Tests the [`Paragraph`] widget against the expected [`Buffer`] by rendering it onto an equal /// Tests the [`Paragraph`] widget against the expected [`Buffer`] by rendering it onto an equal
/// area and comparing the rendered and expected content. /// area and comparing the rendered and expected content.

View file

@ -1,10 +1,9 @@
use std::{collections::VecDeque, mem}; use std::{collections::VecDeque, mem};
use ratatui_core::{layout::Alignment, text::StyledGrapheme};
use unicode_segmentation::UnicodeSegmentation; use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthStr; use unicode_width::UnicodeWidthStr;
use crate::{layout::Alignment, text::StyledGrapheme};
/// A state machine to pack styled symbols into lines. /// A state machine to pack styled symbols into lines.
/// Cannot implement it as Iterator since it yields slices of the internal buffer (need streaming /// Cannot implement it as Iterator since it yields slices of the internal buffer (need streaming
/// iterators for that). /// iterators for that).
@ -344,12 +343,13 @@ fn trim_offset(src: &str, mut offset: usize) -> &str {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use ratatui_core::{
use crate::{
style::Style, style::Style,
text::{Line, Text}, text::{Line, Text},
}; };
use super::*;
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
enum Composer { enum Composer {
WordWrapper { trim: bool }, WordWrapper { trim: bool },

View file

@ -8,13 +8,12 @@
use std::iter; use std::iter;
use strum::{Display, EnumString}; use ratatui_core::{
use unicode_width::UnicodeWidthStr;
use crate::{
prelude::*, prelude::*,
symbols::scrollbar::{Set, DOUBLE_HORIZONTAL, DOUBLE_VERTICAL}, symbols::scrollbar::{Set, DOUBLE_HORIZONTAL, DOUBLE_VERTICAL},
}; };
use strum::{Display, EnumString};
use unicode_width::UnicodeWidthStr;
/// A widget to display a scrollbar /// A widget to display a scrollbar
/// ///

View file

@ -1,8 +1,9 @@
use std::cmp::min; use std::cmp::min;
use ratatui_core::{prelude::*, style::Styled, widgets::WidgetRef};
use strum::{Display, EnumString}; use strum::{Display, EnumString};
use crate::{prelude::*, style::Styled, widgets::Block}; use crate::{block::BlockExt, Block};
/// Widget to render a sparkline over one or more lines. /// Widget to render a sparkline over one or more lines.
/// ///
@ -208,10 +209,10 @@ impl Sparkline<'_> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use ratatui_core::buffer::Cell;
use strum::ParseError; use strum::ParseError;
use super::*; use super::*;
use crate::buffer::Cell;
#[test] #[test]
fn render_direction_to_string() { fn render_direction_to_string() {

View file

@ -1,4 +1,4 @@
use crate::{prelude::*, style::Styled}; use ratatui_core::{prelude::*, style::Styled, widgets::WidgetRef};
/// A [`Cell`] contains the [`Text`] to be displayed in a [`Row`] of a [`Table`]. /// A [`Cell`] contains the [`Text`] to be displayed in a [`Row`] of a [`Table`].
/// ///

View file

@ -1,5 +1,6 @@
use ratatui_core::{prelude::*, style::Styled};
use super::Cell; use super::Cell;
use crate::{prelude::*, style::Styled};
/// A single row of data to be displayed in a [`Table`] widget. /// A single row of data to be displayed in a [`Table`] widget.
/// ///

View file

@ -1,9 +1,15 @@
use itertools::Itertools; use itertools::Itertools;
use ratatui_core::{
layout::Flex,
prelude::*,
style::Styled,
widgets::{StatefulWidgetRef, WidgetRef},
};
#[allow(unused_imports)] // `Cell` is used in the doc comment but not the code #[allow(unused_imports)] // `Cell` is used in the doc comment but not the code
use super::Cell; use super::Cell;
use super::{HighlightSpacing, Row, TableState}; use super::{HighlightSpacing, Row, TableState};
use crate::{layout::Flex, prelude::*, style::Styled, widgets::Block}; use crate::{block::BlockExt, Block};
/// A widget to display data in formatted columns. /// A widget to display data in formatted columns.
/// ///
@ -867,8 +873,10 @@ where
mod tests { mod tests {
use std::vec; use std::vec;
use ratatui_core::{layout::Constraint::*, style::Style, text::Line};
use super::*; use super::*;
use crate::{layout::Constraint::*, style::Style, text::Line, widgets::Cell}; use crate::table::Cell;
#[test] #[test]
fn new() { fn new() {
@ -1031,14 +1039,15 @@ mod tests {
#[cfg(test)] #[cfg(test)]
mod state { mod state {
use ratatui_core::{
buffer::Buffer,
layout::{Constraint, Rect},
widgets::StatefulWidget,
};
use rstest::{fixture, rstest}; use rstest::{fixture, rstest};
use super::TableState; use super::TableState;
use crate::{ use crate::{Row, Table};
buffer::Buffer,
layout::{Constraint, Rect},
widgets::{Row, StatefulWidget, Table},
};
#[fixture] #[fixture]
fn table_buf() -> Buffer { fn table_buf() -> Buffer {

View file

@ -41,8 +41,8 @@
/// Note that if [`Table::widths`] is not called before rendering, the rendered columns will have /// Note that if [`Table::widths`] is not called before rendering, the rendered columns will have
/// equal width. /// equal width.
/// ///
/// [`Table`]: crate::widgets::Table /// [`Table`]: crate::Table
/// [`Table::widths`]: crate::widgets::Table::widths /// [`Table::widths`]: crate::Table::widths
/// [`Frame::render_stateful_widget`]: crate::Frame::render_stateful_widget /// [`Frame::render_stateful_widget`]: crate::Frame::render_stateful_widget
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]

View file

@ -1,4 +1,6 @@
use crate::{prelude::*, style::Styled, widgets::Block}; use ratatui_core::{prelude::*, style::Styled, widgets::WidgetRef};
use crate::{block::BlockExt, Block};
const DEFAULT_HIGHLIGHT_STYLE: Style = Style::new().add_modifier(Modifier::REVERSED); const DEFAULT_HIGHLIGHT_STYLE: Style = Style::new().add_modifier(Modifier::REVERSED);

386
ratatui/Cargo.toml Normal file
View file

@ -0,0 +1,386 @@
[package]
name = "ratatui"
version = "0.28.1" # crate version
authors = ["Florian Dehau <work@fdehau.com>", "The Ratatui Developers"]
description = "A library that's all about cooking up terminal user interfaces"
documentation = "https://docs.rs/ratatui/latest/ratatui/"
repository = "https://github.com/ratatui/ratatui"
homepage = "https://ratatui.rs"
keywords = ["tui", "terminal", "dashboard"]
categories = ["command-line-interface"]
readme = "README.md"
license = "MIT"
exclude = [
"assets/*",
".github",
"Makefile.toml",
"CONTRIBUTING.md",
"*.log",
"tags",
]
edition = "2021"
rust-version = "1.74.0"
[features]
#! The crate provides a set of optional features that can be enabled in your `cargo.toml` file.
#!
## By default, we enable the crossterm backend as this is a reasonable choice for most applications
## as it is supported on Linux/Mac/Windows systems. We also enable the `underline-color` feature
## which allows you to set the underline color of text.
default = ["crossterm", "underline-color"]
#! Generally an application will only use one backend, so you should only enable one of the following features:
## enables the [`CrosstermBackend`](backend::CrosstermBackend) backend and adds a dependency on [`crossterm`].
crossterm = ["dep:ratatui-crossterm", "dep:crossterm"]
## enables the [`TermionBackend`](backend::TermionBackend) backend and adds a dependency on [`termion`].
# termion = ["dep:ratatui-termion", "dep:termion"]
# ## enables the [`TermwizBackend`](backend::TermwizBackend) backend and adds a dependency on [`termwiz`].
# termwiz = ["dep:ratatui-termwiz"]
#! The following optional features are available for all backends:
## enables serialization and deserialization of style and color types using the [`serde`] crate.
## This is useful if you want to save themes to a file.
serde = ["dep:serde", "bitflags/serde", "compact_str/serde"]
## enables the [`border!`] macro.
macros = []
## enables conversions from colors in the [`palette`] crate to [`Color`](crate::style::Color).
palette = ["dep:palette"]
## enables all widgets.
all-widgets = ["widget-calendar"]
#! Widgets that add dependencies are gated behind feature flags to prevent unused transitive
#! dependencies. The available features are:
## enables the [`calendar`](widgets::calendar) widget module and adds a dependency on [`time`].
widget-calendar = ["ratatui-widgets/widget-calendar", "dep:time"]
#! The following optional features are only available for some backends:
## enables the backend code that sets the underline color.
## Underline color is only supported by the [`CrosstermBackend`](backend::CrosstermBackend) backend,
## and is not supported on Windows 7.
# underline-color = ["ratatui-crossterm/underline-color"]
underline-color = ["ratatui-core/underline-color"]
#! The following features are unstable and may change in the future:
## Enable all unstable features.
unstable = [
"unstable-rendered-line-info",
"unstable-widget-ref",
"unstable-backend-writer",
]
## Enables the [`Paragraph::line_count`](widgets::Paragraph::line_count)
## [`Paragraph::line_width`](widgets::Paragraph::line_width) methods
## which are experimental and may change in the future.
## See [Issue 293](https://github.com/ratatui/ratatui/issues/293) for more details.
unstable-rendered-line-info = []
## Enables the [`WidgetRef`](widgets::WidgetRef) and [`StatefulWidgetRef`](widgets::StatefulWidgetRef) traits which are experimental and may change in
## the future.
unstable-widget-ref = []
## Enables getting access to backends' writers.
unstable-backend-writer = []
[dependencies]
ratatui-widgets = { workspace = true }
ratatui-core = { workspace = true }
ratatui-crossterm = { workspace = true, optional = true }
# ratatui-termwiz = { workspace = true, optional = true }
bitflags = "2.3"
cassowary = "0.3"
crossterm = { workspace = true, optional = true }
compact_str = "0.8.0"
document-features = { version = "0.2.7", optional = true }
instability = "0.3.1"
itertools = "0.13"
lru = "0.12.0"
paste = "1.0.2"
palette = { version = "0.7.6", optional = true }
serde = { version = "1", optional = true, features = ["derive"] }
strum = { version = "0.26.3", features = ["derive"] }
unicode-segmentation = "1.10"
unicode-truncate = "1"
unicode-width = "=0.1.13"
time = { workspace = true, optional = true }
# [target.'cfg(not(windows))'.dependencies]
# # termion is not supported on Windows
# ratatui-termion = { workspace = true, optional = true }
# termion = { workspace = true, optional = true }
[dev-dependencies]
argh = "0.1.12"
color-eyre = "0.6.2"
criterion = { version = "0.5.1", features = ["html_reports"] }
fakeit = "1.1"
font8x8 = "0.3.1"
futures = "0.3.30"
indoc = "2"
octocrab = "0.40.0"
pretty_assertions = "1.4.0"
rand = "0.8.5"
rand_chacha = "0.3.1"
rstest = "0.22.0"
serde_json = "1.0.109"
tokio = { version = "1.39.2", features = [
"rt",
"macros",
"time",
"rt-multi-thread",
] }
tracing = "0.1.40"
tracing-appender = "0.2.3"
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
[lints.rust]
unsafe_code = "forbid"
[lints.clippy]
cargo = { level = "warn", priority = -1 }
pedantic = { level = "warn", priority = -1 }
cast_possible_truncation = "allow"
cast_possible_wrap = "allow"
cast_precision_loss = "allow"
cast_sign_loss = "allow"
missing_errors_doc = "allow"
missing_panics_doc = "allow"
module_name_repetitions = "allow"
must_use_candidate = "allow"
# we often split up a module into multiple files with the main type in a file named after the
# module, so we want to allow this pattern
module_inception = "allow"
# nursery or restricted
as_underscore = "warn"
deref_by_slicing = "warn"
else_if_without_else = "warn"
empty_line_after_doc_comments = "warn"
equatable_if_let = "warn"
fn_to_numeric_cast_any = "warn"
format_push_string = "warn"
map_err_ignore = "warn"
missing_const_for_fn = "warn"
mixed_read_write_in_expression = "warn"
mod_module_files = "warn"
needless_pass_by_ref_mut = "warn"
needless_raw_strings = "warn"
or_fun_call = "warn"
redundant_type_annotations = "warn"
rest_pat_in_fully_bound_structs = "warn"
string_lit_chars_any = "warn"
string_slice = "warn"
string_to_string = "warn"
unnecessary_self_imports = "warn"
use_self = "warn"
[package.metadata.docs.rs]
all-features = true
# see https://doc.rust-lang.org/nightly/rustdoc/scraped-examples.html
cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"]
rustdoc-args = ["--cfg", "docsrs"]
# Improve benchmark consistency
[profile.bench]
codegen-units = 1
lto = true
[lib]
bench = false
[[bench]]
name = "main"
harness = false
[[example]]
name = "async"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "barchart"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "barchart-grouped"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "block"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "calendar"
required-features = ["crossterm", "widget-calendar"]
doc-scrape-examples = true
[[example]]
name = "canvas"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "chart"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "colors"
required-features = ["crossterm"]
# this example is a bit verbose, so we don't want to include it in the docs
doc-scrape-examples = false
[[example]]
name = "colors_rgb"
required-features = ["crossterm", "palette"]
doc-scrape-examples = true
[[example]]
name = "constraint-explorer"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "constraints"
required-features = ["crossterm"]
doc-scrape-examples = false
[[example]]
name = "custom_widget"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "demo"
# this runs for all of the terminal backends, so it can't be built using --all-features or scraped
doc-scrape-examples = false
[[example]]
name = "demo2"
required-features = ["crossterm", "palette", "widget-calendar"]
doc-scrape-examples = true
[[example]]
name = "docsrs"
required-features = ["crossterm"]
doc-scrape-examples = false
[[example]]
name = "flex"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "gauge"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "hello_world"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "inline"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "layout"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "line_gauge"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "hyperlink"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "list"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "minimal"
required-features = ["crossterm"]
# prefer to show the more featureful examples in the docs
doc-scrape-examples = false
[[example]]
name = "modifiers"
required-features = ["crossterm"]
# this example is a bit verbose, so we don't want to include it in the docs
doc-scrape-examples = false
[[example]]
name = "panic"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "paragraph"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "popup"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "ratatui-logo"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "scrollbar"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "sparkline"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "table"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "tabs"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "tracing"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "user_input"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]]
name = "widget_impl"
required-features = ["crossterm", "unstable-widget-ref"]
doc-scrape-examples = true
[[test]]
name = "state_serde"
required-features = ["serde"]

Some files were not shown because too many files have changed in this diff Show more