1use core::ffi::c_void;
7
8use azul_core::{
9 callbacks::{TimerCallbackReturn, Update},
10 dom::{DomId, OptionDomNodeId},
11 geom::{LogicalPosition, LogicalSize, OptionLogicalPosition},
12 id::NodeId,
13 menu::Menu,
14 refany::{OptionRefAny, RefAny},
15 resources::ImageRef,
16 task::{
17 Duration, GetSystemTimeCallback, Instant, OptionDuration, OptionInstant, TerminateTimer,
18 ThreadId, TimerId,
19 },
20 window::{KeyboardState, MouseState, WindowFlags},
21};
22
23use azul_css::AzString;
24
25use crate::{
26 callbacks::CallbackInfo,
27 thread::Thread,
28 window_state::{FullWindowState, WindowCreateOptions},
29};
30
31const DEFAULT_TIMER_TICK_MS: u64 = 10;
33
34pub type TimerCallbackType = extern "C" fn(
36 RefAny,
37 TimerCallbackInfo,
38) -> TimerCallbackReturn;
39
40#[repr(C)]
42pub struct TimerCallback {
43 pub cb: TimerCallbackType,
44 pub ctx: OptionRefAny,
47}
48
49impl TimerCallback {
50 pub fn create(cb: TimerCallbackType) -> Self {
51 Self {
52 cb,
53 ctx: OptionRefAny::None,
54 }
55 }
56}
57
58impl core::fmt::Debug for TimerCallback {
59 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
60 write!(f, "TimerCallback {{ cb: {:p} }}", self.cb as *const ())
61 }
62}
63
64impl Clone for TimerCallback {
65 fn clone(&self) -> Self {
66 Self {
67 cb: self.cb,
68 ctx: self.ctx.clone(),
69 }
70 }
71}
72
73impl From<TimerCallbackType> for TimerCallback {
74 fn from(cb: TimerCallbackType) -> Self {
75 Self {
76 cb,
77 ctx: OptionRefAny::None,
78 }
79 }
80}
81
82impl PartialEq for TimerCallback {
83 fn eq(&self, other: &Self) -> bool {
84 self.cb as *const () as usize == other.cb as *const () as usize
85 }
86}
87
88impl Eq for TimerCallback {}
89
90impl PartialOrd for TimerCallback {
91 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
92 (self.cb as *const () as usize).partial_cmp(&(other.cb as *const () as usize))
93 }
94}
95
96impl Ord for TimerCallback {
97 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
98 (self.cb as *const () as usize).cmp(&(other.cb as *const () as usize))
99 }
100}
101
102impl core::hash::Hash for TimerCallback {
103 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
104 (self.cb as *const () as usize).hash(state);
105 }
106}
107
108#[derive(Debug, Clone, PartialEq, Eq, Hash)]
110#[repr(C)]
111pub struct Timer {
112 pub refany: RefAny,
113 pub node_id: OptionDomNodeId,
114 pub created: Instant,
115 pub last_run: OptionInstant,
116 pub run_count: usize,
117 pub delay: OptionDuration,
118 pub interval: OptionDuration,
119 pub timeout: OptionDuration,
120 pub callback: TimerCallback,
121}
122
123impl Timer {
124 pub fn create<C: Into<TimerCallback>>(
125 refany: RefAny,
126 callback: C,
127 get_system_time_fn: GetSystemTimeCallback,
128 ) -> Self {
129 Timer {
130 refany,
131 node_id: None.into(),
132 created: (get_system_time_fn.cb)(),
133 run_count: 0,
134 last_run: OptionInstant::None,
135 delay: OptionDuration::None,
136 interval: OptionDuration::None,
137 timeout: OptionDuration::None,
138 callback: callback.into(),
139 }
140 }
141
142 pub fn tick_millis(&self) -> u64 {
143 match self.interval.as_ref() {
144 Some(Duration::System(s)) => s.millis(),
145 Some(Duration::Tick(s)) => s.tick_diff,
146 None => DEFAULT_TIMER_TICK_MS,
147 }
148 }
149
150 pub fn is_about_to_finish(&self, instant_now: &Instant) -> bool {
151 match self.timeout {
152 OptionDuration::Some(timeout) => {
153 instant_now.duration_since(&self.created).greater_than(&timeout)
154 }
155 OptionDuration::None => false,
156 }
157 }
158
159 pub fn instant_of_next_run(&self) -> Instant {
160 let last_run = match self.last_run.as_ref() {
161 Some(s) => s,
162 None => &self.created,
163 };
164
165 last_run
166 .clone()
167 .add_optional_duration(self.delay.as_ref())
168 .add_optional_duration(self.interval.as_ref())
169 }
170
171 #[inline]
172 pub fn with_delay(mut self, delay: Duration) -> Self {
173 self.delay = OptionDuration::Some(delay);
174 self
175 }
176
177 #[inline]
178 pub fn with_interval(mut self, interval: Duration) -> Self {
179 self.interval = OptionDuration::Some(interval);
180 self
181 }
182
183 #[inline]
184 pub fn with_timeout(mut self, timeout: Duration) -> Self {
185 self.timeout = OptionDuration::Some(timeout);
186 self
187 }
188
189 pub fn invoke(
195 &mut self,
196 callback_info: &CallbackInfo,
197 get_system_time_fn: &GetSystemTimeCallback,
198 ) -> TimerCallbackReturn {
199 let now = (get_system_time_fn.cb)();
200
201 match self.last_run.as_ref() {
203 Some(last_run) => {
204 if let OptionDuration::Some(interval) = self.interval {
206 if now.duration_since(last_run).smaller_than(&interval) {
207 return TimerCallbackReturn {
208 should_update: Update::DoNothing,
209 should_terminate: TerminateTimer::Continue,
210 };
211 }
212 }
213 }
214 None => {
215 if let OptionDuration::Some(delay) = self.delay {
217 if now.duration_since(&self.created).smaller_than(&delay) {
218 return TimerCallbackReturn {
219 should_update: Update::DoNothing,
220 should_terminate: TerminateTimer::Continue,
221 };
222 }
223 }
224 }
225 }
226
227 let is_about_to_finish = self.is_about_to_finish(&now);
228
229 let mut timer_callback_info = TimerCallbackInfo {
232 callback_info: *callback_info,
233 node_id: self.node_id,
234 frame_start: now.clone(),
235 call_count: self.run_count,
236 is_about_to_finish,
237 _abi_ref: core::ptr::null(),
238 _abi_mut: core::ptr::null_mut(),
239 };
240
241 let mut result = (self.callback.cb)(self.refany.clone(), timer_callback_info);
242
243 if is_about_to_finish {
244 result.should_terminate = TerminateTimer::Terminate;
245 }
246
247 self.run_count += 1;
248 self.last_run = OptionInstant::Some(now);
249
250 result
251 }
252}
253
254impl Default for Timer {
255 fn default() -> Self {
256 extern "C" fn default_callback(_: RefAny, _: TimerCallbackInfo) -> TimerCallbackReturn {
257 TimerCallbackReturn::terminate_unchanged()
258 }
259
260 extern "C" fn default_time() -> Instant {
261 Instant::Tick(azul_core::task::SystemTick { tick_counter: 0 })
262 }
263
264 Timer::create(
265 RefAny::new(()),
266 default_callback as TimerCallbackType,
267 GetSystemTimeCallback { cb: default_time },
268 )
269 }
270}
271
272#[derive(Clone)]
277#[repr(C)]
278pub struct TimerCallbackInfo {
279 pub callback_info: CallbackInfo,
280 pub node_id: OptionDomNodeId,
281 pub frame_start: Instant,
282 pub call_count: usize,
283 pub is_about_to_finish: bool,
284 pub _abi_ref: *const c_void,
285 pub _abi_mut: *mut c_void,
286}
287
288impl TimerCallbackInfo {
289 pub fn create(
290 callback_info: CallbackInfo,
291 node_id: OptionDomNodeId,
292 frame_start: Instant,
293 call_count: usize,
294 is_about_to_finish: bool,
295 ) -> Self {
296 Self {
297 callback_info,
298 node_id,
299 frame_start,
300 call_count,
301 is_about_to_finish,
302 _abi_ref: core::ptr::null(),
303 _abi_mut: core::ptr::null_mut(),
304 }
305 }
306
307 pub fn get_attached_node_size(&self) -> Option<LogicalSize> {
308 let node_id = self.node_id.into_option()?;
309 self.callback_info.get_node_size(node_id)
310 }
311
312 pub fn get_attached_node_position(&self) -> Option<azul_core::geom::LogicalPosition> {
313 let node_id = self.node_id.into_option()?;
314 self.callback_info.get_node_position(node_id)
315 }
316
317 pub fn get_callback_info(&self) -> &CallbackInfo {
318 &self.callback_info
319 }
320
321 pub fn get_callback_info_mut(&mut self) -> &mut CallbackInfo {
322 &mut self.callback_info
323 }
324
325 pub fn get_ctx(&self) -> OptionRefAny {
331 self.callback_info.get_ctx()
332 }
333
334 pub fn add_timer(&mut self, timer_id: TimerId, timer: Timer) {
336 self.callback_info.add_timer(timer_id, timer);
337 }
338
339 pub fn remove_timer(&mut self, timer_id: TimerId) {
341 self.callback_info.remove_timer(timer_id);
342 }
343
344 pub fn add_thread(&mut self, thread_id: ThreadId, thread: Thread) {
346 self.callback_info.add_thread(thread_id, thread);
347 }
348
349 pub fn remove_thread(&mut self, thread_id: ThreadId) {
351 self.callback_info.remove_thread(thread_id);
352 }
353
354 pub fn stop_propagation(&mut self) {
356 self.callback_info.stop_propagation();
357 }
358
359 pub fn create_window(&mut self, options: WindowCreateOptions) {
361 self.callback_info.create_window(options);
362 }
363
364 pub fn close_window(&mut self) {
366 self.callback_info.close_window();
367 }
368
369 pub fn modify_window_state(&mut self, state: FullWindowState) {
371 self.callback_info.modify_window_state(state);
372 }
373
374 pub fn add_image_to_cache(&mut self, id: AzString, image: ImageRef) {
376 self.callback_info.add_image_to_cache(id, image);
377 }
378
379 pub fn remove_image_from_cache(&mut self, id: AzString) {
381 self.callback_info.remove_image_from_cache(id);
382 }
383
384 pub fn update_all_image_callbacks(&mut self) {
389 self.callback_info.update_all_image_callbacks();
390 }
391
392 pub fn trigger_virtual_view_rerender(&mut self, dom_id: DomId, node_id: NodeId) {
394 self.callback_info.trigger_virtual_view_rerender(dom_id, node_id);
395 }
396
397 pub fn reload_system_fonts(&mut self) {
399 self.callback_info.reload_system_fonts();
400 }
401
402 pub fn prevent_default(&mut self) {
404 self.callback_info.prevent_default();
405 }
406
407 pub fn open_menu(&mut self, menu: Menu) {
409 self.callback_info.open_menu(menu);
410 }
411
412 pub fn open_menu_at(&mut self, menu: Menu, position: LogicalPosition) {
414 self.callback_info.open_menu_at(menu, position);
415 }
416
417 pub fn show_tooltip(&mut self, text: AzString) {
419 self.callback_info.show_tooltip(text);
420 }
421
422 pub fn show_tooltip_at(&mut self, text: AzString, position: LogicalPosition) {
424 self.callback_info.show_tooltip_at(text, position);
425 }
426
427 pub fn hide_tooltip(&mut self) {
429 self.callback_info.hide_tooltip();
430 }
431
432 pub fn open_menu_for_hit_node(&mut self, menu: Menu) -> bool {
434 self.callback_info.open_menu_for_hit_node(menu)
435 }
436
437 pub fn get_current_window_flags(&self) -> WindowFlags {
439 self.callback_info.get_current_window_flags()
440 }
441
442 pub fn get_current_keyboard_state(&self) -> KeyboardState {
444 self.callback_info.get_current_keyboard_state()
445 }
446
447 pub fn get_current_mouse_state(&self) -> MouseState {
449 self.callback_info.get_current_mouse_state()
450 }
451
452 pub fn get_cursor_relative_to_node(&self) -> azul_core::geom::OptionCursorNodePosition {
454 self.callback_info.get_cursor_relative_to_node()
455 }
456
457 pub fn get_cursor_relative_to_viewport(&self) -> OptionLogicalPosition {
459 self.callback_info.get_cursor_relative_to_viewport()
460 }
461
462 pub fn get_cursor_position(&self) -> Option<LogicalPosition> {
464 self.callback_info.get_cursor_position()
465 }
466
467 pub fn get_current_time(&self) -> Instant {
469 self.frame_start.clone()
470 }
471
472 pub fn is_dom_focused(&self) -> bool {
474 true }
477
478 pub fn is_pen_in_contact(&self) -> bool {
480 false }
482
483 pub fn is_pen_eraser(&self) -> bool {
485 false }
487
488 pub fn is_pen_barrel_button_pressed(&self) -> bool {
490 false }
492
493 pub fn is_dragging(&self) -> bool {
495 self.callback_info.get_current_mouse_state().left_down
496 }
497
498 pub fn is_drag_active(&self) -> bool {
500 self.callback_info.get_current_mouse_state().left_down
501 }
502
503 pub fn is_node_drag_active(&self) -> bool {
505 self.callback_info.get_current_mouse_state().left_down
506 }
507
508 pub fn is_file_drag_active(&self) -> bool {
510 false }
512
513 pub fn has_sufficient_history_for_gestures(&self) -> bool {
515 false }
517
518 pub fn get_scroll_node_info(
524 &self,
525 dom_id: azul_core::dom::DomId,
526 node_id: azul_core::id::NodeId,
527 ) -> Option<crate::managers::scroll_state::ScrollNodeInfo> {
528 self.callback_info.get_scroll_node_info(dom_id, node_id)
529 }
530
531 pub fn find_scroll_parent(
536 &self,
537 dom_id: azul_core::dom::DomId,
538 node_id: azul_core::id::NodeId,
539 ) -> Option<azul_core::id::NodeId> {
540 self.callback_info.find_scroll_parent(dom_id, node_id)
541 }
542
543 #[cfg(feature = "std")]
548 pub fn get_scroll_input_queue(
549 &self,
550 ) -> crate::managers::scroll_state::ScrollInputQueue {
551 self.callback_info.get_scroll_input_queue()
552 }
553
554 pub fn scroll_to(
559 &mut self,
560 dom_id: azul_core::dom::DomId,
561 node_id: azul_core::styled_dom::NodeHierarchyItemId,
562 position: azul_core::geom::LogicalPosition,
563 ) {
564 self.callback_info.scroll_to(dom_id, node_id, position);
565 }
566
567 pub fn scroll_to_unclamped(
569 &mut self,
570 dom_id: azul_core::dom::DomId,
571 node_id: azul_core::styled_dom::NodeHierarchyItemId,
572 position: azul_core::geom::LogicalPosition,
573 ) {
574 self.callback_info.scroll_to_unclamped(dom_id, node_id, position);
575 }
576
577 pub fn set_cursor_visibility(&mut self, visible: bool) {
581 self.callback_info.set_cursor_visibility(visible);
582 }
583
584 pub fn set_cursor_visibility_toggle(&mut self) {
590 use crate::callbacks::CallbackChange;
591 self.callback_info.push_change(CallbackChange::SetCursorVisibility { visible: true });
593 }
594
595 pub fn reset_cursor_blink(&mut self) {
597 self.callback_info.reset_cursor_blink();
598 }
599}
600
601#[derive(Debug, Clone)]
603#[repr(C, u8)]
604pub enum OptionTimer {
605 None,
606 Some(Timer),
607}
608
609impl From<Option<Timer>> for OptionTimer {
610 fn from(o: Option<Timer>) -> Self {
611 match o {
612 None => OptionTimer::None,
613 Some(t) => OptionTimer::Some(t),
614 }
615 }
616}
617
618impl OptionTimer {
619 pub fn into_option(self) -> Option<Timer> {
620 match self {
621 OptionTimer::None => None,
622 OptionTimer::Some(t) => Some(t),
623 }
624 }
625}