feat!: implement stylize methods directly on Style (#1572)

This makes it possible to create constants using the shorthand methods.

```rust
const MY_STYLE: Style = Style::new().blue().on_black();
```

Rather than implementing Styled for Style and then adding extension
methods that implement the Stylize shorthands, this implements the
methods as const functions directly on Style.

BREAKING CHANGE: `Style` no longer implements `Styled`. Any calls to
methods implemented by the blanket implementation of Stylize are now
defined directly on Style. Remove the Stylize import if it is no longer
used by your code.

The `reset()` method does not have a direct replacement, as it clashes
with the existing `reset()` method. Use `Style::reset()` rather than
`some_style.reset()`

Fixes: #1158
This commit is contained in:
Josh McKinney
2025-05-14 15:26:29 -07:00
committed by GitHub
parent e15fefa922
commit 702fff501c
14 changed files with 83 additions and 54 deletions

View File

@@ -10,7 +10,7 @@ GitHub with a [breaking change] label.
This is a quick summary of the sections below:
- [Unreleased](#unreleased)
- [v0.30.0 Unreleased](#v0300-unreleased)
- The `From` impls for backend types are now replaced with more specific traits
- `FrameExt` trait for `unstable-widget-ref` feature
- `List::highlight_symbol` now accepts `Into<Line>` instead of `&str`
@@ -83,7 +83,28 @@ This is a quick summary of the sections below:
- MSRV is now 1.63.0
- `List` no longer ignores empty strings
## Unreleased (0.30.0)
## v0.30.0 Unreleased
### `Style` no longer implements `Styled` ([#1572])
[#1572]: https://github.com/ratatui/ratatui/pull/1572
Any calls to methods implemented by the blanket implementation of `Stylize` are now defined directly
on `Style`. Remove the `Stylize` import if it is no longer used by your code.
```diff
- use ratatui::style::Stylize;
let style = Style::new().red();
```
The `reset()` method does not have a direct replacement, as it clashes with the existing `reset()`
method. Use the `Style::reset()` method instead.
```diff
- some_style.reset();
+ Style::reset();
```
### Disabling `default-features` suppresses the error message if `show_cursor()` fails when dropping `Terminal` ([#1794])
@@ -101,8 +122,6 @@ enabled feature flags.
### Disabling `default-features` will now disable layout cache, which can have a negative impact on performance ([#1795])
[#1795]: https://github.com/ratatui/ratatui/pull/1795
Layout cache is now opt-in in `ratatui-core` and enabled by default in `ratatui`. If app doesn't
make use of `no_std`-compatibility, and disables `default-feature`, it is recommended to explicitly
re-enable layout cache. Not doing so may impact performance.

View File

@@ -202,7 +202,7 @@ fn title_block(title: String) -> Block<'static> {
.borders(Borders::TOP)
.title_alignment(Alignment::Center)
.border_style(Style::new().dark_gray())
.title_style(Style::new().reset())
.title_style(Style::reset())
.title(title)
}

View File

@@ -2,7 +2,7 @@ use itertools::Itertools;
use palette::Okhsv;
use ratatui::buffer::Buffer;
use ratatui::layout::{Constraint, Direction, Layout, Margin, Rect};
use ratatui::style::{Color, Style, Stylize};
use ratatui::style::{Color, Style};
use ratatui::symbols;
use ratatui::widgets::calendar::{CalendarEventStore, Monthly};
use ratatui::widgets::{Bar, BarChart, BarGroup, Block, Clear, LineGauge, Padding, Widget};

View File

@@ -81,6 +81,7 @@ mod color;
pub mod palette;
#[cfg(feature = "palette")]
mod palette_conversion;
#[macro_use]
mod stylize;
bitflags! {
@@ -259,18 +260,6 @@ impl fmt::Debug for Style {
}
}
impl Styled for Style {
type Item = Self;
fn style(&self) -> Style {
*self
}
fn set_style<S: Into<Self>>(self, style: S) -> Self::Item {
self.patch(style)
}
}
impl Style {
/// Returns a `Style` with default properties.
pub const fn new() -> Self {
@@ -493,6 +482,33 @@ impl Style {
}
Ok(())
}
color!(pub const Color::Black, black(), on_black() -> Self);
color!(pub const Color::Red, red(), on_red() -> Self);
color!(pub const Color::Green, green(), on_green() -> Self);
color!(pub const Color::Yellow, yellow(), on_yellow() -> Self);
color!(pub const Color::Blue, blue(), on_blue() -> Self);
color!(pub const Color::Magenta, magenta(), on_magenta() -> Self);
color!(pub const Color::Cyan, cyan(), on_cyan() -> Self);
color!(pub const Color::Gray, gray(), on_gray() -> Self);
color!(pub const Color::DarkGray, dark_gray(), on_dark_gray() -> Self);
color!(pub const Color::LightRed, light_red(), on_light_red() -> Self);
color!(pub const Color::LightGreen, light_green(), on_light_green() -> Self);
color!(pub const Color::LightYellow, light_yellow(), on_light_yellow() -> Self);
color!(pub const Color::LightBlue, light_blue(), on_light_blue() -> Self);
color!(pub const Color::LightMagenta, light_magenta(), on_light_magenta() -> Self);
color!(pub const Color::LightCyan, light_cyan(), on_light_cyan() -> Self);
color!(pub const Color::White, white(), on_white() -> Self);
modifier!(pub const Modifier::BOLD, bold(), not_bold() -> Self);
modifier!(pub const Modifier::DIM, dim(), not_dim() -> Self);
modifier!(pub const Modifier::ITALIC, italic(), not_italic() -> Self);
modifier!(pub const Modifier::UNDERLINED, underlined(), not_underlined() -> Self);
modifier!(pub const Modifier::SLOW_BLINK, slow_blink(), not_slow_blink() -> Self);
modifier!(pub const Modifier::RAPID_BLINK, rapid_blink(), not_rapid_blink() -> Self);
modifier!(pub const Modifier::REVERSED, reversed(), not_reversed() -> Self);
modifier!(pub const Modifier::HIDDEN, hidden(), not_hidden() -> Self);
modifier!(pub const Modifier::CROSSED_OUT, crossed_out(), not_crossed_out() -> Self);
}
impl From<Color> for Style {
@@ -734,14 +750,19 @@ mod tests {
const _RESET: Style = Style::reset();
const _RED_FG: Style = Style::new().fg(RED);
const _RED_FG_SHORT: Style = Style::new().red();
const _BLACK_BG: Style = Style::new().bg(BLACK);
const _BLACK_BG_SHORT: Style = Style::new().on_black();
const _ADD_BOLD: Style = Style::new().add_modifier(BOLD);
const _ADD_BOLD_SHORT: Style = Style::new().bold();
const _REMOVE_ITALIC: Style = Style::new().remove_modifier(ITALIC);
const _REMOVE_ITALIC_SHORT: Style = Style::new().not_italic();
const ALL: Style = Style::new()
.fg(RED)
.bg(BLACK)
.add_modifier(BOLD)
.remove_modifier(ITALIC);
const ALL_SHORT: Style = Style::new().red().on_black().bold().not_italic();
assert_eq!(
ALL,
Style::new()
@@ -750,6 +771,7 @@ mod tests {
.add_modifier(Modifier::BOLD)
.remove_modifier(Modifier::ITALIC)
);
assert_eq!(ALL, ALL_SHORT);
}
#[rstest]
@@ -824,11 +846,6 @@ mod tests {
assert_eq!(stylized, Style::new().remove_modifier(expected));
}
#[test]
fn reset_can_be_stylized() {
assert_eq!(Style::new().reset(), Style::reset());
}
#[test]
fn from_color() {
assert_eq!(Style::from(Color::Red), Style::new().fg(Color::Red));

View File

@@ -126,16 +126,16 @@ macro_rules! color {
}
};
( const $variant:ident, $fg:ident(), $bg:ident() -> $ty:ty ) => {
(pub const $variant:expr, $color:ident(), $on_color:ident() -> $ty:ty ) => {
#[doc = concat!("Sets the foreground color to [`", stringify!($color), "`](", stringify!($variant), ").")]
#[must_use = concat!("`", stringify!($color), "` returns the modified style without modifying the original")]
const fn $color(self) -> $ty {
pub const fn $color(self) -> $ty {
self.fg($variant)
}
#[doc = concat!("Sets the background color to [`", stringify!($color), "`](", stringify!($variant), ").")]
#[must_use = concat!("`", stringify!($on_color), "` returns the modified style without modifying the original")]
const fn $on_color(self) -> $ty {
pub const fn $on_color(self) -> $ty {
self.bg($variant)
}
};
@@ -176,16 +176,16 @@ macro_rules! modifier {
}
};
( const $variant:expr, $modifier:ident(), $not_modifier:ident() -> $ty:ty ) => {
(pub const $variant:expr, $modifier:ident(), $not_modifier:ident() -> $ty:ty ) => {
#[doc = concat!("Adds the [`", stringify!($modifier), "`](", stringify!($variant), ") modifier.")]
#[must_use = concat!("`", stringify!($modifier), "` returns the modified style without modifying the original")]
const fn $modifier>(self) -> $ty {
pub const fn $modifier(self) -> $ty {
self.add_modifier($variant)
}
#[doc = concat!("Removes the [`", stringify!($modifier), "`](", stringify!($variant), ") modifier.")]
#[must_use = concat!("`", stringify!($not_modifier), "` returns the modified style without modifying the original")]
const fn $not_modifier(self) -> $ty {
pub const fn $not_modifier(self) -> $ty {
self.remove_modifier($variant)
}
};

View File

@@ -1106,12 +1106,12 @@ mod tests {
#[test]
fn styled_graphemes() {
const RED: Style = Style::new().fg(Color::Red);
const GREEN: Style = Style::new().fg(Color::Green);
const BLUE: Style = Style::new().fg(Color::Blue);
const RED_ON_WHITE: Style = Style::new().fg(Color::Red).bg(Color::White);
const GREEN_ON_WHITE: Style = Style::new().fg(Color::Green).bg(Color::White);
const BLUE_ON_WHITE: Style = Style::new().fg(Color::Blue).bg(Color::White);
const RED: Style = Style::new().red();
const GREEN: Style = Style::new().green();
const BLUE: Style = Style::new().blue();
const RED_ON_WHITE: Style = Style::new().red().on_white();
const GREEN_ON_WHITE: Style = Style::new().green().on_white();
const BLUE_ON_WHITE: Style = Style::new().blue().on_white();
let line = Line::from(vec![
Span::styled("He", RED),
@@ -1192,9 +1192,9 @@ mod tests {
use super::*;
use crate::buffer::Cell;
const BLUE: Style = Style::new().fg(Color::Blue);
const GREEN: Style = Style::new().fg(Color::Green);
const ITALIC: Style = Style::new().add_modifier(Modifier::ITALIC);
const BLUE: Style = Style::new().blue();
const GREEN: Style = Style::new().green();
const ITALIC: Style = Style::new().italic();
#[fixture]
fn hello_world() -> Line<'static> {

View File

@@ -119,7 +119,7 @@ macro_rules! span {
#[cfg(test)]
mod tests {
use ratatui_core::{
style::{Color, Modifier, Style, Stylize},
style::{Color, Modifier, Style},
text::Span,
};

View File

@@ -541,8 +541,6 @@ impl fmt::Display for ResetRegion {
#[cfg(test)]
mod tests {
use ratatui_core::style::Stylize;
use super::*;
#[test]

View File

@@ -780,8 +780,6 @@ mod tests {
#[test]
fn from_cell_attribute_for_style() {
use ratatui_core::style::Stylize;
#[cfg(feature = "underline-color")]
const STYLE: Style = Style::new()
.underline_color(Color::Reset)

View File

@@ -143,7 +143,7 @@ fn draw_line_high(painter: &mut Painter, x1: usize, y1: usize, x2: usize, y2: us
mod tests {
use ratatui_core::buffer::Buffer;
use ratatui_core::layout::Rect;
use ratatui_core::style::{Style, Stylize};
use ratatui_core::style::Style;
use ratatui_core::symbols::Marker;
use ratatui_core::widgets::Widget;
use rstest::rstest;

View File

@@ -79,7 +79,7 @@ impl Shape for Rectangle {
mod tests {
use ratatui_core::buffer::Buffer;
use ratatui_core::layout::{Margin, Rect};
use ratatui_core::style::{Style, Stylize};
use ratatui_core::style::Style;
use ratatui_core::symbols::Marker;
use ratatui_core::widgets::Widget;

View File

@@ -4,14 +4,14 @@ use alloc::vec::Vec;
use itertools::Itertools;
use ratatui_core::buffer::Buffer;
use ratatui_core::layout::Rect;
use ratatui_core::style::{Modifier, Style, Styled};
use ratatui_core::style::{Style, Styled};
use ratatui_core::symbols;
use ratatui_core::text::{Line, Span};
use ratatui_core::widgets::Widget;
use crate::block::{Block, BlockExt};
const DEFAULT_HIGHLIGHT_STYLE: Style = Style::new().add_modifier(Modifier::REVERSED);
const DEFAULT_HIGHLIGHT_STYLE: Style = Style::new().reversed();
/// A widget that displays a horizontal set of Tabs with a single tab selected.
///
@@ -125,6 +125,7 @@ impl<'a> Tabs<'a> {
/// let tabs = Tabs::new(vec!["Tab 1".red(), "Tab 2".blue()]);
/// ```
/// [`String`]: alloc::string::String
/// [`Modifier::REVERSED`]: ratatui_core::style::Modifier
pub fn new<Iter>(titles: Iter) -> Self
where
Iter: IntoIterator,
@@ -672,11 +673,7 @@ mod tests {
.bold()
.not_italic()
.style,
Style::default()
.fg(Color::Black)
.bg(Color::White)
.add_modifier(Modifier::BOLD)
.remove_modifier(Modifier::ITALIC)
Style::default().black().on_white().bold().not_italic()
);
}
}

View File

@@ -1,7 +1,7 @@
use ratatui::backend::TestBackend;
use ratatui::buffer::Buffer;
use ratatui::layout::{Constraint, Direction, Layout, Rect};
use ratatui::style::{Color, Modifier, Style, Stylize};
use ratatui::style::{Color, Modifier, Style};
use ratatui::text::Span;
use ratatui::widgets::{Block, Gauge, LineGauge};
use ratatui::{symbols, Terminal};

View File

@@ -1,7 +1,7 @@
use ratatui::backend::TestBackend;
use ratatui::buffer::Buffer;
use ratatui::layout::Rect;
use ratatui::style::{Style, Stylize};
use ratatui::style::Style;
use ratatui::widgets::Tabs;
use ratatui::{symbols, Terminal};