1use core::ffi::c_void;
7
8use azul_core::{
9 callbacks::{TimerCallbackReturn, Update},
10 dom::OptionDomNodeId,
11 geom::{LogicalPosition, LogicalSize, OptionLogicalPosition},
12 menu::Menu,
13 refany::{OptionRefAny, RefAny},
14 resources::ImageRef,
15 task::{
16 Duration, GetSystemTimeCallback, Instant, OptionDuration, OptionInstant, TerminateTimer,
17 ThreadId, TimerId,
18 },
19 window::{KeyboardState, MouseState, WindowFlags},
20};
21
22use azul_css::AzString;
23
24use crate::{
25 callbacks::CallbackInfo,
26 thread::Thread,
27 window_state::{FullWindowState, WindowCreateOptions},
28};
29
30pub type TimerCallbackType = extern "C" fn(
32 RefAny,
33 TimerCallbackInfo,
34) -> TimerCallbackReturn;
35
36#[repr(C)]
38pub struct TimerCallback {
39 pub cb: TimerCallbackType,
40 pub ctx: OptionRefAny,
43}
44
45impl TimerCallback {
46 pub fn create(cb: TimerCallbackType) -> Self {
47 Self {
48 cb,
49 ctx: OptionRefAny::None,
50 }
51 }
52}
53
54impl core::fmt::Debug for TimerCallback {
55 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
56 write!(f, "TimerCallback {{ cb: {:p} }}", self.cb as *const ())
57 }
58}
59
60impl Clone for TimerCallback {
61 fn clone(&self) -> Self {
62 Self {
63 cb: self.cb,
64 ctx: self.ctx.clone(),
65 }
66 }
67}
68
69impl From<TimerCallbackType> for TimerCallback {
70 fn from(cb: TimerCallbackType) -> Self {
71 Self {
72 cb,
73 ctx: OptionRefAny::None,
74 }
75 }
76}
77
78impl PartialEq for TimerCallback {
79 fn eq(&self, other: &Self) -> bool {
80 self.cb as usize == other.cb as usize
81 }
82}
83
84impl Eq for TimerCallback {}
85
86impl PartialOrd for TimerCallback {
87 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
88 (self.cb as usize).partial_cmp(&(other.cb as usize))
89 }
90}
91
92impl Ord for TimerCallback {
93 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
94 (self.cb as usize).cmp(&(other.cb as usize))
95 }
96}
97
98impl core::hash::Hash for TimerCallback {
99 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
100 (self.cb as usize).hash(state);
101 }
102}
103
104#[derive(Debug, Clone, PartialEq, Eq, Hash)]
106#[repr(C)]
107pub struct Timer {
108 pub refany: RefAny,
109 pub node_id: OptionDomNodeId,
110 pub created: Instant,
111 pub last_run: OptionInstant,
112 pub run_count: usize,
113 pub delay: OptionDuration,
114 pub interval: OptionDuration,
115 pub timeout: OptionDuration,
116 pub callback: TimerCallback,
117}
118
119impl Timer {
120 pub fn create<C: Into<TimerCallback>>(
121 refany: RefAny,
122 callback: C,
123 get_system_time_fn: GetSystemTimeCallback,
124 ) -> Self {
125 Timer {
126 refany,
127 node_id: None.into(),
128 created: (get_system_time_fn.cb)(),
129 run_count: 0,
130 last_run: OptionInstant::None,
131 delay: OptionDuration::None,
132 interval: OptionDuration::None,
133 timeout: OptionDuration::None,
134 callback: callback.into(),
135 }
136 }
137
138 pub fn tick_millis(&self) -> u64 {
139 match self.interval.as_ref() {
140 Some(Duration::System(s)) => s.millis(),
141 Some(Duration::Tick(s)) => s.tick_diff,
142 None => 10,
143 }
144 }
145
146 pub fn is_about_to_finish(&self, instant_now: &Instant) -> bool {
147 let mut finish = false;
148 if let OptionDuration::Some(timeout) = self.timeout {
149 finish = instant_now
150 .duration_since(&self.created)
151 .greater_than(&timeout);
152 }
153 finish
154 }
155
156 pub fn instant_of_next_run(&self) -> Instant {
157 let last_run = match self.last_run.as_ref() {
158 Some(s) => s,
159 None => &self.created,
160 };
161
162 last_run
163 .clone()
164 .add_optional_duration(self.delay.as_ref())
165 .add_optional_duration(self.interval.as_ref())
166 }
167
168 #[inline]
169 pub fn with_delay(mut self, delay: Duration) -> Self {
170 self.delay = OptionDuration::Some(delay);
171 self
172 }
173
174 #[inline]
175 pub fn with_interval(mut self, interval: Duration) -> Self {
176 self.interval = OptionDuration::Some(interval);
177 self
178 }
179
180 #[inline]
181 pub fn with_timeout(mut self, timeout: Duration) -> Self {
182 self.timeout = OptionDuration::Some(timeout);
183 self
184 }
185
186 pub fn invoke(
191 &mut self,
192 callback_info: &CallbackInfo,
193 get_system_time_fn: &GetSystemTimeCallback,
194 ) -> TimerCallbackReturn {
195 let now = (get_system_time_fn.cb)();
196
197 match self.last_run.as_ref() {
199 Some(last_run) => {
200 if let OptionDuration::Some(interval) = self.interval {
202 if now.duration_since(last_run).smaller_than(&interval) {
203 return TimerCallbackReturn {
204 should_update: Update::DoNothing,
205 should_terminate: TerminateTimer::Continue,
206 };
207 }
208 }
209 }
210 None => {
211 if let OptionDuration::Some(delay) = self.delay {
213 if now.duration_since(&self.created).smaller_than(&delay) {
214 return TimerCallbackReturn {
215 should_update: Update::DoNothing,
216 should_terminate: TerminateTimer::Continue,
217 };
218 }
219 }
220 }
221 }
222
223 let is_about_to_finish = self.is_about_to_finish(&now);
224
225 let mut timer_callback_info = TimerCallbackInfo {
228 callback_info: *callback_info,
229 node_id: self.node_id,
230 frame_start: now.clone(),
231 call_count: self.run_count,
232 is_about_to_finish,
233 _abi_ref: core::ptr::null(),
234 _abi_mut: core::ptr::null_mut(),
235 };
236
237 let mut result = (self.callback.cb)(self.refany.clone(), timer_callback_info);
238
239 if is_about_to_finish {
240 result.should_terminate = TerminateTimer::Terminate;
241 }
242
243 self.run_count += 1;
244 self.last_run = OptionInstant::Some(now);
245
246 result
247 }
248}
249
250impl Default for Timer {
251 fn default() -> Self {
252 extern "C" fn default_callback(_: RefAny, _: TimerCallbackInfo) -> TimerCallbackReturn {
253 TimerCallbackReturn::terminate_unchanged()
254 }
255
256 extern "C" fn default_time() -> Instant {
257 Instant::Tick(azul_core::task::SystemTick { tick_counter: 0 })
258 }
259
260 Timer::create(
261 RefAny::new(()),
262 default_callback as TimerCallbackType,
263 GetSystemTimeCallback { cb: default_time },
264 )
265 }
266}
267
268#[derive(Clone)]
274#[repr(C)]
275pub struct TimerCallbackInfo {
276 pub callback_info: CallbackInfo,
277 pub node_id: OptionDomNodeId,
278 pub frame_start: Instant,
279 pub call_count: usize,
280 pub is_about_to_finish: bool,
281 pub _abi_ref: *const c_void,
282 pub _abi_mut: *mut c_void,
283}
284
285impl TimerCallbackInfo {
286 pub fn create(
287 callback_info: CallbackInfo,
288 node_id: OptionDomNodeId,
289 frame_start: Instant,
290 call_count: usize,
291 is_about_to_finish: bool,
292 ) -> Self {
293 Self {
294 callback_info,
295 node_id,
296 frame_start,
297 call_count,
298 is_about_to_finish,
299 _abi_ref: core::ptr::null(),
300 _abi_mut: core::ptr::null_mut(),
301 }
302 }
303
304 pub fn get_attached_node_size(&self) -> Option<LogicalSize> {
305 let node_id = self.node_id.into_option()?;
306 self.callback_info.get_node_size(node_id)
307 }
308
309 pub fn get_attached_node_position(&self) -> Option<azul_core::geom::LogicalPosition> {
310 let node_id = self.node_id.into_option()?;
311 self.callback_info.get_node_position(node_id)
312 }
313
314 pub fn get_callback_info(&self) -> &CallbackInfo {
315 &self.callback_info
316 }
317
318 pub fn get_callback_info_mut(&mut self) -> &mut CallbackInfo {
319 &mut self.callback_info
320 }
321
322 pub fn get_ctx(&self) -> OptionRefAny {
328 self.callback_info.get_ctx()
329 }
330
331 pub fn add_timer(&mut self, timer_id: TimerId, timer: Timer) {
333 self.callback_info.add_timer(timer_id, timer);
334 }
335
336 pub fn remove_timer(&mut self, timer_id: TimerId) {
338 self.callback_info.remove_timer(timer_id);
339 }
340
341 pub fn add_thread(&mut self, thread_id: ThreadId, thread: Thread) {
343 self.callback_info.add_thread(thread_id, thread);
344 }
345
346 pub fn remove_thread(&mut self, thread_id: ThreadId) {
348 self.callback_info.remove_thread(thread_id);
349 }
350
351 pub fn stop_propagation(&mut self) {
353 self.callback_info.stop_propagation();
354 }
355
356 pub fn create_window(&mut self, options: WindowCreateOptions) {
358 self.callback_info.create_window(options);
359 }
360
361 pub fn close_window(&mut self) {
363 self.callback_info.close_window();
364 }
365
366 pub fn modify_window_state(&mut self, state: FullWindowState) {
368 self.callback_info.modify_window_state(state);
369 }
370
371 pub fn add_image_to_cache(&mut self, id: AzString, image: ImageRef) {
373 self.callback_info.add_image_to_cache(id, image);
374 }
375
376 pub fn remove_image_from_cache(&mut self, id: AzString) {
378 self.callback_info.remove_image_from_cache(id);
379 }
380
381 pub fn reload_system_fonts(&mut self) {
383 self.callback_info.reload_system_fonts();
384 }
385
386 pub fn prevent_default(&mut self) {
388 self.callback_info.prevent_default();
389 }
390
391 pub fn open_menu(&mut self, menu: Menu) {
393 self.callback_info.open_menu(menu);
394 }
395
396 pub fn open_menu_at(&mut self, menu: Menu, position: LogicalPosition) {
398 self.callback_info.open_menu_at(menu, position);
399 }
400
401 pub fn show_tooltip(&mut self, text: AzString) {
403 self.callback_info.show_tooltip(text);
404 }
405
406 pub fn show_tooltip_at(&mut self, text: AzString, position: LogicalPosition) {
408 self.callback_info.show_tooltip_at(text, position);
409 }
410
411 pub fn hide_tooltip(&mut self) {
413 self.callback_info.hide_tooltip();
414 }
415
416 pub fn open_menu_for_hit_node(&mut self, menu: Menu) -> bool {
418 self.callback_info.open_menu_for_hit_node(menu)
419 }
420
421 pub fn get_current_window_flags(&self) -> WindowFlags {
423 self.callback_info.get_current_window_flags()
424 }
425
426 pub fn get_current_keyboard_state(&self) -> KeyboardState {
428 self.callback_info.get_current_keyboard_state()
429 }
430
431 pub fn get_current_mouse_state(&self) -> MouseState {
433 self.callback_info.get_current_mouse_state()
434 }
435
436 pub fn get_cursor_relative_to_node(&self) -> OptionLogicalPosition {
438 self.callback_info.get_cursor_relative_to_node()
439 }
440
441 pub fn get_cursor_relative_to_viewport(&self) -> OptionLogicalPosition {
443 self.callback_info.get_cursor_relative_to_viewport()
444 }
445
446 pub fn get_cursor_position(&self) -> Option<LogicalPosition> {
448 self.callback_info.get_cursor_position()
449 }
450
451 pub fn get_current_time(&self) -> Instant {
453 self.frame_start.clone()
454 }
455
456 pub fn is_dom_focused(&self) -> bool {
458 true }
461
462 pub fn is_pen_in_contact(&self) -> bool {
464 false }
466
467 pub fn is_pen_eraser(&self) -> bool {
469 false }
471
472 pub fn is_pen_barrel_button_pressed(&self) -> bool {
474 false }
476
477 pub fn is_dragging(&self) -> bool {
479 self.callback_info.get_current_mouse_state().left_down
480 }
481
482 pub fn is_drag_active(&self) -> bool {
484 self.callback_info.get_current_mouse_state().left_down
485 }
486
487 pub fn is_node_drag_active(&self) -> bool {
489 self.callback_info.get_current_mouse_state().left_down
490 }
491
492 pub fn is_file_drag_active(&self) -> bool {
494 false }
496
497 pub fn has_sufficient_history_for_gestures(&self) -> bool {
499 false }
501
502 pub fn set_cursor_visibility(&mut self, visible: bool) {
506 self.callback_info.set_cursor_visibility(visible);
507 }
508
509 pub fn set_cursor_visibility_toggle(&mut self) {
514 use crate::callbacks::CallbackChange;
517 self.callback_info.push_change(CallbackChange::SetCursorVisibility { visible: true });
523 }
524
525 pub fn reset_cursor_blink(&mut self) {
527 self.callback_info.reset_cursor_blink();
528 }
529}
530
531pub fn invoke_timer(
533 timer: &mut Timer,
534 callback_info: CallbackInfo,
535 frame_start: Instant,
536 get_system_time_fn: GetSystemTimeCallback,
537) -> TimerCallbackReturn {
538 let instant_now = (get_system_time_fn.cb)();
539
540 match timer.last_run.as_ref() {
542 Some(last_run) => {
543 if let OptionDuration::Some(interval) = timer.interval {
545 if instant_now.duration_since(last_run).smaller_than(&interval) {
546 return TimerCallbackReturn {
547 should_update: Update::DoNothing,
548 should_terminate: TerminateTimer::Continue,
549 };
550 }
551 }
552 }
553 None => {
554 if let OptionDuration::Some(delay) = timer.delay {
556 if instant_now
557 .duration_since(&timer.created)
558 .smaller_than(&delay)
559 {
560 return TimerCallbackReturn {
561 should_update: Update::DoNothing,
562 should_terminate: TerminateTimer::Continue,
563 };
564 }
565 }
566 }
567 }
568
569 let run_count = timer.run_count;
570 let is_about_to_finish = timer.is_about_to_finish(&instant_now);
571 let mut timer_callback_info = TimerCallbackInfo {
572 callback_info,
573 node_id: timer.node_id,
574 frame_start,
575 call_count: run_count,
576 is_about_to_finish,
577 _abi_ref: core::ptr::null(),
578 _abi_mut: core::ptr::null_mut(),
579 };
580 let mut res = (timer.callback.cb)(timer.refany.clone(), timer_callback_info);
581
582 if is_about_to_finish {
583 res.should_terminate = TerminateTimer::Terminate;
584 }
585
586 timer.last_run = OptionInstant::Some(instant_now);
587 timer.run_count += 1;
588
589 res
590}
591
592#[derive(Debug, Clone)]
594#[repr(C, u8)]
595pub enum OptionTimer {
596 None,
597 Some(Timer),
598}
599
600impl From<Option<Timer>> for OptionTimer {
601 fn from(o: Option<Timer>) -> Self {
602 match o {
603 None => OptionTimer::None,
604 Some(t) => OptionTimer::Some(t),
605 }
606 }
607}
608
609impl OptionTimer {
610 pub fn into_option(self) -> Option<Timer> {
611 match self {
612 OptionTimer::None => None,
613 OptionTimer::Some(t) => Some(t),
614 }
615 }
616}