use crate::geometry::{Rect, Size};
use crate::widget::{MeasureCx, PaintCx, Widget};
const DEFAULT_MIX: f32 = 0.25;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Ghost {
mix: f32,
}
impl Ghost {
#[must_use]
pub fn new() -> Self {
Self { mix: DEFAULT_MIX }
}
#[must_use]
pub fn mix(mut self, ratio: f32) -> Self {
self.mix = ratio.clamp(0.0, 1.0);
self
}
}
impl Default for Ghost {
fn default() -> Self {
Self::new()
}
}
impl<Msg: 'static> Widget<Msg> for Ghost {
fn measure(&self, _cx: &mut MeasureCx<'_>, available: Size) -> Size {
available
}
fn paint(&self, cx: &mut PaintCx<'_>, area: Rect) {
let color = cx.style("ghost", None, &[]).text().bg.unwrap_or_else(|| cx.color("accent"));
cx.tint_ground(area, color, self.mix);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::color::ColorDepth;
use crate::runtime::{App, Command, Harness};
use crate::widget::View;
use crate::widgets::{Button, Text};
struct Preview {
mix: f32,
pressed: u32,
}
impl App for Preview {
type Msg = ();
fn update(&mut self, (): ()) -> Command<()> {
self.pressed += 1;
Command::none()
}
fn view(&self, ui: &mut View<'_, ()>) {
ui.stack(|ui| {
ui.place(Rect::new(0, 0, 12, 2), |ui| {
ui.add(Text::new("under"));
});
ui.place(Rect::new(0, 1, 12, 1), |ui| {
ui.add(Button::new("press").on_press(()));
});
ui.place(Rect::new(2, 0, 8, 2), |ui| {
ui.add(Ghost::new().mix(self.mix));
});
})
.fill();
}
}
#[test]
fn a_ghost_lays_the_accent_over_the_ground_without_a_line_or_a_hit_area() {
let mut h = Harness::new(Preview { mix: 0.25, pressed: 0 }, 14, 3);
assert_eq!(h.screen(), "under\n press\n\n", "the ghost draws no glyph of its own");
let theme = h.env().theme();
let (canvas, accent) = (theme.color("canvas").expect("canvas"), theme.color("accent").expect("accent"));
assert_eq!(h.bg(3, 0), Some(canvas.mix(accent, 0.25)));
assert_eq!(h.bg(1, 0), Some(canvas), "left of the ghost the ground is plain");
assert_eq!(h.fg(3, 0), h.fg(1, 0), "the text keeps its colour");
h.click(4, 1);
assert_eq!(h.app().pressed, 1, "the press went through to the button beneath");
}
#[test]
fn the_mix_says_how_strong_the_tone_is() {
let ground = |mix: f32| {
let h = Harness::new(Preview { mix, pressed: 0 }, 14, 3);
h.bg(3, 0)
};
let theme = Harness::new(Preview { mix: 0.0, pressed: 0 }, 14, 3);
let canvas = theme.env().theme().color("canvas");
assert_eq!(ground(0.0), canvas, "nothing mixed in is no ghost at all");
let (light, strong) = (ground(0.2), ground(0.5));
assert_ne!(light, canvas);
assert_ne!(light, strong);
}
#[test]
fn in_sixteen_colours_the_ghost_still_shows_on_the_ground() {
let mut h = Harness::new(Preview { mix: 0.25, pressed: 0 }, 14, 3);
h.set_depth(ColorDepth::Ansi16);
let ghost = h.buffer()[(3, 0)].bg;
assert_ne!(ghost, h.buffer()[(1, 0)].bg, "the tone is told from the ground");
assert_ne!(ghost, h.buffer()[(3, 0)].fg, "and the text on it reads");
}
}