use rlvgl_widgets::{
calendar::Calendar, message_box::MessageBox, spinbox::Spinbox, table::Table, window::Window,
};
use crate::{
style::Color,
theme::{ColorScheme, ComponentSize, ComponentStyle, SchemeColors, Theme, Variant},
};
pub trait ThemedPartsProps: Sized {
fn themed_parts(
self,
theme: &Theme,
scheme: ColorScheme,
variant: Variant,
size: ComponentSize,
) -> Self;
}
impl ThemedPartsProps for Calendar {
fn themed_parts(
mut self,
theme: &Theme,
scheme: ColorScheme,
variant: Variant,
size: ComponentSize,
) -> Self {
let resolved = theme.component_style(scheme, variant, size);
let colors = theme.scheme(scheme);
self.style = resolved.style;
self.day_cell_color = panel_color(resolved, colors);
self.selected_color = resolved.accent_color;
self.today_color = theme.scheme(ColorScheme::Warning).solid;
self.text_color = resolved.text_color;
self.header_text_color = colors.solid;
self.overflow_text_color = colors.border;
self
}
}
impl ThemedPartsProps for MessageBox {
fn themed_parts(
mut self,
theme: &Theme,
scheme: ColorScheme,
variant: Variant,
size: ComponentSize,
) -> Self {
let resolved = theme.component_style(scheme, variant, size);
self.style = resolved.style;
self.text_color = resolved.text_color;
self.button_text_color = resolved.accent_color;
self
}
}
impl ThemedPartsProps for Spinbox {
fn themed_parts(
mut self,
theme: &Theme,
scheme: ColorScheme,
variant: Variant,
size: ComponentSize,
) -> Self {
let resolved = theme.component_style(scheme, variant, size);
self.style = resolved.style;
self.text_color = resolved.text_color;
self.cursor_color = resolved.accent_color;
self
}
}
impl ThemedPartsProps for Table {
fn themed_parts(
mut self,
theme: &Theme,
scheme: ColorScheme,
variant: Variant,
size: ComponentSize,
) -> Self {
let resolved = theme.component_style(scheme, variant, size);
let colors = theme.scheme(scheme);
self.style = resolved.style;
self.cell_bg_color = panel_color(resolved, colors);
self.selected_color = resolved.accent_color;
self.text_color = resolved.text_color;
self.grid_color = colors.border;
self
}
}
impl ThemedPartsProps for Window {
fn themed_parts(
mut self,
theme: &Theme,
scheme: ColorScheme,
variant: Variant,
size: ComponentSize,
) -> Self {
let resolved = theme.component_style(scheme, variant, size);
let colors = theme.scheme(scheme);
self.style = resolved.style;
self.header_color = resolved.accent_color;
self.title_color = colors.contrast;
self.button_color = colors.muted;
self.button_text_color = colors.solid;
self
}
}
fn panel_color(resolved: ComponentStyle, colors: SchemeColors) -> Color {
if resolved.style.bg_color.3 == 0 {
colors.subtle
} else {
resolved.style.bg_color
}
}
#[cfg(test)]
mod tests {
use super::*;
use rlvgl_core::widget::Rect;
use rlvgl_widgets::window::DEFAULT_HEADER_HEIGHT;
#[test]
fn table_themed_parts_apply_cell_and_selection_colors() {
let theme = Theme::material_light();
let table = Table::new(rect(0, 0, 100, 60)).themed_parts(
&theme,
ColorScheme::Info,
Variant::Outline,
ComponentSize::Lg,
);
assert_eq!(table.style.border_width, 2);
assert_eq!(table.cell_bg_color, theme.scheme(ColorScheme::Info).subtle);
assert_eq!(table.selected_color, theme.scheme(ColorScheme::Info).solid);
assert_eq!(table.grid_color, theme.scheme(ColorScheme::Info).border);
}
#[test]
fn calendar_themed_parts_apply_calendar_specific_colors() {
let theme = Theme::material_light();
let calendar = Calendar::new(rect(0, 0, 140, 120)).themed_parts(
&theme,
ColorScheme::Success,
Variant::Subtle,
ComponentSize::Md,
);
assert_eq!(
calendar.selected_color,
theme.scheme(ColorScheme::Success).solid
);
assert_eq!(
calendar.today_color,
theme.scheme(ColorScheme::Warning).solid
);
assert_eq!(
calendar.header_text_color,
theme.scheme(ColorScheme::Success).solid
);
}
#[test]
fn text_entry_direct_widgets_receive_accent_colors() {
let theme = Theme::material_light();
let spinbox = Spinbox::new(rect(0, 0, 80, 24)).themed_parts(
&theme,
ColorScheme::Danger,
Variant::Ghost,
ComponentSize::Sm,
);
let message_box = MessageBox::new(rect(0, 0, 120, 80), "Title", "Body", &["OK"])
.themed_parts(
&theme,
ColorScheme::Danger,
Variant::Ghost,
ComponentSize::Sm,
);
assert_eq!(
spinbox.cursor_color,
theme.scheme(ColorScheme::Danger).solid
);
assert_eq!(
message_box.button_text_color,
theme.scheme(ColorScheme::Danger).solid
);
}
#[test]
fn window_themed_parts_apply_header_palette() {
let theme = Theme::material_light();
let window = Window::new(rect(0, 0, 120, 80), DEFAULT_HEADER_HEIGHT).themed_parts(
&theme,
ColorScheme::Primary,
Variant::Solid,
ComponentSize::Md,
);
assert_eq!(
window.header_color,
theme.scheme(ColorScheme::Primary).solid
);
assert_eq!(
window.title_color,
theme.scheme(ColorScheme::Primary).contrast
);
assert_eq!(
window.button_color,
theme.scheme(ColorScheme::Primary).muted
);
}
fn rect(x: i32, y: i32, width: i32, height: i32) -> Rect {
Rect {
x,
y,
width,
height,
}
}
}