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
use super::event::*;
use super::super::control::*;
use super::super::controller::*;
use binding::*;
use std::mem;
use std::sync::*;
///
/// Core UI session structures
///
pub struct UiSessionCore {
/// The sequential ID of the last wake for update event
last_update_id: u64,
/// The UI tree for the applicaiton
ui_tree: BindRef<Control>,
/// Functions to be called next time the core is updated
update_callbacks: Vec<Box<FnMut(&mut UiSessionCore) -> ()+Send>>
}
impl UiSessionCore {
///
/// Creates a new UI core
///
pub fn new(controller: Arc<Controller>) -> UiSessionCore {
// Assemble the UI for the controller
let ui_tree = assemble_ui(controller);
UiSessionCore {
last_update_id: 0,
ui_tree: ui_tree,
update_callbacks: vec![]
}
}
///
/// Retrieves the ID of the last update that was dispatched for this core
///
pub fn last_update_id(&self) -> u64 { self.last_update_id }
///
/// Retrieves a reference to the UI tree for the whole application
///
pub fn ui_tree(&self) -> BindRef<Control> { BindRef::clone(&self.ui_tree) }
///
/// Dispatches an event to the specified controller
///
pub fn dispatch_event(&mut self, events: Vec<UiEvent>, controller: &Controller) {
for event in events {
// Send the event to the controllers
match event {
UiEvent::Action(controller_path, event_name, action_parameter) => {
// Find the controller along this path
if controller_path.len() == 0 {
// Straight to the root controller
self.dispatch_action(controller, event_name, action_parameter);
} else {
// Controller along a path
let mut controller = controller.get_subcontroller(&controller_path[0]);
for controller_name in controller_path.into_iter().skip(1) {
controller = controller.map_or(None, move |ctrl| ctrl.get_subcontroller(&controller_name));
}
match controller {
Some(ref controller) => self.dispatch_action(&**controller, event_name, action_parameter),
None => () // TODO: event has disappeared into the void :-(
}
}
},
UiEvent::Tick => {
// Send a tick to this controller
self.dispatch_tick(controller);
}
}
}
// It might be time to wake anything waiting on the update stream
self.wake_for_updates();
}
///
/// Registers a function to be called next time the core is updated
///
pub fn on_next_update<Callback: 'static+FnOnce(&mut UiSessionCore) -> ()+Send>(&mut self, callback: Callback) {
// FnBox is in nightly, so here's our 'boxed FnOnce' workaround
let mut callback = Some(callback);
// Call the function when the next update occurs
self.update_callbacks.push(Box::new(move |session_core| {
// Swap out the preserved callback
let mut call_once = None;
mem::swap(&mut call_once, &mut callback);
// Call it if it hasn't been called before
if let Some(call_once) = call_once {
call_once(session_core);
}
}));
}
///
/// Wakes things up that might be waiting for updates
///
pub fn wake_for_updates(&mut self) {
// Update the last update ID
self.last_update_id += 1;
// Perform the callbacks
let mut callbacks = vec![];
mem::swap(&mut callbacks, &mut self.update_callbacks);
for mut callback in callbacks {
callback(self);
}
}
///
/// Dispatches an action to a controller
///
fn dispatch_action(&mut self, controller: &Controller, event_name: String, action_parameter: ActionParameter) {
controller.action(&event_name, &action_parameter);
}
///
/// Sends ticks to the specified controller and all its subcontrollers
///
fn dispatch_tick(&mut self, controller: &Controller) {
// Send ticks to the subcontrollers first
let ui = controller.ui().get();
let subcontrollers = ui.all_controllers();
for subcontroller_name in subcontrollers {
if let Some(subcontroller) = controller.get_subcontroller(&subcontroller_name) {
self.dispatch_tick(&*subcontroller);
}
}
// Send the tick to the controller
controller.tick();
}
}