mirror of
https://github.com/ratatui-org/ratatui
synced 2024-11-25 06:00:34 +00:00
feat(stylize): allow all widgets to be styled (#289)
* feat(stylize): allow all widgets to be styled - Add styled impl to: - Barchart - Chart (including Axis and Dataset), - Guage and LineGuage - List and ListItem - Sparkline - Table, Row, and Cell - Tabs - Style - Allow modifiers to be removed (e.g. .not_italic()) - Allow .bg() to recieve Into<Color> - Made shorthand methods consistent with modifier names (e.g. dim() not dimmed() and underlined() not underline()) - Simplify integration tests - Add doc comments - Simplified stylize macros with https://crates.io/crates/paste * build: run clippy before tests Runny clippy first means that we fail fast when there is an issue that can easily be fixed rather than having to wait 30-40s for the failure
This commit is contained in:
parent
6f6c355c5c
commit
9f1f59a51c
17 changed files with 904 additions and 322 deletions
|
@ -40,6 +40,7 @@ bitflags = "2.3"
|
|||
cassowary = "0.3"
|
||||
crossterm = { version = "0.26", optional = true }
|
||||
indoc = "2.0"
|
||||
paste = "1.0"
|
||||
serde = { version = "1", optional = true, features = ["derive"] }
|
||||
termion = { version = "2.0", optional = true }
|
||||
termwiz = { version = "0.20.0", optional = true }
|
||||
|
|
|
@ -18,18 +18,18 @@ run_task = [
|
|||
private = true
|
||||
dependencies = [
|
||||
"style-check",
|
||||
"clippy-unix",
|
||||
"check-unix",
|
||||
"test-unix",
|
||||
"clippy-unix",
|
||||
]
|
||||
|
||||
[tasks.ci-windows]
|
||||
private = true
|
||||
dependencies = [
|
||||
"style-check",
|
||||
"clippy-windows",
|
||||
"check-windows",
|
||||
"test-windows",
|
||||
"clippy-windows",
|
||||
]
|
||||
|
||||
[tasks.style-check]
|
||||
|
|
|
@ -206,7 +206,7 @@ pub mod prelude {
|
|||
backend::Backend,
|
||||
buffer::Buffer,
|
||||
layout::{Alignment, Constraint, Corner, Direction, Layout, Margin, Rect},
|
||||
style::{Color, Modifier, Style, Stylize},
|
||||
style::{Color, Modifier, Style, Styled, Stylize},
|
||||
symbols::{self, Marker},
|
||||
terminal::{Frame, Terminal, TerminalOptions, Viewport},
|
||||
text::{Line, Masked, Span, Text},
|
||||
|
|
170
src/style.rs
170
src/style.rs
|
@ -15,20 +15,16 @@
|
|||
//!
|
||||
//! # Using style shorthands
|
||||
//!
|
||||
//! This is best for consise styling.
|
||||
//! This is best for concise styling.
|
||||
//! ## Example
|
||||
//! ```
|
||||
//! use ratatui::{
|
||||
//! style::{Color, Modifier, Style, Styled, Stylize},
|
||||
//! text::Span,
|
||||
//! };
|
||||
//! use ratatui::prelude::*;
|
||||
//!
|
||||
//! assert_eq!(
|
||||
//! "hello".red().on_blue().bold(),
|
||||
//! Span::styled("hello", Style::default().fg(Color::Red).bg(Color::Blue).add_modifier(Modifier::BOLD))
|
||||
//! )
|
||||
//! ```
|
||||
mod stylized;
|
||||
|
||||
use std::{
|
||||
fmt::{self, Debug},
|
||||
|
@ -36,7 +32,9 @@ use std::{
|
|||
};
|
||||
|
||||
use bitflags::bitflags;
|
||||
pub use stylized::{Styled, Stylize};
|
||||
|
||||
mod stylize;
|
||||
pub use stylize::{Styled, Stylize};
|
||||
|
||||
/// ANSI Color
|
||||
///
|
||||
|
@ -258,6 +256,17 @@ impl Default for Style {
|
|||
}
|
||||
}
|
||||
|
||||
impl Styled for Style {
|
||||
type Item = Style;
|
||||
|
||||
fn style(&self) -> Style {
|
||||
*self
|
||||
}
|
||||
|
||||
fn set_style(self, style: Style) -> Self::Item {
|
||||
self.patch(style)
|
||||
}
|
||||
}
|
||||
impl Style {
|
||||
pub const fn new() -> Style {
|
||||
Style {
|
||||
|
@ -671,4 +680,151 @@ mod tests {
|
|||
.remove_modifier(Modifier::ITALIC)
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn style_can_be_stylized() {
|
||||
// foreground colors
|
||||
assert_eq!(Style::new().black(), Style::new().fg(Color::Black));
|
||||
assert_eq!(Style::new().red(), Style::new().fg(Color::Red));
|
||||
assert_eq!(Style::new().green(), Style::new().fg(Color::Green));
|
||||
assert_eq!(Style::new().yellow(), Style::new().fg(Color::Yellow));
|
||||
assert_eq!(Style::new().blue(), Style::new().fg(Color::Blue));
|
||||
assert_eq!(Style::new().magenta(), Style::new().fg(Color::Magenta));
|
||||
assert_eq!(Style::new().cyan(), Style::new().fg(Color::Cyan));
|
||||
assert_eq!(Style::new().white(), Style::new().fg(Color::White));
|
||||
assert_eq!(Style::new().gray(), Style::new().fg(Color::Gray));
|
||||
assert_eq!(Style::new().dark_gray(), Style::new().fg(Color::DarkGray));
|
||||
assert_eq!(Style::new().light_red(), Style::new().fg(Color::LightRed));
|
||||
assert_eq!(
|
||||
Style::new().light_green(),
|
||||
Style::new().fg(Color::LightGreen)
|
||||
);
|
||||
assert_eq!(
|
||||
Style::new().light_yellow(),
|
||||
Style::new().fg(Color::LightYellow)
|
||||
);
|
||||
assert_eq!(Style::new().light_blue(), Style::new().fg(Color::LightBlue));
|
||||
assert_eq!(
|
||||
Style::new().light_magenta(),
|
||||
Style::new().fg(Color::LightMagenta)
|
||||
);
|
||||
assert_eq!(Style::new().light_cyan(), Style::new().fg(Color::LightCyan));
|
||||
assert_eq!(Style::new().white(), Style::new().fg(Color::White));
|
||||
|
||||
// Background colors
|
||||
assert_eq!(Style::new().on_black(), Style::new().bg(Color::Black));
|
||||
assert_eq!(Style::new().on_red(), Style::new().bg(Color::Red));
|
||||
assert_eq!(Style::new().on_green(), Style::new().bg(Color::Green));
|
||||
assert_eq!(Style::new().on_yellow(), Style::new().bg(Color::Yellow));
|
||||
assert_eq!(Style::new().on_blue(), Style::new().bg(Color::Blue));
|
||||
assert_eq!(Style::new().on_magenta(), Style::new().bg(Color::Magenta));
|
||||
assert_eq!(Style::new().on_cyan(), Style::new().bg(Color::Cyan));
|
||||
assert_eq!(Style::new().on_white(), Style::new().bg(Color::White));
|
||||
assert_eq!(Style::new().on_gray(), Style::new().bg(Color::Gray));
|
||||
assert_eq!(
|
||||
Style::new().on_dark_gray(),
|
||||
Style::new().bg(Color::DarkGray)
|
||||
);
|
||||
assert_eq!(
|
||||
Style::new().on_light_red(),
|
||||
Style::new().bg(Color::LightRed)
|
||||
);
|
||||
assert_eq!(
|
||||
Style::new().on_light_green(),
|
||||
Style::new().bg(Color::LightGreen)
|
||||
);
|
||||
assert_eq!(
|
||||
Style::new().on_light_yellow(),
|
||||
Style::new().bg(Color::LightYellow)
|
||||
);
|
||||
assert_eq!(
|
||||
Style::new().on_light_blue(),
|
||||
Style::new().bg(Color::LightBlue)
|
||||
);
|
||||
assert_eq!(
|
||||
Style::new().on_light_magenta(),
|
||||
Style::new().bg(Color::LightMagenta)
|
||||
);
|
||||
assert_eq!(
|
||||
Style::new().on_light_cyan(),
|
||||
Style::new().bg(Color::LightCyan)
|
||||
);
|
||||
assert_eq!(Style::new().on_white(), Style::new().bg(Color::White));
|
||||
|
||||
// Add Modifiers
|
||||
assert_eq!(
|
||||
Style::new().bold(),
|
||||
Style::new().add_modifier(Modifier::BOLD)
|
||||
);
|
||||
assert_eq!(Style::new().dim(), Style::new().add_modifier(Modifier::DIM));
|
||||
assert_eq!(
|
||||
Style::new().italic(),
|
||||
Style::new().add_modifier(Modifier::ITALIC)
|
||||
);
|
||||
assert_eq!(
|
||||
Style::new().underlined(),
|
||||
Style::new().add_modifier(Modifier::UNDERLINED)
|
||||
);
|
||||
assert_eq!(
|
||||
Style::new().slow_blink(),
|
||||
Style::new().add_modifier(Modifier::SLOW_BLINK)
|
||||
);
|
||||
assert_eq!(
|
||||
Style::new().rapid_blink(),
|
||||
Style::new().add_modifier(Modifier::RAPID_BLINK)
|
||||
);
|
||||
assert_eq!(
|
||||
Style::new().reversed(),
|
||||
Style::new().add_modifier(Modifier::REVERSED)
|
||||
);
|
||||
assert_eq!(
|
||||
Style::new().hidden(),
|
||||
Style::new().add_modifier(Modifier::HIDDEN)
|
||||
);
|
||||
assert_eq!(
|
||||
Style::new().crossed_out(),
|
||||
Style::new().add_modifier(Modifier::CROSSED_OUT)
|
||||
);
|
||||
|
||||
// Remove Modifiers
|
||||
assert_eq!(
|
||||
Style::new().not_bold(),
|
||||
Style::new().remove_modifier(Modifier::BOLD)
|
||||
);
|
||||
assert_eq!(
|
||||
Style::new().not_dim(),
|
||||
Style::new().remove_modifier(Modifier::DIM)
|
||||
);
|
||||
assert_eq!(
|
||||
Style::new().not_italic(),
|
||||
Style::new().remove_modifier(Modifier::ITALIC)
|
||||
);
|
||||
assert_eq!(
|
||||
Style::new().not_underlined(),
|
||||
Style::new().remove_modifier(Modifier::UNDERLINED)
|
||||
);
|
||||
assert_eq!(
|
||||
Style::new().not_slow_blink(),
|
||||
Style::new().remove_modifier(Modifier::SLOW_BLINK)
|
||||
);
|
||||
assert_eq!(
|
||||
Style::new().not_rapid_blink(),
|
||||
Style::new().remove_modifier(Modifier::RAPID_BLINK)
|
||||
);
|
||||
assert_eq!(
|
||||
Style::new().not_reversed(),
|
||||
Style::new().remove_modifier(Modifier::REVERSED)
|
||||
);
|
||||
assert_eq!(
|
||||
Style::new().not_hidden(),
|
||||
Style::new().remove_modifier(Modifier::HIDDEN)
|
||||
);
|
||||
assert_eq!(
|
||||
Style::new().not_crossed_out(),
|
||||
Style::new().remove_modifier(Modifier::CROSSED_OUT)
|
||||
);
|
||||
|
||||
// reset
|
||||
assert_eq!(Style::new().reset(), Style::reset());
|
||||
}
|
||||
}
|
||||
|
|
260
src/style/stylize.rs
Normal file
260
src/style/stylize.rs
Normal file
|
@ -0,0 +1,260 @@
|
|||
use paste::paste;
|
||||
|
||||
use crate::{
|
||||
style::{Color, Modifier, Style},
|
||||
text::Span,
|
||||
};
|
||||
|
||||
/// A trait for objects that have a `Style`.
|
||||
///
|
||||
/// This trait enables generic code to be written that can interact with any object that has a
|
||||
/// `Style`. This is used by the `Stylize` trait to allow generic code to be written that can
|
||||
/// interact with any object that can be styled.
|
||||
pub trait Styled {
|
||||
type Item;
|
||||
|
||||
fn style(&self) -> Style;
|
||||
fn set_style(self, style: Style) -> Self::Item;
|
||||
}
|
||||
|
||||
/// Generates two methods for each color, one for setting the foreground color (`red()`, `blue()`,
|
||||
/// etc) and one for setting the background color (`on_red()`, `on_blue()`, etc.). Each method sets
|
||||
/// the color of the style to the corresponding color.
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// color!(black);
|
||||
///
|
||||
/// // generates
|
||||
///
|
||||
/// #[doc = "Sets the foreground color to [`black`](Color::Black)."]
|
||||
/// fn black(self) -> T {
|
||||
/// self.fg(Color::Black)
|
||||
/// }
|
||||
///
|
||||
/// #[doc = "Sets the background color to [`black`](Color::Black)."]
|
||||
/// fn on_black(self) -> T {
|
||||
/// self.bg(Color::Black)
|
||||
/// }
|
||||
/// ```
|
||||
macro_rules! color {
|
||||
( $color:ident ) => {
|
||||
paste! {
|
||||
#[doc = "Sets the foreground color to [`" $color "`](Color::" $color:camel ")."]
|
||||
fn $color(self) -> T {
|
||||
self.fg(Color::[<$color:camel>])
|
||||
}
|
||||
|
||||
#[doc = "Sets the background color to [`" $color "`](Color::" $color:camel ")."]
|
||||
fn [<on_ $color>](self) -> T {
|
||||
self.bg(Color::[<$color:camel>])
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Generates a method for a modifier (`bold()`, `italic()`, etc.). Each method sets the modifier
|
||||
/// of the style to the corresponding modifier.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// modifier!(bold);
|
||||
///
|
||||
/// // generates
|
||||
///
|
||||
/// #[doc = "Adds the [`BOLD`](Modifier::BOLD) modifier."]
|
||||
/// fn bold(self) -> T {
|
||||
/// self.add_modifier(Modifier::BOLD)
|
||||
/// }
|
||||
///
|
||||
/// #[doc = "Removes the [`BOLD`](Modifier::BOLD) modifier."]
|
||||
/// fn not_bold(self) -> T {
|
||||
/// self.remove_modifier(Modifier::BOLD)
|
||||
/// }
|
||||
/// ```
|
||||
macro_rules! modifier {
|
||||
( $modifier:ident ) => {
|
||||
paste! {
|
||||
#[doc = "Adds the [`" $modifier:upper "`](Modifier::" $modifier:upper ") modifier."]
|
||||
fn [<$modifier>](self) -> T {
|
||||
self.add_modifier(Modifier::[<$modifier:upper>])
|
||||
}
|
||||
}
|
||||
|
||||
paste! {
|
||||
#[doc = "Removes the [`" $modifier:upper "`](Modifier::" $modifier:upper ") modifier."]
|
||||
fn [<not_ $modifier>](self) -> T {
|
||||
self.remove_modifier(Modifier::[<$modifier:upper>])
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// The trait that enables something to be have a style.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use ratatui::{
|
||||
/// style::{Color, Modifier, Style, Styled, Stylize},
|
||||
/// text::Span,
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// "hello".red().on_blue().bold(),
|
||||
/// Span::styled("hello", Style::default().fg(Color::Red).bg(Color::Blue).add_modifier(Modifier::BOLD))
|
||||
/// )
|
||||
pub trait Stylize<'a, T>: Sized {
|
||||
fn bg(self, color: Color) -> T;
|
||||
fn fg<S: Into<Color>>(self, color: S) -> T;
|
||||
fn reset(self) -> T;
|
||||
fn add_modifier(self, modifier: Modifier) -> T;
|
||||
fn remove_modifier(self, modifier: Modifier) -> T;
|
||||
|
||||
color!(black);
|
||||
color!(red);
|
||||
color!(green);
|
||||
color!(yellow);
|
||||
color!(blue);
|
||||
color!(magenta);
|
||||
color!(cyan);
|
||||
color!(gray);
|
||||
color!(dark_gray);
|
||||
color!(light_red);
|
||||
color!(light_green);
|
||||
color!(light_yellow);
|
||||
color!(light_blue);
|
||||
color!(light_magenta);
|
||||
color!(light_cyan);
|
||||
color!(white);
|
||||
|
||||
modifier!(bold);
|
||||
modifier!(dim);
|
||||
modifier!(italic);
|
||||
modifier!(underlined);
|
||||
modifier!(slow_blink);
|
||||
modifier!(rapid_blink);
|
||||
modifier!(reversed);
|
||||
modifier!(hidden);
|
||||
modifier!(crossed_out);
|
||||
}
|
||||
|
||||
impl<'a, T, U> Stylize<'a, T> for U
|
||||
where
|
||||
U: Styled<Item = T>,
|
||||
{
|
||||
fn bg(self, color: Color) -> T {
|
||||
let style = self.style().bg(color);
|
||||
self.set_style(style)
|
||||
}
|
||||
|
||||
fn fg<S: Into<Color>>(self, color: S) -> T {
|
||||
let style = self.style().fg(color.into());
|
||||
self.set_style(style)
|
||||
}
|
||||
|
||||
fn add_modifier(self, modifier: Modifier) -> T {
|
||||
let style = self.style().add_modifier(modifier);
|
||||
self.set_style(style)
|
||||
}
|
||||
|
||||
fn remove_modifier(self, modifier: Modifier) -> T {
|
||||
let style = self.style().remove_modifier(modifier);
|
||||
self.set_style(style)
|
||||
}
|
||||
|
||||
fn reset(self) -> T {
|
||||
self.set_style(Style::reset())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Styled for &'a str {
|
||||
type Item = Span<'a>;
|
||||
|
||||
fn style(&self) -> Style {
|
||||
Style::default()
|
||||
}
|
||||
|
||||
fn set_style(self, style: Style) -> Self::Item {
|
||||
Span::styled(self, style)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn reset() {
|
||||
assert_eq!(
|
||||
"hello".on_cyan().light_red().bold().underlined().reset(),
|
||||
Span::styled("hello", Style::reset())
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fg() {
|
||||
let cyan_fg = Style::default().fg(Color::Cyan);
|
||||
|
||||
assert_eq!("hello".cyan(), Span::styled("hello", cyan_fg));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bg() {
|
||||
let cyan_bg = Style::default().bg(Color::Cyan);
|
||||
|
||||
assert_eq!("hello".on_cyan(), Span::styled("hello", cyan_bg));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn color_modifier() {
|
||||
let cyan_bold = Style::default()
|
||||
.fg(Color::Cyan)
|
||||
.add_modifier(Modifier::BOLD);
|
||||
|
||||
assert_eq!("hello".cyan().bold(), Span::styled("hello", cyan_bold))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fg_bg() {
|
||||
let cyan_fg_bg = Style::default().bg(Color::Cyan).fg(Color::Cyan);
|
||||
|
||||
assert_eq!("hello".cyan().on_cyan(), Span::styled("hello", cyan_fg_bg))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repeated_attributes() {
|
||||
let cyan_bg = Style::default().bg(Color::Cyan);
|
||||
let cyan_fg = Style::default().fg(Color::Cyan);
|
||||
|
||||
// Behavior: the last one set is the definitive one
|
||||
assert_eq!("hello".on_red().on_cyan(), Span::styled("hello", cyan_bg));
|
||||
assert_eq!("hello".red().cyan(), Span::styled("hello", cyan_fg));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_chained() {
|
||||
let all_modifier_black = Style::default()
|
||||
.bg(Color::Black)
|
||||
.fg(Color::Black)
|
||||
.add_modifier(
|
||||
Modifier::UNDERLINED
|
||||
| Modifier::BOLD
|
||||
| Modifier::DIM
|
||||
| Modifier::SLOW_BLINK
|
||||
| Modifier::REVERSED
|
||||
| Modifier::CROSSED_OUT,
|
||||
);
|
||||
assert_eq!(
|
||||
"hello"
|
||||
.on_black()
|
||||
.black()
|
||||
.bold()
|
||||
.underlined()
|
||||
.dim()
|
||||
.slow_blink()
|
||||
.crossed_out()
|
||||
.reversed(),
|
||||
Span::styled("hello", all_modifier_black)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,228 +0,0 @@
|
|||
use crate::{
|
||||
style::{Color, Modifier, Style},
|
||||
text::Span,
|
||||
};
|
||||
|
||||
pub trait Styled {
|
||||
type Item;
|
||||
|
||||
fn style(&self) -> Style;
|
||||
fn set_style(self, style: Style) -> Self::Item;
|
||||
}
|
||||
|
||||
// Otherwise rustfmt behaves weirdly for some reason
|
||||
macro_rules! calculated_docs {
|
||||
($(#[doc = $doc:expr] $item:item)*) => { $(#[doc = $doc] $item)* };
|
||||
}
|
||||
|
||||
macro_rules! modifier_method {
|
||||
($method_name:ident Modifier::$modifier:ident) => {
|
||||
calculated_docs! {
|
||||
#[doc = concat!(
|
||||
"Applies the [`",
|
||||
stringify!($modifier),
|
||||
"`](crate::style::Modifier::",
|
||||
stringify!($modifier),
|
||||
") modifier.",
|
||||
)]
|
||||
fn $method_name(self) -> T {
|
||||
self.modifier(Modifier::$modifier)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! color_method {
|
||||
($method_name_fg:ident, $method_name_bg:ident Color::$color:ident) => {
|
||||
calculated_docs! {
|
||||
#[doc = concat!(
|
||||
"Sets the foreground color to [`",
|
||||
stringify!($color),
|
||||
"`](Color::",
|
||||
stringify!($color),
|
||||
")."
|
||||
)]
|
||||
fn $method_name_fg(self) -> T {
|
||||
self.fg(Color::$color)
|
||||
}
|
||||
|
||||
#[doc = concat!(
|
||||
"Sets the background color to [`",
|
||||
stringify!($color),
|
||||
"`](Color::",
|
||||
stringify!($color),
|
||||
")."
|
||||
)]
|
||||
fn $method_name_bg(self) -> T {
|
||||
self.bg(Color::$color)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// The trait that enables something to be have a style.
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use ratatui::{
|
||||
/// style::{Color, Modifier, Style, Styled, Stylize},
|
||||
/// text::Span,
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// "hello".red().on_blue().bold(),
|
||||
/// Span::styled("hello", Style::default().fg(Color::Red).bg(Color::Blue).add_modifier(Modifier::BOLD))
|
||||
/// )
|
||||
pub trait Stylize<'a, T>: Sized {
|
||||
// Colors
|
||||
fn fg<S: Into<Color>>(self, color: S) -> T;
|
||||
fn bg(self, color: Color) -> T;
|
||||
|
||||
color_method!(black, on_black Color::Black);
|
||||
color_method!(red, on_red Color::Red);
|
||||
color_method!(green, on_green Color::Green);
|
||||
color_method!(yellow, on_yellow Color::Yellow);
|
||||
color_method!(blue, on_blue Color::Blue);
|
||||
color_method!(magenta, on_magenta Color::Magenta);
|
||||
color_method!(cyan, on_cyan Color::Cyan);
|
||||
color_method!(gray, on_gray Color::Gray);
|
||||
color_method!(dark_gray, on_dark_gray Color::DarkGray);
|
||||
color_method!(light_red, on_light_red Color::LightRed);
|
||||
color_method!(light_green, on_light_green Color::LightGreen);
|
||||
color_method!(light_yellow, on_light_yellow Color::LightYellow);
|
||||
color_method!(light_blue, on_light_blue Color::LightBlue);
|
||||
color_method!(light_magenta, on_light_magenta Color::LightMagenta);
|
||||
color_method!(light_cyan, on_light_cyan Color::LightCyan);
|
||||
color_method!(white, on_white Color::White);
|
||||
|
||||
// Styles
|
||||
fn reset(self) -> T;
|
||||
|
||||
// Modifiers
|
||||
fn modifier(self, modifier: Modifier) -> T;
|
||||
|
||||
modifier_method!(bold Modifier::BOLD);
|
||||
modifier_method!(dimmed Modifier::DIM);
|
||||
modifier_method!(italic Modifier::ITALIC);
|
||||
modifier_method!(underline Modifier::UNDERLINED);
|
||||
modifier_method!(slow_blink Modifier::SLOW_BLINK);
|
||||
modifier_method!(rapid_blink Modifier::RAPID_BLINK);
|
||||
modifier_method!(reversed Modifier::REVERSED);
|
||||
modifier_method!(hidden Modifier::HIDDEN);
|
||||
modifier_method!(crossed_out Modifier::CROSSED_OUT);
|
||||
}
|
||||
|
||||
impl<'a, T, U> Stylize<'a, T> for U
|
||||
where
|
||||
U: Styled<Item = T>,
|
||||
{
|
||||
fn fg<S: Into<Color>>(self, color: S) -> T {
|
||||
let style = self.style().fg(color.into());
|
||||
self.set_style(style)
|
||||
}
|
||||
|
||||
fn modifier(self, modifier: Modifier) -> T {
|
||||
let style = self.style().add_modifier(modifier);
|
||||
self.set_style(style)
|
||||
}
|
||||
|
||||
fn bg(self, color: Color) -> T {
|
||||
let style = self.style().bg(color);
|
||||
self.set_style(style)
|
||||
}
|
||||
|
||||
fn reset(self) -> T {
|
||||
self.set_style(Style::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Styled for &'a str {
|
||||
type Item = Span<'a>;
|
||||
|
||||
fn style(&self) -> Style {
|
||||
Style::default()
|
||||
}
|
||||
|
||||
fn set_style(self, style: Style) -> Self::Item {
|
||||
Span::styled(self, style)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn reset() {
|
||||
assert_eq!(
|
||||
"hello".on_cyan().light_red().bold().underline().reset(),
|
||||
Span::from("hello")
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fg() {
|
||||
let cyan_fg = Style::default().fg(Color::Cyan);
|
||||
|
||||
assert_eq!("hello".cyan(), Span::styled("hello", cyan_fg));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bg() {
|
||||
let cyan_bg = Style::default().bg(Color::Cyan);
|
||||
|
||||
assert_eq!("hello".on_cyan(), Span::styled("hello", cyan_bg));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn color_modifier() {
|
||||
let cyan_bold = Style::default()
|
||||
.fg(Color::Cyan)
|
||||
.add_modifier(Modifier::BOLD);
|
||||
|
||||
assert_eq!("hello".cyan().bold(), Span::styled("hello", cyan_bold))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fg_bg() {
|
||||
let cyan_fg_bg = Style::default().bg(Color::Cyan).fg(Color::Cyan);
|
||||
|
||||
assert_eq!("hello".cyan().on_cyan(), Span::styled("hello", cyan_fg_bg))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn repeated_attributes() {
|
||||
let cyan_bg = Style::default().bg(Color::Cyan);
|
||||
let cyan_fg = Style::default().fg(Color::Cyan);
|
||||
|
||||
// Behavior: the last one set is the definitive one
|
||||
assert_eq!("hello".on_red().on_cyan(), Span::styled("hello", cyan_bg));
|
||||
assert_eq!("hello".red().cyan(), Span::styled("hello", cyan_fg));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_chained() {
|
||||
let all_modifier_black = Style::default()
|
||||
.bg(Color::Black)
|
||||
.fg(Color::Black)
|
||||
.add_modifier(
|
||||
Modifier::UNDERLINED
|
||||
| Modifier::BOLD
|
||||
| Modifier::DIM
|
||||
| Modifier::SLOW_BLINK
|
||||
| Modifier::REVERSED
|
||||
| Modifier::CROSSED_OUT,
|
||||
);
|
||||
assert_eq!(
|
||||
"hello"
|
||||
.on_black()
|
||||
.black()
|
||||
.bold()
|
||||
.underline()
|
||||
.dimmed()
|
||||
.slow_blink()
|
||||
.crossed_out()
|
||||
.reversed(),
|
||||
Span::styled("hello", all_modifier_black)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -244,8 +244,9 @@ impl<'a> Styled for Span<'a> {
|
|||
self.style
|
||||
}
|
||||
|
||||
fn set_style(self, style: Style) -> Self {
|
||||
Self::styled(self.content, style)
|
||||
fn set_style(mut self, style: Style) -> Self {
|
||||
self.style = style;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,4 @@
|
|||
use crate::{
|
||||
buffer::Buffer,
|
||||
layout::Rect,
|
||||
style::Style,
|
||||
symbols,
|
||||
widgets::{Block, Widget},
|
||||
};
|
||||
use crate::prelude::*;
|
||||
|
||||
mod bar;
|
||||
mod bar_group;
|
||||
|
@ -25,9 +19,9 @@ pub use bar_group::BarGroup;
|
|||
/// .bar_width(3)
|
||||
/// .bar_gap(1)
|
||||
/// .group_gap(3)
|
||||
/// .bar_style(Style::default().fg(Color::Yellow).bg(Color::Red))
|
||||
/// .value_style(Style::default().fg(Color::Red).add_modifier(Modifier::BOLD))
|
||||
/// .label_style(Style::default().fg(Color::White))
|
||||
/// .bar_style(Style::new().yellow().on_red())
|
||||
/// .value_style(Style::new().red().bold())
|
||||
/// .label_style(Style::new().white())
|
||||
/// .data(&[("B0", 0), ("B1", 2), ("B2", 4), ("B3", 3)])
|
||||
/// .data(BarGroup::default().bars(&[Bar::default().value(10), Bar::default().value(20)]))
|
||||
/// .max(4);
|
||||
|
@ -328,16 +322,23 @@ impl<'a> Widget for BarChart<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Styled for BarChart<'a> {
|
||||
type Item = BarChart<'a>;
|
||||
fn style(&self) -> Style {
|
||||
self.style
|
||||
}
|
||||
|
||||
fn set_style(self, style: Style) -> Self {
|
||||
self.style(style)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use itertools::iproduct;
|
||||
|
||||
use super::*;
|
||||
use crate::{
|
||||
assert_buffer_eq,
|
||||
style::Color,
|
||||
widgets::{BorderType, Borders},
|
||||
};
|
||||
use crate::assert_buffer_eq;
|
||||
|
||||
#[test]
|
||||
fn default() {
|
||||
|
@ -417,7 +418,7 @@ mod tests {
|
|||
let mut buffer = Buffer::empty(Rect::new(0, 0, 15, 3));
|
||||
let widget = BarChart::default()
|
||||
.data(&[("foo", 1), ("bar", 2)])
|
||||
.bar_style(Style::default().fg(Color::Red));
|
||||
.bar_style(Style::new().red());
|
||||
widget.render(buffer.area, &mut buffer);
|
||||
let mut expected = Buffer::with_lines(vec![
|
||||
" █ ",
|
||||
|
@ -514,7 +515,7 @@ mod tests {
|
|||
let widget = BarChart::default()
|
||||
.data(&[("foo", 1), ("bar", 2)])
|
||||
.bar_width(3)
|
||||
.value_style(Style::default().fg(Color::Red));
|
||||
.value_style(Style::new().red());
|
||||
widget.render(buffer.area, &mut buffer);
|
||||
let mut expected = Buffer::with_lines(vec![
|
||||
" ███ ",
|
||||
|
@ -531,7 +532,7 @@ mod tests {
|
|||
let mut buffer = Buffer::empty(Rect::new(0, 0, 15, 3));
|
||||
let widget = BarChart::default()
|
||||
.data(&[("foo", 1), ("bar", 2)])
|
||||
.label_style(Style::default().fg(Color::Red));
|
||||
.label_style(Style::new().red());
|
||||
widget.render(buffer.area, &mut buffer);
|
||||
let mut expected = Buffer::with_lines(vec![
|
||||
" █ ",
|
||||
|
@ -548,7 +549,7 @@ mod tests {
|
|||
let mut buffer = Buffer::empty(Rect::new(0, 0, 15, 3));
|
||||
let widget = BarChart::default()
|
||||
.data(&[("foo", 1), ("bar", 2)])
|
||||
.style(Style::default().fg(Color::Red));
|
||||
.style(Style::new().red());
|
||||
widget.render(buffer.area, &mut buffer);
|
||||
let mut expected = Buffer::with_lines(vec![
|
||||
" █ ",
|
||||
|
@ -720,4 +721,15 @@ mod tests {
|
|||
assert_eq!(barchart.label_height(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_be_stylized() {
|
||||
assert_eq!(
|
||||
BarChart::default().black().on_white().bold().style,
|
||||
Style::default()
|
||||
.fg(Color::Black)
|
||||
.bg(Color::White)
|
||||
.add_modifier(Modifier::BOLD)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -514,7 +514,10 @@ impl<'a> Styled for Block<'a> {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::layout::Rect;
|
||||
use crate::{
|
||||
layout::Rect,
|
||||
style::{Color, Modifier, Stylize},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn inner_takes_into_account_the_borders() {
|
||||
|
@ -872,4 +875,16 @@ mod tests {
|
|||
.style(_DEFAULT_STYLE)
|
||||
.padding(_DEFAULT_PADDING);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_be_stylized() {
|
||||
assert_eq!(
|
||||
Block::default().black().on_white().bold().not_dim().style,
|
||||
Style::default()
|
||||
.fg(Color::Black)
|
||||
.bg(Color::White)
|
||||
.add_modifier(Modifier::BOLD)
|
||||
.remove_modifier(Modifier::DIM)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use unicode_width::UnicodeWidthStr;
|
|||
use crate::{
|
||||
buffer::Buffer,
|
||||
layout::{Alignment, Constraint, Rect},
|
||||
style::{Color, Style},
|
||||
style::{Color, Style, Styled},
|
||||
symbols,
|
||||
text::{Line as TextLine, Span},
|
||||
widgets::{
|
||||
|
@ -612,9 +612,46 @@ impl<'a> Widget for Chart<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Styled for Axis<'a> {
|
||||
type Item = Axis<'a>;
|
||||
|
||||
fn style(&self) -> Style {
|
||||
self.style
|
||||
}
|
||||
|
||||
fn set_style(self, style: Style) -> Self::Item {
|
||||
self.style(style)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Styled for Dataset<'a> {
|
||||
type Item = Dataset<'a>;
|
||||
|
||||
fn style(&self) -> Style {
|
||||
self.style
|
||||
}
|
||||
|
||||
fn set_style(self, style: Style) -> Self::Item {
|
||||
self.style(style)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Styled for Chart<'a> {
|
||||
type Item = Chart<'a>;
|
||||
|
||||
fn style(&self) -> Style {
|
||||
self.style
|
||||
}
|
||||
|
||||
fn set_style(self, style: Style) -> Self::Item {
|
||||
self.style(style)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::style::{Modifier, Stylize};
|
||||
|
||||
struct LegendTestCase {
|
||||
chart_area: Rect,
|
||||
|
@ -652,4 +689,40 @@ mod tests {
|
|||
assert_eq!(layout.legend_area, case.legend_area);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn axis_can_be_stylized() {
|
||||
assert_eq!(
|
||||
Axis::default().black().on_white().bold().not_dim().style,
|
||||
Style::default()
|
||||
.fg(Color::Black)
|
||||
.bg(Color::White)
|
||||
.add_modifier(Modifier::BOLD)
|
||||
.remove_modifier(Modifier::DIM)
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dataset_can_be_stylized() {
|
||||
assert_eq!(
|
||||
Dataset::default().black().on_white().bold().not_dim().style,
|
||||
Style::default()
|
||||
.fg(Color::Black)
|
||||
.bg(Color::White)
|
||||
.add_modifier(Modifier::BOLD)
|
||||
.remove_modifier(Modifier::DIM)
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chart_can_be_stylized() {
|
||||
assert_eq!(
|
||||
Chart::new(vec![]).black().on_white().bold().not_dim().style,
|
||||
Style::default()
|
||||
.fg(Color::Black)
|
||||
.bg(Color::White)
|
||||
.add_modifier(Modifier::BOLD)
|
||||
.remove_modifier(Modifier::DIM)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::{
|
||||
buffer::Buffer,
|
||||
layout::Rect,
|
||||
style::{Color, Style},
|
||||
style::{Color, Style, Styled},
|
||||
symbols,
|
||||
text::{Line, Span},
|
||||
widgets::{Block, Widget},
|
||||
|
@ -296,9 +296,34 @@ impl<'a> Widget for LineGauge<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Styled for Gauge<'a> {
|
||||
type Item = Gauge<'a>;
|
||||
|
||||
fn style(&self) -> Style {
|
||||
self.style
|
||||
}
|
||||
|
||||
fn set_style(self, style: Style) -> Self::Item {
|
||||
self.style(style)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Styled for LineGauge<'a> {
|
||||
type Item = LineGauge<'a>;
|
||||
|
||||
fn style(&self) -> Style {
|
||||
self.style
|
||||
}
|
||||
|
||||
fn set_style(self, style: Style) -> Self::Item {
|
||||
self.style(style)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::style::{Modifier, Stylize};
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
|
@ -317,4 +342,33 @@ mod tests {
|
|||
fn gauge_invalid_ratio_lower_bound() {
|
||||
Gauge::default().ratio(-0.5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gauge_can_be_stylized() {
|
||||
assert_eq!(
|
||||
Gauge::default().black().on_white().bold().not_dim().style,
|
||||
Style::default()
|
||||
.fg(Color::Black)
|
||||
.bg(Color::White)
|
||||
.add_modifier(Modifier::BOLD)
|
||||
.remove_modifier(Modifier::DIM)
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn line_gauge_can_be_stylized() {
|
||||
assert_eq!(
|
||||
LineGauge::default()
|
||||
.black()
|
||||
.on_white()
|
||||
.bold()
|
||||
.not_dim()
|
||||
.style,
|
||||
Style::default()
|
||||
.fg(Color::Black)
|
||||
.bg(Color::White)
|
||||
.add_modifier(Modifier::BOLD)
|
||||
.remove_modifier(Modifier::DIM)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ use unicode_width::UnicodeWidthStr;
|
|||
use crate::{
|
||||
buffer::Buffer,
|
||||
layout::{Corner, Rect},
|
||||
style::Style,
|
||||
style::{Style, Styled},
|
||||
text::Text,
|
||||
widgets::{Block, StatefulWidget, Widget},
|
||||
};
|
||||
|
@ -291,6 +291,30 @@ impl<'a> Widget for List<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Styled for List<'a> {
|
||||
type Item = List<'a>;
|
||||
|
||||
fn style(&self) -> Style {
|
||||
self.style
|
||||
}
|
||||
|
||||
fn set_style(self, style: Style) -> Self::Item {
|
||||
self.style(style)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Styled for ListItem<'a> {
|
||||
type Item = ListItem<'a>;
|
||||
|
||||
fn style(&self) -> Style {
|
||||
self.style
|
||||
}
|
||||
|
||||
fn set_style(self, style: Style) -> Self::Item {
|
||||
self.style(style)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::borrow::Cow;
|
||||
|
@ -298,7 +322,7 @@ mod tests {
|
|||
use super::*;
|
||||
use crate::{
|
||||
assert_buffer_eq,
|
||||
style::Color,
|
||||
style::{Color, Modifier, Stylize},
|
||||
text::{Line, Span},
|
||||
widgets::{Borders, StatefulWidget, Widget},
|
||||
};
|
||||
|
@ -880,4 +904,28 @@ mod tests {
|
|||
"did not scroll the selected item into view"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_can_be_stylized() {
|
||||
assert_eq!(
|
||||
List::new(vec![]).black().on_white().bold().not_dim().style,
|
||||
Style::default()
|
||||
.fg(Color::Black)
|
||||
.bg(Color::White)
|
||||
.add_modifier(Modifier::BOLD)
|
||||
.remove_modifier(Modifier::DIM)
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_item_can_be_stylized() {
|
||||
assert_eq!(
|
||||
ListItem::new("").black().on_white().bold().not_dim().style,
|
||||
Style::default()
|
||||
.fg(Color::Black)
|
||||
.bg(Color::White)
|
||||
.add_modifier(Modifier::BOLD)
|
||||
.remove_modifier(Modifier::DIM)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -214,7 +214,7 @@ mod test {
|
|||
use super::*;
|
||||
use crate::{
|
||||
backend::TestBackend,
|
||||
style::Color,
|
||||
style::{Color, Modifier, Stylize},
|
||||
text::{Line, Span},
|
||||
widgets::Borders,
|
||||
Terminal,
|
||||
|
@ -702,4 +702,16 @@ mod test {
|
|||
Buffer::with_lines(vec!["こんにちは, ", "世界! 😃 "]),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_be_stylized() {
|
||||
assert_eq!(
|
||||
Paragraph::new("").black().on_white().bold().not_dim().style,
|
||||
Style::default()
|
||||
.fg(Color::Black)
|
||||
.bg(Color::White)
|
||||
.add_modifier(Modifier::BOLD)
|
||||
.remove_modifier(Modifier::DIM)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::cmp::min;
|
|||
use crate::{
|
||||
buffer::Buffer,
|
||||
layout::Rect,
|
||||
style::Style,
|
||||
style::{Style, Styled},
|
||||
symbols,
|
||||
widgets::{Block, Widget},
|
||||
};
|
||||
|
@ -89,6 +89,18 @@ impl<'a> Sparkline<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Styled for Sparkline<'a> {
|
||||
type Item = Sparkline<'a>;
|
||||
|
||||
fn style(&self) -> Style {
|
||||
self.style
|
||||
}
|
||||
|
||||
fn set_style(self, style: Style) -> Self::Item {
|
||||
self.style(style)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Widget for Sparkline<'a> {
|
||||
fn render(mut self, area: Rect, buf: &mut Buffer) {
|
||||
let spark_area = match self.block.take() {
|
||||
|
@ -155,7 +167,11 @@ impl<'a> Widget for Sparkline<'a> {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{assert_buffer_eq, buffer::Cell};
|
||||
use crate::{
|
||||
assert_buffer_eq,
|
||||
buffer::Cell,
|
||||
style::{Color, Modifier, Stylize},
|
||||
};
|
||||
|
||||
// Helper function to render a sparkline to a buffer with a given width
|
||||
// filled with x symbols to make it easier to assert on the result
|
||||
|
@ -206,4 +222,21 @@ mod tests {
|
|||
let buffer = render(widget, 12);
|
||||
assert_buffer_eq!(buffer, Buffer::with_lines(vec!["xxx█▇▆▅▄▃▂▁ "]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_be_stylized() {
|
||||
assert_eq!(
|
||||
Sparkline::default()
|
||||
.black()
|
||||
.on_white()
|
||||
.bold()
|
||||
.not_dim()
|
||||
.style,
|
||||
Style::default()
|
||||
.fg(Color::Black)
|
||||
.bg(Color::White)
|
||||
.add_modifier(Modifier::BOLD)
|
||||
.remove_modifier(Modifier::DIM)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ use unicode_width::UnicodeWidthStr;
|
|||
use crate::{
|
||||
buffer::Buffer,
|
||||
layout::{Constraint, Direction, Layout, Rect},
|
||||
style::Style,
|
||||
style::{Style, Styled},
|
||||
text::Text,
|
||||
widgets::{Block, StatefulWidget, Widget},
|
||||
};
|
||||
|
@ -58,6 +58,18 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Styled for Cell<'a> {
|
||||
type Item = Cell<'a>;
|
||||
|
||||
fn style(&self) -> Style {
|
||||
self.style
|
||||
}
|
||||
|
||||
fn set_style(self, style: Style) -> Self::Item {
|
||||
self.style(style)
|
||||
}
|
||||
}
|
||||
|
||||
/// Holds data to be displayed in a [`Table`] widget.
|
||||
///
|
||||
/// A [`Row`] is a collection of cells. It can be created from simple strings:
|
||||
|
@ -136,6 +148,18 @@ impl<'a> Row<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Styled for Row<'a> {
|
||||
type Item = Row<'a>;
|
||||
|
||||
fn style(&self) -> Style {
|
||||
self.style
|
||||
}
|
||||
|
||||
fn set_style(self, style: Style) -> Self::Item {
|
||||
self.style(style)
|
||||
}
|
||||
}
|
||||
|
||||
/// A widget to display data in formatted columns.
|
||||
///
|
||||
/// It is a collection of [`Row`]s, themselves composed of [`Cell`]s:
|
||||
|
@ -336,6 +360,18 @@ impl<'a> Table<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Styled for Table<'a> {
|
||||
type Item = Table<'a>;
|
||||
|
||||
fn style(&self) -> Style {
|
||||
self.style
|
||||
}
|
||||
|
||||
fn set_style(self, style: Style) -> Self::Item {
|
||||
self.style(style)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct TableState {
|
||||
offset: usize,
|
||||
|
@ -505,11 +541,59 @@ impl<'a> Widget for Table<'a> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::vec;
|
||||
|
||||
use super::*;
|
||||
use crate::style::{Color, Modifier, Style, Stylize};
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn table_invalid_percentages() {
|
||||
Table::new(vec![]).widths(&[Constraint::Percentage(110)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cell_can_be_stylized() {
|
||||
assert_eq!(
|
||||
Cell::from("").black().on_white().bold().not_dim().style,
|
||||
Style::default()
|
||||
.fg(Color::Black)
|
||||
.bg(Color::White)
|
||||
.add_modifier(Modifier::BOLD)
|
||||
.remove_modifier(Modifier::DIM)
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn row_can_be_stylized() {
|
||||
assert_eq!(
|
||||
Row::new(vec![Cell::from("")])
|
||||
.black()
|
||||
.on_white()
|
||||
.bold()
|
||||
.not_italic()
|
||||
.style,
|
||||
Style::default()
|
||||
.fg(Color::Black)
|
||||
.bg(Color::White)
|
||||
.add_modifier(Modifier::BOLD)
|
||||
.remove_modifier(Modifier::ITALIC)
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn table_can_be_stylized() {
|
||||
assert_eq!(
|
||||
Table::new(vec![Row::new(vec![Cell::from("")])])
|
||||
.black()
|
||||
.on_white()
|
||||
.bold()
|
||||
.not_crossed_out()
|
||||
.style,
|
||||
Style::default()
|
||||
.fg(Color::Black)
|
||||
.bg(Color::White)
|
||||
.add_modifier(Modifier::BOLD)
|
||||
.remove_modifier(Modifier::CROSSED_OUT)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::{
|
||||
buffer::Buffer,
|
||||
layout::Rect,
|
||||
style::Style,
|
||||
style::{Style, Styled},
|
||||
symbols,
|
||||
text::{Line, Span},
|
||||
widgets::{Block, Widget},
|
||||
|
@ -83,6 +83,18 @@ impl<'a> Tabs<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Styled for Tabs<'a> {
|
||||
type Item = Tabs<'a>;
|
||||
|
||||
fn style(&self) -> Style {
|
||||
self.style
|
||||
}
|
||||
|
||||
fn set_style(self, style: Style) -> Self::Item {
|
||||
self.style(style)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Widget for Tabs<'a> {
|
||||
fn render(mut self, area: Rect, buf: &mut Buffer) {
|
||||
buf.set_style(area, self.style);
|
||||
|
@ -130,3 +142,26 @@ impl<'a> Widget for Tabs<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::style::{Color, Modifier, Stylize};
|
||||
|
||||
#[test]
|
||||
fn can_be_stylized() {
|
||||
assert_eq!(
|
||||
Tabs::new(vec![""])
|
||||
.black()
|
||||
.on_white()
|
||||
.bold()
|
||||
.not_italic()
|
||||
.style,
|
||||
Style::default()
|
||||
.fg(Color::Black)
|
||||
.bg(Color::White)
|
||||
.add_modifier(Modifier::BOLD)
|
||||
.remove_modifier(Modifier::ITALIC)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
130
tests/stylize.rs
130
tests/stylize.rs
|
@ -1,88 +1,114 @@
|
|||
use std::io;
|
||||
|
||||
use ratatui::{
|
||||
backend::TestBackend,
|
||||
buffer::Buffer,
|
||||
layout::Rect,
|
||||
style::{Color, Stylize},
|
||||
widgets::{Block, Borders, Paragraph},
|
||||
style::{Color, Style, Stylize},
|
||||
widgets::{BarChart, Block, Borders, Paragraph},
|
||||
Terminal,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn paragraph_block_styles() {
|
||||
let backend = TestBackend::new(10, 1);
|
||||
let mut terminal = Terminal::new(backend).unwrap();
|
||||
fn barchart_can_be_stylized() {
|
||||
let barchart = BarChart::default()
|
||||
.on_white()
|
||||
.bar_style(Style::new().red())
|
||||
.bar_width(2)
|
||||
.value_style(Style::new().green())
|
||||
.label_style(Style::new().blue())
|
||||
.data(&[("A", 1), ("B", 2), ("C", 3)])
|
||||
.max(3);
|
||||
|
||||
let area = Rect::new(0, 0, 9, 5);
|
||||
let mut terminal = Terminal::new(TestBackend::new(9, 6)).unwrap();
|
||||
terminal
|
||||
.draw(|f| {
|
||||
let paragraph = Paragraph::new("Text".cyan());
|
||||
f.render_widget(
|
||||
paragraph,
|
||||
Rect {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 10,
|
||||
height: 1,
|
||||
},
|
||||
);
|
||||
f.render_widget(barchart, area);
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let mut expected = Buffer::with_lines(vec!["Text "]);
|
||||
for x in 0..4 {
|
||||
expected.get_mut(x, 0).set_fg(Color::Cyan);
|
||||
let mut expected = Buffer::with_lines(vec![
|
||||
" ██ ",
|
||||
" ▅▅ ██ ",
|
||||
"▂▂ ██ ██ ",
|
||||
"1█ 2█ 3█ ",
|
||||
"A B C ",
|
||||
" ",
|
||||
]);
|
||||
for y in area.y..area.height {
|
||||
// background
|
||||
for x in area.x..area.width {
|
||||
expected.get_mut(x, y).set_bg(Color::White);
|
||||
}
|
||||
// bars
|
||||
for x in [0, 1, 3, 4, 6, 7].iter() {
|
||||
expected.get_mut(*x, y).set_fg(Color::Red);
|
||||
}
|
||||
}
|
||||
// values
|
||||
for x in 0..3 {
|
||||
expected.get_mut(x * 3, 3).set_fg(Color::Green);
|
||||
}
|
||||
// labels
|
||||
for x in 0..3 {
|
||||
expected.get_mut(x * 3, 4).set_fg(Color::Blue);
|
||||
expected.get_mut(x * 3 + 1, 4).set_fg(Color::Reset);
|
||||
}
|
||||
|
||||
terminal.backend().assert_buffer(&expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn block_styles() {
|
||||
let backend = TestBackend::new(10, 10);
|
||||
let mut terminal = Terminal::new(backend).unwrap();
|
||||
fn block_can_be_stylized() -> io::Result<()> {
|
||||
let block = Block::default()
|
||||
.title("Title".light_blue())
|
||||
.on_cyan()
|
||||
.cyan()
|
||||
.borders(Borders::ALL);
|
||||
|
||||
terminal
|
||||
.draw(|f| {
|
||||
let block = Block::default()
|
||||
.title("Title".light_blue())
|
||||
.on_cyan()
|
||||
.cyan()
|
||||
.borders(Borders::ALL);
|
||||
f.render_widget(
|
||||
block,
|
||||
Rect {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 8,
|
||||
height: 8,
|
||||
},
|
||||
);
|
||||
})
|
||||
.unwrap();
|
||||
let area = Rect::new(0, 0, 8, 3);
|
||||
let mut terminal = Terminal::new(TestBackend::new(11, 4))?;
|
||||
terminal.draw(|f| {
|
||||
f.render_widget(block, area);
|
||||
})?;
|
||||
|
||||
let mut expected = Buffer::with_lines(vec![
|
||||
"┌Title─┐ ",
|
||||
"│ │ ",
|
||||
"│ │ ",
|
||||
"│ │ ",
|
||||
"│ │ ",
|
||||
"│ │ ",
|
||||
"│ │ ",
|
||||
"└──────┘ ",
|
||||
" ",
|
||||
" ",
|
||||
"┌Title─┐ ",
|
||||
"│ │ ",
|
||||
"└──────┘ ",
|
||||
" ",
|
||||
]);
|
||||
for x in 0..8 {
|
||||
for y in 0..8 {
|
||||
for x in area.x..area.width {
|
||||
for y in area.y..area.height {
|
||||
expected
|
||||
.get_mut(x, y)
|
||||
.set_fg(Color::Cyan)
|
||||
.set_bg(Color::Cyan);
|
||||
}
|
||||
}
|
||||
|
||||
for x in 1..=5 {
|
||||
expected.get_mut(x, 0).set_fg(Color::LightBlue);
|
||||
}
|
||||
|
||||
terminal.backend().assert_buffer(&expected);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn paragraph_can_be_stylized() -> io::Result<()> {
|
||||
let paragraph = Paragraph::new("Text".cyan());
|
||||
|
||||
let area = Rect::new(0, 0, 10, 1);
|
||||
let mut terminal = Terminal::new(TestBackend::new(10, 1))?;
|
||||
terminal.draw(|f| {
|
||||
f.render_widget(paragraph, area);
|
||||
})?;
|
||||
|
||||
let mut expected = Buffer::with_lines(vec!["Text "]);
|
||||
for x in 0..4 {
|
||||
expected.get_mut(x, 0).set_fg(Color::Cyan);
|
||||
}
|
||||
terminal.backend().assert_buffer(&expected);
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue