use crate::render::{Color, Theme};
#[derive(Debug, Clone, Copy)]
pub struct StyleResolver<'a> {
theme: &'a Theme,
}
impl<'a> StyleResolver<'a> {
pub fn new(theme: &'a Theme) -> Self {
Self { theme }
}
#[inline]
pub fn theme(&self) -> &'a Theme {
self.theme
}
#[inline]
pub fn line_width(&self, config_override: Option<f32>) -> f32 {
config_override.unwrap_or(self.theme.line_width)
}
#[inline]
pub fn fill_alpha(&self, config_override: Option<f32>, default: f32) -> f32 {
config_override.unwrap_or(default).clamp(0.0, 1.0)
}
#[inline]
pub fn font_size(&self, scale: f32) -> f32 {
self.theme.font_size * scale
}
#[inline]
pub fn edge_color(&self, fill: Color, explicit: Option<Color>) -> Color {
explicit.unwrap_or_else(|| fill.darken(0.3))
}
#[inline]
pub fn edge_color_with_factor(
&self,
fill: Color,
explicit: Option<Color>,
darken_factor: f32,
) -> Color {
explicit.unwrap_or_else(|| fill.darken(darken_factor))
}
#[inline]
pub fn grid_line_width(&self) -> f32 {
self.theme.line_width * 0.5
}
#[inline]
pub fn axis_line_width(&self) -> f32 {
self.theme.line_width * 0.5
}
#[inline]
pub fn tick_line_width(&self) -> f32 {
self.theme.line_width * 0.4
}
#[inline]
pub fn patch_line_width(&self, config_override: Option<f32>) -> f32 {
config_override.unwrap_or(0.8)
}
#[inline]
pub fn series_color(&self, explicit: Option<Color>, series_index: usize) -> Color {
explicit.unwrap_or_else(|| self.theme.get_color(series_index))
}
#[inline]
pub fn marker_size(&self, config_override: Option<f32>) -> f32 {
config_override.unwrap_or(6.0)
}
#[inline]
pub fn tick_label_font_size(&self) -> f32 {
self.theme.tick_label_font_size
}
#[inline]
pub fn axis_label_font_size(&self) -> f32 {
self.theme.axis_label_font_size
}
#[inline]
pub fn foreground(&self) -> Color {
self.theme.foreground
}
#[inline]
pub fn background(&self) -> Color {
self.theme.background
}
#[inline]
pub fn grid_color(&self) -> Color {
self.theme.grid_color
}
}
pub mod defaults {
pub const VIOLIN_FILL_ALPHA: f32 = 0.6;
pub const BOXPLOT_FILL_ALPHA: f32 = 0.7;
pub const HISTOGRAM_FILL_ALPHA: f32 = 1.0;
pub const KDE_FILL_ALPHA: f32 = 0.3;
pub const BAR_FILL_ALPHA: f32 = 1.0;
pub const PATCH_LINE_WIDTH: f32 = 0.8;
pub const HISTOGRAM_BAR_WIDTH: f32 = 0.9;
pub const BOXPLOT_WIDTH_RATIO: f32 = 0.5;
pub const BOXPLOT_CAP_WIDTH: f32 = 0.5;
pub const FLIER_SIZE: f32 = 6.0;
pub const RUG_HEIGHT: f32 = 0.05;
pub const RUG_ALPHA: f32 = 0.5;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_style_resolver_creation() {
let theme = Theme::light();
let resolver = StyleResolver::new(&theme);
assert_eq!(resolver.theme().line_width, theme.line_width);
}
#[test]
fn test_line_width_override() {
let theme = Theme::light();
let resolver = StyleResolver::new(&theme);
assert_eq!(resolver.line_width(Some(2.5)), 2.5);
assert_eq!(resolver.line_width(None), theme.line_width);
}
#[test]
fn test_fill_alpha() {
let theme = Theme::light();
let resolver = StyleResolver::new(&theme);
assert_eq!(resolver.fill_alpha(Some(0.5), 1.0), 0.5);
assert_eq!(resolver.fill_alpha(None, 0.7), 0.7);
assert_eq!(resolver.fill_alpha(Some(1.5), 1.0), 1.0);
assert_eq!(resolver.fill_alpha(Some(-0.5), 1.0), 0.0);
}
#[test]
fn test_font_size_scaling() {
let theme = Theme::light();
let resolver = StyleResolver::new(&theme);
assert_eq!(resolver.font_size(1.0), theme.font_size);
assert_eq!(resolver.font_size(1.4), theme.font_size * 1.4);
assert_eq!(resolver.font_size(0.8), theme.font_size * 0.8);
}
#[test]
fn test_edge_color_explicit() {
let theme = Theme::light();
let resolver = StyleResolver::new(&theme);
let fill = Color::BLUE;
let explicit = Color::RED;
assert_eq!(resolver.edge_color(fill, Some(explicit)), explicit);
}
#[test]
fn test_edge_color_auto_darken() {
let theme = Theme::light();
let resolver = StyleResolver::new(&theme);
let fill = Color::new(100, 150, 200);
let edge = resolver.edge_color(fill, None);
assert_eq!(edge.r, 70); assert_eq!(edge.g, 105); assert_eq!(edge.b, 140); }
#[test]
fn test_edge_color_custom_factor() {
let theme = Theme::light();
let resolver = StyleResolver::new(&theme);
let fill = Color::new(100, 150, 200);
let edge = resolver.edge_color_with_factor(fill, None, 0.5);
assert_eq!(edge.r, 50); assert_eq!(edge.g, 75); assert_eq!(edge.b, 100); }
#[test]
fn test_grid_and_axis_widths() {
let theme = Theme::light();
let resolver = StyleResolver::new(&theme);
assert_eq!(resolver.grid_line_width(), theme.line_width * 0.5);
assert_eq!(resolver.axis_line_width(), theme.line_width * 0.5);
assert_eq!(resolver.tick_line_width(), theme.line_width * 0.4);
}
#[test]
fn test_patch_line_width() {
let theme = Theme::light();
let resolver = StyleResolver::new(&theme);
assert_eq!(resolver.patch_line_width(None), 0.8);
assert_eq!(resolver.patch_line_width(Some(1.5)), 1.5);
}
#[test]
fn test_series_color() {
let theme = Theme::light();
let resolver = StyleResolver::new(&theme);
assert_eq!(resolver.series_color(Some(Color::RED), 0), Color::RED);
assert_eq!(resolver.series_color(None, 0), theme.get_color(0));
assert_eq!(resolver.series_color(None, 1), theme.get_color(1));
}
#[test]
fn test_marker_size() {
let theme = Theme::light();
let resolver = StyleResolver::new(&theme);
assert_eq!(resolver.marker_size(None), 6.0);
assert_eq!(resolver.marker_size(Some(10.0)), 10.0);
}
#[test]
fn test_theme_colors() {
let theme = Theme::dark();
let resolver = StyleResolver::new(&theme);
assert_eq!(resolver.foreground(), theme.foreground);
assert_eq!(resolver.background(), theme.background);
assert_eq!(resolver.grid_color(), theme.grid_color);
}
#[test]
fn test_different_themes() {
let light = Theme::light();
let light_resolver = StyleResolver::new(&light);
let dark = Theme::dark();
let dark_resolver = StyleResolver::new(&dark);
let presentation = Theme::presentation();
let presentation_resolver = StyleResolver::new(&presentation);
assert!(presentation_resolver.line_width(None) > light_resolver.line_width(None));
assert_ne!(light_resolver.background(), dark_resolver.background());
}
}