use std::collections::HashMap;
use crate::{Config, Theme, ThemeDst, Window};
use kas::draw::{color, DrawIface, DrawSharedImpl, SharedState};
use kas::event::EventState;
use kas::theme::{ThemeControl, ThemeDraw};
use kas::TkAction;
type DynTheme<DS> = Box<dyn ThemeDst<DS>>;
pub struct MultiTheme<DS> {
names: HashMap<String, usize>,
themes: Vec<DynTheme<DS>>,
active: usize,
}
pub struct MultiThemeBuilder<DS> {
names: HashMap<String, usize>,
themes: Vec<DynTheme<DS>>,
}
impl<DS> MultiTheme<DS> {
pub fn builder() -> MultiThemeBuilder<DS> {
MultiThemeBuilder {
names: HashMap::new(),
themes: vec![],
}
}
}
impl<DS> MultiThemeBuilder<DS> {
#[must_use]
pub fn add<S: ToString, T>(mut self, name: S, theme: T) -> Self
where
DS: DrawSharedImpl,
T: ThemeDst<DS> + 'static,
{
let index = self.themes.len();
self.names.insert(name.to_string(), index);
self.themes.push(Box::new(theme));
self
}
pub fn try_build(self) -> Option<MultiTheme<DS>> {
if self.themes.is_empty() {
return None;
}
Some(MultiTheme {
names: self.names,
themes: self.themes,
active: 0,
})
}
pub fn build(self) -> MultiTheme<DS> {
self.try_build()
.unwrap_or_else(|| panic!("MultiThemeBuilder: no themes added"))
}
}
impl<DS: DrawSharedImpl> Theme<DS> for MultiTheme<DS> {
type Config = Config;
type Window = Box<dyn Window>;
type Draw<'a> = Box<dyn ThemeDraw + 'a>;
fn config(&self) -> std::borrow::Cow<Self::Config> {
let boxed_config = self.themes[self.active].config();
let config: Config = boxed_config
.as_ref()
.downcast_ref::<Config>()
.unwrap()
.clone();
std::borrow::Cow::Owned(config)
}
fn apply_config(&mut self, config: &Self::Config) -> TkAction {
let mut action = TkAction::empty();
for theme in &mut self.themes {
action |= theme.apply_config(config);
}
action
}
fn init(&mut self, shared: &mut SharedState<DS>) {
for theme in &mut self.themes {
theme.init(shared);
}
}
fn new_window(&self, dpi_factor: f32) -> Self::Window {
self.themes[self.active].new_window(dpi_factor)
}
fn update_window(&self, window: &mut Self::Window, dpi_factor: f32) {
self.themes[self.active].update_window(window, dpi_factor);
}
fn draw<'a>(
&'a self,
draw: DrawIface<'a, DS>,
ev: &'a mut EventState,
window: &'a mut Self::Window,
) -> Box<dyn ThemeDraw + 'a> {
self.themes[self.active].draw(draw, ev, window)
}
fn clear_color(&self) -> color::Rgba {
self.themes[self.active].clear_color()
}
}
impl<DS> ThemeControl for MultiTheme<DS> {
fn set_font_size(&mut self, size: f32) -> TkAction {
let mut action = TkAction::empty();
for theme in &mut self.themes {
action = action.max(theme.set_font_size(size));
}
action
}
fn set_scheme(&mut self, scheme: &str) -> TkAction {
let mut action = TkAction::empty();
for theme in &mut self.themes {
action = action.max(theme.set_scheme(scheme));
}
action
}
fn list_schemes(&self) -> Vec<&str> {
self.themes[self.active].list_schemes()
}
fn set_theme(&mut self, theme: &str) -> TkAction {
if let Some(index) = self.names.get(theme).cloned() {
if index != self.active {
self.active = index;
return TkAction::RESIZE | TkAction::THEME_UPDATE;
}
}
TkAction::empty()
}
}