diff --git a/cursive-core/src/style/gradient/mod.rs b/cursive-core/src/style/gradient/mod.rs index f8239f87..cbea5e0e 100644 --- a/cursive-core/src/style/gradient/mod.rs +++ b/cursive-core/src/style/gradient/mod.rs @@ -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>>(colors: &[R]) -> Option { + 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 { // Find the segment @@ -63,6 +86,30 @@ impl Linear { } } +impl From<(Rgb, Rgb)> for Linear { + fn from((start, end): (Rgb, Rgb)) -> Self { + Self::new(start, end) + } +} + +impl From<[Rgb; 2]> for Linear { + fn from([start, end]: [Rgb; 2]) -> Self { + Self::new(start, end) + } +} + +impl From<(Rgb, Rgb)> for Linear { + fn from((start, end): (Rgb, Rgb)) -> Self { + Self::new(start.as_f32(), end.as_f32()) + } +} + +impl From<[Rgb; 2]> for Linear { + fn from([start, end]: [Rgb; 2]) -> Self { + Self::new(start.as_f32(), end.as_f32()) + } +} + /// Radial gradient. pub struct Radial { /// Where the gradient starts. diff --git a/cursive-core/src/utils/markup/gradient.rs b/cursive-core/src/utils/markup/gradient.rs index c745bc2b..40975104 100644 --- a/cursive-core/src/utils/markup/gradient.rs +++ b/cursive-core/src/utils/markup/gradient.rs @@ -66,25 +66,29 @@ where } /// Decorate a text with the given gradient. -pub fn decorate_front(text: S, start: Rgb, end: Rgb) -> StyledString +pub fn decorate_front(text: S, gradient: G) -> StyledString where S: Into, + G: Into, { 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(text: S, start: Rgb, end: Rgb) -> StyledString +pub fn decorate_back(text: S, gradient: G) -> StyledString where S: Into, + G: Into, { 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) } @@ -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, diff --git a/cursive/examples/gradient.rs b/cursive/examples/gradient.rs index ed83fbab..42f85ec6 100644 --- a/cursive/examples/gradient.rs +++ b/cursive/examples/gradient.rs @@ -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, ));