use std::collections::HashMap;
#[cfg(feature = "unsize")]
use std::marker::Unsize;
use crate::{Config, StackDst, Theme, ThemeDst, Window};
use kas::draw::{color, DrawHandle, DrawIface, DrawSharedImpl, SharedState, ThemeApi};
use kas::TkAction;
#[cfg(feature = "unsize")]
type DynTheme<DS> = StackDst<dyn ThemeDst<DS>>;
#[cfg(not(feature = "unsize"))]
type DynTheme<DS> = Box<dyn ThemeDst<DS>>;
#[cfg_attr(doc_cfg, doc(cfg(feature = "stack_dst")))]
pub struct MultiTheme<DS> {
names: HashMap<String, usize>,
themes: Vec<DynTheme<DS>>,
active: usize,
}
#[cfg_attr(doc_cfg, doc(cfg(feature = "stack_dst")))]
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> {
#[cfg(feature = "unsize")]
pub fn add<S: ToString, U>(mut self, name: S, theme: U) -> Self
where
U: Unsize<dyn ThemeDst<DS>>,
Box<U>: Unsize<dyn ThemeDst<DS>>,
{
let index = self.themes.len();
self.names.insert(name.to_string(), index);
self.themes.push(StackDst::new_or_boxed(theme));
self
}
#[cfg(not(feature = "unsize"))]
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 = StackDst<dyn Window>;
#[cfg(not(feature = "gat"))]
type DrawHandle = StackDst<dyn DrawHandle>;
#[cfg(feature = "gat")]
type DrawHandle<'a> = StackDst<dyn DrawHandle + '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);
}
#[cfg(not(feature = "gat"))]
unsafe fn draw_handle(
&self,
draw: DrawIface<DS>,
window: &mut Self::Window,
) -> StackDst<dyn DrawHandle> {
unsafe fn extend_lifetime_mut<'b, T: ?Sized>(r: &'b mut T) -> &'static mut T {
std::mem::transmute::<&'b mut T, &'static mut T>(r)
}
self.themes[self.active].draw_handle(
DrawIface {
draw: extend_lifetime_mut(draw.draw),
shared: extend_lifetime_mut(draw.shared),
pass: draw.pass,
},
extend_lifetime_mut(window),
)
}
#[cfg(feature = "gat")]
fn draw_handle<'a>(
&'a self,
draw: DrawIface<'a, DS>,
window: &'a mut Self::Window,
) -> StackDst<dyn DrawHandle + 'a> {
self.themes[self.active].draw_handle(draw, window)
}
fn clear_color(&self) -> color::Rgba {
self.themes[self.active].clear_color()
}
}
impl<DS> ThemeApi 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()
}
}