Skip to content

Commit

Permalink
Make gradient spans decorator take gradients directly
Browse files Browse the repository at this point in the history
  • Loading branch information
gyscos committed Jun 28, 2024
1 parent 48cafd5 commit f3ce44c
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 9 deletions.
47 changes: 47 additions & 0 deletions cursive-core/src/style/gradient/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,29 @@ impl Linear {
}
}

/// Create a simple gradient with evenly spaced colors.
///
/// * Returns `None` if `colors` is empty.
/// * Returns a constant "gradient" (same start and end) if `colors.len() == 1`.
/// * Returns a piecewise gradient between all colors otherwise.
pub fn evenly_spaced<R: Copy + Into<Rgb<f32>>>(colors: &[R]) -> Option<Self> {
if colors.is_empty() {
return None;
}

if colors.len() == 1 {
return Some(Self::new(colors[0], colors[0]));
}

let step = 1f32 / (colors.len() - 1) as f32;
let mut colors = colors.iter().copied().map(Into::into).enumerate();
let (_, start) = colors.next().unwrap();
let (_, end) = colors.next_back().unwrap();
let middle = colors.map(|(i, color)| (step * i as f32, color)).collect();

Some(Self { start, middle, end })
}

/// Interpolate the color for the given position.
pub fn interpolate(&self, x: f32) -> Rgb<f32> {
// Find the segment
Expand Down Expand Up @@ -63,6 +86,30 @@ impl Linear {
}
}

impl From<(Rgb<f32>, Rgb<f32>)> for Linear {
fn from((start, end): (Rgb<f32>, Rgb<f32>)) -> Self {
Self::new(start, end)
}
}

impl From<[Rgb<f32>; 2]> for Linear {
fn from([start, end]: [Rgb<f32>; 2]) -> Self {
Self::new(start, end)
}
}

impl From<(Rgb<u8>, Rgb<u8>)> for Linear {
fn from((start, end): (Rgb<u8>, Rgb<u8>)) -> Self {
Self::new(start.as_f32(), end.as_f32())
}
}

impl From<[Rgb<u8>; 2]> for Linear {
fn from([start, end]: [Rgb<u8>; 2]) -> Self {
Self::new(start.as_f32(), end.as_f32())
}
}

/// Radial gradient.
pub struct Radial {
/// Where the gradient starts.
Expand Down
16 changes: 10 additions & 6 deletions cursive-core/src/utils/markup/gradient.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,25 +66,29 @@ where
}

/// Decorate a text with the given gradient.
pub fn decorate_front<S>(text: S, start: Rgb, end: Rgb) -> StyledString
pub fn decorate_front<S, G>(text: S, gradient: G) -> StyledString
where
S: Into<StyledString>,
G: Into<Linear>,
{
let text = text.into();
let gradient = gradient.into();

let spans = decorate_front_with(&text, |x| Linear::new(start, end).interpolate(x).as_u8());
let spans = decorate_front_with(&text, |x| gradient.interpolate(x).as_u8());

StyledString::with_spans(text.into_source(), spans)
}

/// Decorate a text with the given gradient as background.
pub fn decorate_back<S>(text: S, start: Rgb, end: Rgb) -> StyledString
pub fn decorate_back<S, G>(text: S, gradient: G) -> StyledString
where
S: Into<StyledString>,
G: Into<Linear>,
{
let text = text.into();
let gradient = gradient.into();

let spans = decorate_back_with(&text, |x| Linear::new(start, end).interpolate(x).as_u8());
let spans = decorate_back_with(&text, |x| gradient.interpolate(x).as_u8());

StyledString::with_spans(text.into_source(), spans)
}
Expand All @@ -97,14 +101,14 @@ mod tests {

#[test]
fn simple() {
let gradient = decorate_front("ab", Rgb::new(255, 0, 0), Rgb::new(0, 0, 255));
let gradient = decorate_front("ab", (Rgb::new(255, 0, 0), Rgb::new(0, 0, 255)));

assert_eq!(
gradient,
cursup::parse("/#FF0000{a}/#0000FF{b}").canonical()
);

let gradient = decorate_front("abcde", Rgb::new(255, 0, 0), Rgb::new(0, 0, 255));
let gradient = decorate_front("abcde", (Rgb::new(255, 0, 0), Rgb::new(0, 0, 255)));

assert_eq!(
gradient,
Expand Down
6 changes: 3 additions & 3 deletions cursive/examples/gradient.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ fn main() {
let mut siv = cursive::default();

let text = "So many colors! So little time!";
let text = gradient::decorate_front(text, Rgb::new(255, 0, 0), Rgb::new(0, 0, 255));
let text = gradient::decorate_back(text, Rgb::new(0, 0, 255), Rgb::new(0, 255, 0));
let text = gradient::decorate_front(text, (Rgb::new(255, 0, 0), Rgb::new(0, 0, 255)));
let text = gradient::decorate_back(text, (Rgb::new(0, 0, 255), Rgb::new(0, 255, 0)));

// Add a simple view
siv.add_layer(Dialog::new().content(TextView::new(text)).button(
gradient::decorate_back("Moar", Rgb::new(255, 0, 0), Rgb::new(255, 255, 0)),
gradient::decorate_back("Moar", (Rgb::new(255, 0, 0), Rgb::new(255, 255, 0))),
show_more,
));

Expand Down

0 comments on commit f3ce44c

Please sign in to comment.