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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
use crate::*;
use euclid::*;
use std::any::Any;
use std::collections::HashMap;

pub type LocalSpace = vger::defs::LocalSpace;
pub type WorldSpace = vger::defs::WorldSpace;
pub type LocalRect = Rect<f32, LocalSpace>;
pub type LocalOffset = Vector2D<f32, LocalSpace>;
pub type LocalSize = Size2D<f32, LocalSpace>;
pub type LocalPoint = Point2D<f32, LocalSpace>;
pub type WorldRect = Rect<f32, WorldSpace>;
pub type WorldPoint = Point2D<f32, WorldSpace>;

use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use std::sync::{Arc, Mutex};

use tao::event_loop::EventLoopProxy;
use tao::window::Window;

/// `ViewID` is a unique identifier for a view. We're using a u64 and hashing
/// under the assumption there won't be collsions. The underlying u64 is a function
/// of the path down the view tree.
#[derive(Copy, Clone, Default, Eq, PartialEq, Hash, Debug)]
pub struct ViewID {
    id: u64,
}

impl ViewID {
    /// Computes the ID for a child using a hashable value. For views
    /// which don't have dynamic children (e.g. `vstack` etc.) the value
    /// will be the integer index of the child. Dynamic
    /// views (e.g. `list`) will hash an item identifier.
    pub fn child<T: Hash>(&self, value: &T) -> Self {
        let mut hasher = DefaultHasher::new();
        hasher.write_u64(self.id);
        value.hash(&mut hasher);
        Self {
            id: hasher.finish(),
        }
    }

    pub fn access_id(&self) -> accesskit::NodeId {
        accesskit::NodeId(std::num::NonZeroU64::new(self.id).unwrap())
    }
}

pub const DEBUG_LAYOUT: bool = false;

#[derive(Copy, Clone, Default, PartialEq, Debug)]
pub(crate) struct LayoutBox {
    pub rect: LocalRect,
    pub offset: LocalOffset,
}

// This could use a better name.
pub struct Dirty {
    pub dirty: bool,
    pub event_loop_proxy: Option<EventLoopProxy<()>>,
}

impl Dirty {
    pub fn new(event_loop_proxy: Option<EventLoopProxy<()>>) -> Self {
        Dirty {
            dirty: false,
            event_loop_proxy,
        }
    }
}

/// Restricts what we can store in a StateMap (instead of just using Any)
pub trait AnyState {
    /// So we can downcast.
    fn as_any(&self) -> &dyn Any;
}

pub type StateMap = HashMap<ViewID, Box<dyn AnyState>>;

/// The Context stores all UI state. A user of the library
/// shouldn't have to interact with it directly.
pub struct Context {
    /// Map for `state`.
    pub(crate) state_map: StateMap,

    /// Layout information for all views.
    pub(crate) layout: HashMap<ViewID, LayoutBox>,

    /// Which views each touch (or mouse pointer) is interacting with.
    pub(crate) touches: [ViewID; 16],

    /// Points at which touches (or click-drags) started.
    pub(crate) starts: [LocalPoint; 16],

    /// Previous touch/mouse positions.
    pub(crate) previous_position: [LocalPoint; 16],

    /// The root view ID. This should be randomized for security reasons.
    pub(crate) root_id: ViewID,

    /// The view that has the keybord focus.
    pub(crate) focused_id: Option<ViewID>,

    /// Did state change?
    pub(crate) dirty: Arc<Mutex<Dirty>>,

    /// The tao window
    pub(crate) window: Window,

    /// The current title of the window
    pub(crate) window_title: String,
}

impl Context {
    pub fn new(event_loop_proxy: Option<EventLoopProxy<()>>, window: Window) -> Self {
        Self {
            state_map: HashMap::new(),
            layout: HashMap::new(),
            touches: [ViewID::default(); 16],
            starts: [LocalPoint::zero(); 16],
            previous_position: [LocalPoint::zero(); 16],
            root_id: ViewID::default(),
            focused_id: None,
            dirty: Arc::new(Mutex::new(Dirty::new(event_loop_proxy))),
            window,
            window_title: "rui".into(),
        }
    }

    pub fn with_state<S: Clone + 'static, R, F: Fn(State<S>, &mut Self) -> R>(
        &mut self,
        default: S,
        id: ViewID,
        f: F,
    ) -> R {
        let d = self.dirty.clone();
        let s = self
            .state_map
            .entry(id)
            .or_insert_with(|| Box::new(State::new(default, d)));

        if let Some(state) = s.as_any().downcast_ref::<State<S>>() {
            f(state.clone(), self)
        } else {
            panic!("state has wrong type")
        }
    }

    pub fn with_state_mut<S: Clone + 'static, R, F: FnMut(State<S>, &mut Self) -> R>(
        &mut self,
        default: S,
        id: ViewID,
        f: &mut F,
    ) -> R {
        let d = self.dirty.clone();
        let s = self
            .state_map
            .entry(id)
            .or_insert_with(|| Box::new(State::new(default, d)));

        if let Some(state) = s.as_any().downcast_ref::<State<S>>() {
            f(state.clone(), self)
        } else {
            panic!("state has wrong type")
        }
    }

    pub fn with_state_aux<S: Clone + 'static, T, R, F: Fn(State<S>, &mut Self, &mut T) -> R>(
        &mut self,
        default: S,
        id: ViewID,
        aux: &mut T,
        f: F,
    ) -> R {
        let d = self.dirty.clone();
        let s = self
            .state_map
            .entry(id)
            .or_insert_with(|| Box::new(State::new(default, d)));

        if let Some(state) = s.as_any().downcast_ref::<State<S>>() {
            f(state.clone(), self, aux)
        } else {
            panic!("state has wrong type")
        }
    }
}