1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License in the LICENSE-APACHE file or at:
//     https://www.apache.org/licenses/LICENSE-2.0

//! Stack-DST versions of theme traits

use std::any::Any;
use std::borrow::Cow;
use std::ops::{Deref, DerefMut};

use super::{Theme, Window};
use kas::draw::{color, DrawIface, DrawSharedImpl, SharedState};
use kas::event::EventState;
use kas::theme::{ThemeControl, ThemeDraw, ThemeSize};
use kas::TkAction;

/// An optionally-owning (boxed) reference
///
/// This is related but not identical to [`Cow`].
pub enum MaybeBoxed<'a, B: 'a + ?Sized> {
    Borrowed(&'a B),
    Boxed(Box<B>),
}

impl<T: ?Sized> AsRef<T> for MaybeBoxed<'_, T> {
    fn as_ref(&self) -> &T {
        match self {
            MaybeBoxed::Borrowed(r) => r,
            MaybeBoxed::Boxed(b) => b.as_ref(),
        }
    }
}

/// As [`Theme`], but without associated types
///
/// This trait is implemented automatically for all implementations of
/// [`Theme`]. It is intended only for use where a less parameterised
/// trait is required.
pub trait ThemeDst<DS: DrawSharedImpl>: ThemeControl {
    /// Get current configuration
    fn config(&self) -> MaybeBoxed<dyn Any>;

    /// Apply/set the passed config
    fn apply_config(&mut self, config: &dyn Any) -> TkAction;

    /// Theme initialisation
    ///
    /// See also [`Theme::init`].
    fn init(&mut self, shared: &mut SharedState<DS>);

    /// Construct per-window storage
    ///
    /// See also [`Theme::new_window`].
    fn new_window(&self, dpi_factor: f32) -> Box<dyn Window>;

    /// Update a window created by [`Theme::new_window`]
    ///
    /// See also [`Theme::update_window`].
    fn update_window(&self, window: &mut dyn Window, dpi_factor: f32);

    fn draw<'a>(
        &'a self,
        draw: DrawIface<'a, DS>,
        ev: &'a mut EventState,
        window: &'a mut dyn Window,
    ) -> Box<dyn ThemeDraw + 'a>;

    /// Background colour
    ///
    /// See also [`Theme::clear_color`].
    fn clear_color(&self) -> color::Rgba;
}

impl<DS: DrawSharedImpl, T: Theme<DS>> ThemeDst<DS> for T {
    fn config(&self) -> MaybeBoxed<dyn Any> {
        match self.config() {
            Cow::Borrowed(config) => MaybeBoxed::Borrowed(config),
            Cow::Owned(config) => MaybeBoxed::Boxed(Box::new(config)),
        }
    }

    fn apply_config(&mut self, config: &dyn Any) -> TkAction {
        self.apply_config(config.downcast_ref().unwrap())
    }

    fn init(&mut self, shared: &mut SharedState<DS>) {
        self.init(shared);
    }

    fn new_window(&self, dpi_factor: f32) -> Box<dyn Window> {
        let window = <T as Theme<DS>>::new_window(self, dpi_factor);
        Box::new(window)
    }

    fn update_window(&self, window: &mut dyn Window, dpi_factor: f32) {
        let window = window.as_any_mut().downcast_mut().unwrap();
        self.update_window(window, dpi_factor);
    }

    fn draw<'b>(
        &'b self,
        draw: DrawIface<'b, DS>,
        ev: &'b mut EventState,
        window: &'b mut dyn Window,
    ) -> Box<dyn ThemeDraw + 'b> {
        let window = window.as_any_mut().downcast_mut().unwrap();
        Box::new(<T as Theme<DS>>::draw(self, draw, ev, window))
    }

    fn clear_color(&self) -> color::Rgba {
        self.clear_color()
    }
}

impl Window for Box<dyn Window> {
    fn size(&self) -> &dyn ThemeSize {
        self.deref().size()
    }

    fn as_any_mut(&mut self) -> &mut dyn Any {
        self.deref_mut().as_any_mut()
    }
}