azul_core/
callbacks.rs

1#![allow(dead_code)]
2
3#[cfg(not(feature = "std"))]
4use alloc::string::ToString;
5use alloc::{alloc::Layout, boxed::Box, collections::BTreeMap, vec::Vec};
6use core::{
7    ffi::c_void,
8    fmt,
9    sync::atomic::{AtomicUsize, Ordering as AtomicOrdering},
10};
11#[cfg(feature = "std")]
12use std::hash::Hash;
13
14use azul_css::{
15    AnimationInterpolationFunction, AzString, CssPath, CssProperty, CssPropertyType, FontRef,
16    InterpolateResolver, LayoutRect, LayoutSize,
17};
18use rust_fontconfig::{FcFontCache, FontSource};
19
20use crate::{
21    app_resources::{
22        FontInstanceKey, IdNamespace, ImageCache, ImageMask, ImageRef, LayoutedGlyphs,
23        RendererResources, ShapedWords, WordPositions, Words,
24    },
25    gl::OptionGlContextPtr,
26    id_tree::{NodeDataContainer, NodeId},
27    styled_dom::{
28        CssPropertyCache, DomId, NodeHierarchyItemId, NodeHierarchyItemVec, StyledDom, StyledNode,
29        StyledNodeVec,
30    },
31    task::{
32        CreateThreadCallback, Duration as AzDuration, ExternalSystemCallbacks,
33        GetSystemTimeCallback, Instant as AzInstant, Instant, TerminateTimer, Thread, ThreadId,
34        ThreadReceiver, ThreadSendMsg, ThreadSender, Timer, TimerId,
35    },
36    ui_solver::{
37        LayoutResult, OverflowingScrollNode, PositionInfo, PositionedRectangle,
38        ResolvedTextLayoutOptions, TextLayoutOptions,
39    },
40    window::{
41        AzStringPair, FullWindowState, KeyboardState, LogicalPosition, LogicalRect, LogicalSize,
42        MouseState, OptionChar, OptionLogicalPosition, PhysicalSize, RawWindowHandle,
43        UpdateFocusWarning, WindowCreateOptions, WindowFlags, WindowSize, WindowState, WindowTheme,
44    },
45    FastBTreeSet, FastHashMap,
46};
47
48// NOTE: must be repr(C), otherwise UB
49// due to zero-sized allocation in RefAny::new_c
50// TODO: fix later!
51#[repr(C)]
52pub struct Dummy {
53    pub _dummy: u8,
54}
55
56/// Specifies if the screen should be updated after the callback function has returned
57#[repr(C)]
58#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
59pub enum Update {
60    /// The screen does not need to redraw after the callback has been called
61    DoNothing,
62    /// After the callback is called, the screen needs to redraw (layout() function being called
63    /// again)
64    RefreshDom,
65    /// The layout has to be re-calculated for all windows
66    RefreshDomAllWindows,
67}
68
69impl Update {
70    pub fn max_self(&mut self, other: Self) {
71        if *self == Update::DoNothing && other != Update::DoNothing {
72            *self = other;
73        } else if *self == Update::RefreshDom && other == Update::RefreshDomAllWindows {
74            *self = other;
75        }
76    }
77}
78
79#[derive(Debug)]
80#[repr(C)]
81pub struct RefCountInner {
82    pub num_copies: AtomicUsize,
83    pub num_refs: AtomicUsize,
84    pub num_mutable_refs: AtomicUsize,
85    pub _internal_len: usize,
86    pub _internal_layout_size: usize,
87    pub _internal_layout_align: usize,
88    pub type_id: u64,
89    pub type_name: AzString,
90    pub custom_destructor: extern "C" fn(*mut c_void),
91}
92
93#[derive(Hash, PartialEq, PartialOrd, Ord, Eq)]
94#[repr(C)]
95pub struct RefCount {
96    pub ptr: *const RefCountInner,
97    pub run_destructor: bool,
98}
99
100impl fmt::Debug for RefCount {
101    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
102        self.downcast().fmt(f)
103    }
104}
105
106impl Clone for RefCount {
107    fn clone(&self) -> Self {
108        Self {
109            ptr: self.ptr,
110            run_destructor: true,
111        }
112    }
113}
114
115impl Drop for RefCount {
116    fn drop(&mut self) {
117        self.run_destructor = false;
118        // note: the owning struct of the RefCount has to do the dropping!
119    }
120}
121
122#[derive(Debug, Clone)]
123pub struct RefCountInnerDebug {
124    pub num_copies: usize,
125    pub num_refs: usize,
126    pub num_mutable_refs: usize,
127    pub _internal_len: usize,
128    pub _internal_layout_size: usize,
129    pub _internal_layout_align: usize,
130    pub type_id: u64,
131    pub type_name: AzString,
132    pub custom_destructor: usize,
133}
134
135impl RefCount {
136    fn new(ref_count: RefCountInner) -> Self {
137        RefCount {
138            ptr: Box::into_raw(Box::new(ref_count)),
139            run_destructor: true,
140        }
141    }
142    fn downcast(&self) -> &RefCountInner {
143        unsafe { &*self.ptr }
144    }
145
146    pub fn debug_get_refcount_copied(&self) -> RefCountInnerDebug {
147        let dc = self.downcast();
148        RefCountInnerDebug {
149            num_copies: dc.num_copies.load(AtomicOrdering::SeqCst),
150            num_refs: dc.num_refs.load(AtomicOrdering::SeqCst),
151            num_mutable_refs: dc.num_mutable_refs.load(AtomicOrdering::SeqCst),
152            _internal_len: dc._internal_len,
153            _internal_layout_size: dc._internal_layout_size,
154            _internal_layout_align: dc._internal_layout_align,
155            type_id: dc.type_id,
156            type_name: dc.type_name.clone(),
157            custom_destructor: dc.custom_destructor as usize,
158        }
159    }
160
161    /// Runtime check to check whether this `RefAny` can be borrowed
162    pub fn can_be_shared(&self) -> bool {
163        self.downcast()
164            .num_mutable_refs
165            .load(AtomicOrdering::SeqCst)
166            == 0
167    }
168
169    /// Runtime check to check whether this `RefAny` can be borrowed mutably
170    pub fn can_be_shared_mut(&self) -> bool {
171        let info = self.downcast();
172        info.num_mutable_refs.load(AtomicOrdering::SeqCst) == 0
173            && info.num_refs.load(AtomicOrdering::SeqCst) == 0
174    }
175
176    pub fn increase_ref(&self) {
177        self.downcast()
178            .num_refs
179            .fetch_add(1, AtomicOrdering::SeqCst);
180    }
181
182    pub fn decrease_ref(&self) {
183        self.downcast()
184            .num_refs
185            .fetch_sub(1, AtomicOrdering::SeqCst);
186    }
187
188    pub fn increase_refmut(&self) {
189        self.downcast()
190            .num_mutable_refs
191            .fetch_add(1, AtomicOrdering::SeqCst);
192    }
193
194    pub fn decrease_refmut(&self) {
195        self.downcast()
196            .num_mutable_refs
197            .fetch_sub(1, AtomicOrdering::SeqCst);
198    }
199}
200
201#[derive(Debug)]
202#[repr(C)]
203pub struct Ref<'a, T> {
204    ptr: &'a T,
205    sharing_info: RefCount,
206}
207
208impl<'a, T> Drop for Ref<'a, T> {
209    fn drop(&mut self) {
210        self.sharing_info.decrease_ref();
211    }
212}
213
214impl<'a, T> core::ops::Deref for Ref<'a, T> {
215    type Target = T;
216
217    fn deref(&self) -> &Self::Target {
218        self.ptr
219    }
220}
221
222#[derive(Debug)]
223#[repr(C)]
224pub struct RefMut<'a, T> {
225    ptr: &'a mut T,
226    sharing_info: RefCount,
227}
228
229impl<'a, T> Drop for RefMut<'a, T> {
230    fn drop(&mut self) {
231        self.sharing_info.decrease_refmut();
232    }
233}
234
235impl<'a, T> core::ops::Deref for RefMut<'a, T> {
236    type Target = T;
237
238    fn deref(&self) -> &Self::Target {
239        &*self.ptr
240    }
241}
242
243impl<'a, T> core::ops::DerefMut for RefMut<'a, T> {
244    fn deref_mut(&mut self) -> &mut Self::Target {
245        self.ptr
246    }
247}
248
249#[derive(Debug, Hash, PartialEq, PartialOrd, Ord, Eq)]
250#[repr(C)]
251pub struct RefAny {
252    /// void* to a boxed struct or enum of type "T". RefCount stores the RTTI
253    /// for this opaque type (can be downcasted by the user)
254    pub _internal_ptr: *const c_void,
255    /// All the metadata information is set on the refcount, so that the metadata
256    /// has to only be created once per object, not once per copy
257    pub sharing_info: RefCount,
258    /// Instance of this copy (root = 0th copy).
259    ///
260    /// Necessary to distinguish between the original copy and all other clones
261    pub instance_id: u64,
262    pub run_destructor: bool,
263}
264
265impl_option!(
266    RefAny,
267    OptionRefAny,
268    copy = false,
269    [Debug, Hash, Clone, PartialEq, PartialOrd, Ord, Eq]
270);
271
272// the refcount of RefAny is atomic, therefore `RefAny` is not `Sync`, but it is `Send`
273unsafe impl Send for RefAny {}
274// library-internal only - RefAny is not Sync outside of this library!
275unsafe impl Sync for RefAny {} // necessary for rayon to work
276
277impl RefAny {
278    /// Creates a new, type-erased pointer by casting the `T` value into a
279    /// `Vec<u8>` and saving the length + type ID
280    pub fn new<T: 'static>(value: T) -> Self {
281        extern "C" fn default_custom_destructor<U: 'static>(ptr: &mut c_void) {
282            use core::{mem, ptr};
283
284            // note: in the default constructor, we do not need to check whether U == T
285
286            unsafe {
287                // copy the struct from the heap to the stack and
288                // call mem::drop on U to run the destructor
289                let mut stack_mem = mem::MaybeUninit::<U>::uninit();
290                ptr::copy_nonoverlapping(
291                    (ptr as *mut c_void) as *const U,
292                    stack_mem.as_mut_ptr(),
293                    mem::size_of::<U>(),
294                );
295                let stack_mem = stack_mem.assume_init();
296                mem::drop(stack_mem);
297            }
298        }
299
300        let type_name = ::core::any::type_name::<T>();
301        let st = AzString::from_const_str(type_name);
302        let s = Self::new_c(
303            (&value as *const T) as *const c_void,
304            ::core::mem::size_of::<T>(),
305            Self::get_type_id_static::<T>(),
306            st,
307            default_custom_destructor::<T>,
308        );
309        ::core::mem::forget(value); // do not run the destructor of T here!
310        s
311    }
312
313    /// C-ABI compatible function to create a `RefAny` across the C boundary
314    pub fn new_c(
315        // *const T
316        ptr: *const c_void,
317        // sizeof(T)
318        len: usize,
319        // unique ID of the type (used for type comparison when downcasting)
320        type_id: u64,
321        // name of the class such as "app::MyData", usually compiler- or macro-generated
322        type_name: AzString,
323        custom_destructor: extern "C" fn(&mut c_void),
324    ) -> Self {
325        use core::ptr;
326
327        // special case: calling alloc() with 0 bytes would be undefined behaviour
328        //
329        // In order to invoke the destructor correctly, we need a 0-sized allocation
330        // on the heap (NOT nullptr, as this would lead to UB when calling the destructor)
331        let (_internal_ptr, layout) = if len == 0 {
332            let _dummy: [u8; 0] = [];
333            (ptr::null_mut(), Layout::for_value(&_dummy))
334        } else {
335            // cast the struct as bytes
336            let struct_as_bytes = unsafe { core::slice::from_raw_parts(ptr as *const u8, len) };
337            let layout = Layout::for_value(&*struct_as_bytes);
338
339            // allocate + copy the struct to the heap
340            let heap_struct_as_bytes = unsafe { alloc::alloc::alloc(layout) };
341            unsafe {
342                ptr::copy_nonoverlapping(
343                    struct_as_bytes.as_ptr(),
344                    heap_struct_as_bytes,
345                    struct_as_bytes.len(),
346                )
347            };
348            (heap_struct_as_bytes, layout)
349        };
350
351        let ref_count_inner = RefCountInner {
352            num_copies: AtomicUsize::new(1),
353            num_refs: AtomicUsize::new(0),
354            num_mutable_refs: AtomicUsize::new(0),
355            _internal_len: len,
356            _internal_layout_size: layout.size(),
357            _internal_layout_align: layout.align(),
358            type_id,
359            type_name,
360            // fn(&mut c_void) and fn(*mut c_void) are the same, so transmute is safe
361            custom_destructor: unsafe { core::mem::transmute(custom_destructor) },
362        };
363
364        Self {
365            _internal_ptr: _internal_ptr as *const c_void,
366            sharing_info: RefCount::new(ref_count_inner),
367            instance_id: 0,
368            run_destructor: true,
369        }
370    }
371
372    /// Returns whether this RefAny is the only instance
373    pub fn has_no_copies(&self) -> bool {
374        self.sharing_info
375            .downcast()
376            .num_copies
377            .load(AtomicOrdering::SeqCst)
378            == 1
379            && self
380                .sharing_info
381                .downcast()
382                .num_refs
383                .load(AtomicOrdering::SeqCst)
384                == 0
385            && self
386                .sharing_info
387                .downcast()
388                .num_mutable_refs
389                .load(AtomicOrdering::SeqCst)
390                == 0
391    }
392
393    /// Downcasts the type-erased pointer to a type `&U`, returns `None` if the types don't match
394    #[inline]
395    pub fn downcast_ref<'a, U: 'static>(&'a mut self) -> Option<Ref<'a, U>> {
396        let is_same_type = self.get_type_id() == Self::get_type_id_static::<U>();
397        if !is_same_type {
398            return None;
399        }
400
401        let can_be_shared = self.sharing_info.can_be_shared();
402        if !can_be_shared {
403            return None;
404        }
405
406        if self._internal_ptr.is_null() {
407            return None;
408        }
409        self.sharing_info.increase_ref();
410        Some(Ref {
411            ptr: unsafe { &*(self._internal_ptr as *const U) },
412            sharing_info: self.sharing_info.clone(),
413        })
414    }
415
416    /// Downcasts the type-erased pointer to a type `&mut U`, returns `None` if the types don't
417    /// match
418    #[inline]
419    pub fn downcast_mut<'a, U: 'static>(&'a mut self) -> Option<RefMut<'a, U>> {
420        let is_same_type = self.get_type_id() == Self::get_type_id_static::<U>();
421        if !is_same_type {
422            return None;
423        }
424
425        let can_be_shared_mut = self.sharing_info.can_be_shared_mut();
426        if !can_be_shared_mut {
427            return None;
428        }
429
430        if self._internal_ptr.is_null() {
431            return None;
432        }
433        self.sharing_info.increase_refmut();
434
435        Some(RefMut {
436            ptr: unsafe { &mut *(self._internal_ptr as *mut U) },
437            sharing_info: self.sharing_info.clone(),
438        })
439    }
440
441    // Returns the typeid of `T` as a u64 (necessary because
442    // `core::any::TypeId` is not C-ABI compatible)
443    #[inline]
444    fn get_type_id_static<T: 'static>() -> u64 {
445        use core::{any::TypeId, mem};
446
447        // fast method to serialize the type id into a u64
448        let t_id = TypeId::of::<T>();
449        let struct_as_bytes = unsafe {
450            core::slice::from_raw_parts(
451                (&t_id as *const TypeId) as *const u8,
452                mem::size_of::<TypeId>(),
453            )
454        };
455
456        struct_as_bytes
457            .into_iter()
458            .enumerate()
459            .map(|(s_pos, s)| ((*s as u64) << s_pos))
460            .sum()
461    }
462
463    /// Checks whether the typeids match
464    pub fn is_type(&self, type_id: u64) -> bool {
465        self.sharing_info.downcast().type_id == type_id
466    }
467
468    // Returns the internal type ID
469    pub fn get_type_id(&self) -> u64 {
470        self.sharing_info.downcast().type_id
471    }
472
473    // Returns the type name
474    pub fn get_type_name(&self) -> AzString {
475        self.sharing_info.downcast().type_name.clone()
476    }
477}
478
479impl Clone for RefAny {
480    fn clone(&self) -> Self {
481        self.sharing_info
482            .downcast()
483            .num_copies
484            .fetch_add(1, AtomicOrdering::SeqCst);
485        Self {
486            _internal_ptr: self._internal_ptr,
487            sharing_info: RefCount {
488                ptr: self.sharing_info.ptr,
489                run_destructor: true,
490            },
491            instance_id: self
492                .sharing_info
493                .downcast()
494                .num_copies
495                .load(AtomicOrdering::SeqCst) as u64,
496            run_destructor: true,
497        }
498    }
499}
500
501impl Drop for RefAny {
502    fn drop(&mut self) {
503        use core::ptr;
504
505        self.run_destructor = false;
506
507        let current_copies = self
508            .sharing_info
509            .downcast()
510            .num_copies
511            .fetch_sub(1, AtomicOrdering::SeqCst);
512
513        if current_copies != 1 {
514            return;
515        }
516
517        let sharing_info = unsafe { Box::from_raw(self.sharing_info.ptr as *mut RefCountInner) };
518        let sharing_info = *sharing_info; // sharing_info itself deallocates here
519
520        if sharing_info._internal_len == 0
521            || sharing_info._internal_layout_size == 0
522            || self._internal_ptr.is_null()
523        {
524            let mut _dummy: [u8; 0] = [];
525            (sharing_info.custom_destructor)(_dummy.as_ptr() as *mut c_void);
526        } else {
527            let layout = unsafe {
528                Layout::from_size_align_unchecked(
529                    sharing_info._internal_layout_size,
530                    sharing_info._internal_layout_align,
531                )
532            };
533
534            (sharing_info.custom_destructor)(self._internal_ptr as *mut c_void);
535            unsafe {
536                alloc::alloc::dealloc(self._internal_ptr as *mut u8, layout);
537            }
538        }
539    }
540}
541
542/// This type carries no valuable semantics for WR. However, it reflects the fact that
543/// clients (Servo) may generate pipelines by different semi-independent sources.
544/// These pipelines still belong to the same `IdNamespace` and the same `DocumentId`.
545/// Having this extra Id field enables them to generate `PipelineId` without collision.
546pub type PipelineSourceId = u32;
547
548/// Information about a scroll frame, given to the user by the framework
549#[derive(Debug, Clone, PartialEq, PartialOrd)]
550pub struct ScrollPosition {
551    /// How big is the parent container
552    /// (so that things like "scroll to left edge" can be implemented)?
553    pub parent_rect: LogicalRect,
554    /// How big is the scroll rect (i.e. the union of all children)?
555    pub children_rect: LogicalRect,
556}
557
558#[derive(Copy, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
559pub struct DocumentId {
560    pub namespace_id: IdNamespace,
561    pub id: u32,
562}
563
564impl ::core::fmt::Display for DocumentId {
565    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
566        write!(
567            f,
568            "DocumentId {{ ns: {}, id: {} }}",
569            self.namespace_id, self.id
570        )
571    }
572}
573
574impl ::core::fmt::Debug for DocumentId {
575    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
576        write!(f, "{}", self)
577    }
578}
579
580#[derive(Copy, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
581pub struct PipelineId(pub PipelineSourceId, pub u32);
582
583impl ::core::fmt::Display for PipelineId {
584    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
585        write!(f, "PipelineId({}, {})", self.0, self.1)
586    }
587}
588
589impl ::core::fmt::Debug for PipelineId {
590    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
591        write!(f, "{}", self)
592    }
593}
594
595static LAST_PIPELINE_ID: AtomicUsize = AtomicUsize::new(0);
596
597impl PipelineId {
598    pub const DUMMY: PipelineId = PipelineId(0, 0);
599
600    pub fn new() -> Self {
601        PipelineId(
602            LAST_PIPELINE_ID.fetch_add(1, AtomicOrdering::SeqCst) as u32,
603            0,
604        )
605    }
606}
607
608#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
609pub struct HitTestItem {
610    /// The hit point in the coordinate space of the "viewport" of the display item.
611    /// The viewport is the scroll node formed by the root reference frame of the display item's
612    /// pipeline.
613    pub point_in_viewport: LogicalPosition,
614    /// The coordinates of the original hit test point relative to the origin of this item.
615    /// This is useful for calculating things like text offsets in the client.
616    pub point_relative_to_item: LogicalPosition,
617    /// Necessary to easily get the nearest IFrame node
618    pub is_focusable: bool,
619    /// If this hit is an IFrame node, stores the IFrames DomId + the origin of the IFrame
620    pub is_iframe_hit: Option<(DomId, LogicalPosition)>,
621}
622
623#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
624pub struct ScrollHitTestItem {
625    /// The hit point in the coordinate space of the "viewport" of the display item.
626    /// The viewport is the scroll node formed by the root reference frame of the display item's
627    /// pipeline.
628    pub point_in_viewport: LogicalPosition,
629    /// The coordinates of the original hit test point relative to the origin of this item.
630    /// This is useful for calculating things like text offsets in the client.
631    pub point_relative_to_item: LogicalPosition,
632    /// If this hit is an IFrame node, stores the IFrames DomId + the origin of the IFrame
633    pub scroll_node: OverflowingScrollNode,
634}
635
636/// Implements `Display, Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Hash`
637/// for a Callback with a `.0` field:
638///
639/// ```
640/// # use azul_core::impl_callback;
641/// type T = String;
642/// struct MyCallback {
643///     cb: fn(&T),
644/// };
645///
646/// // impl Display, Debug, etc. for MyCallback
647/// impl_callback!(MyCallback);
648/// ```
649///
650/// This is necessary to work around for https://github.com/rust-lang/rust/issues/54508
651#[macro_export]
652macro_rules! impl_callback {
653    ($callback_value:ident) => {
654        impl ::core::fmt::Display for $callback_value {
655            fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
656                write!(f, "{:?}", self)
657            }
658        }
659
660        impl ::core::fmt::Debug for $callback_value {
661            fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
662                let callback = stringify!($callback_value);
663                write!(f, "{} @ 0x{:x}", callback, self.cb as usize)
664            }
665        }
666
667        impl Clone for $callback_value {
668            fn clone(&self) -> Self {
669                $callback_value {
670                    cb: self.cb.clone(),
671                }
672            }
673        }
674
675        impl ::core::hash::Hash for $callback_value {
676            fn hash<H>(&self, state: &mut H)
677            where
678                H: ::core::hash::Hasher,
679            {
680                state.write_usize(self.cb as usize);
681            }
682        }
683
684        impl PartialEq for $callback_value {
685            fn eq(&self, rhs: &Self) -> bool {
686                self.cb as usize == rhs.cb as usize
687            }
688        }
689
690        impl PartialOrd for $callback_value {
691            fn partial_cmp(&self, other: &Self) -> Option<::core::cmp::Ordering> {
692                Some((self.cb as usize).cmp(&(other.cb as usize)))
693            }
694        }
695
696        impl Ord for $callback_value {
697            fn cmp(&self, other: &Self) -> ::core::cmp::Ordering {
698                (self.cb as usize).cmp(&(other.cb as usize))
699            }
700        }
701
702        impl Eq for $callback_value {}
703
704        impl Copy for $callback_value {}
705    };
706}
707
708#[allow(unused_macros)]
709macro_rules! impl_get_gl_context {
710    () => {
711        /// Returns a reference-counted pointer to the OpenGL context
712        pub fn get_gl_context(&self) -> OptionGlContextPtr {
713            Some(self.gl_context.clone())
714        }
715    };
716}
717
718#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
719#[repr(C)]
720pub struct DomNodeId {
721    pub dom: DomId,
722    pub node: NodeHierarchyItemId,
723}
724
725impl_option!(
726    DomNodeId,
727    OptionDomNodeId,
728    [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
729);
730
731impl DomNodeId {
732    pub const ROOT: DomNodeId = DomNodeId {
733        dom: DomId::ROOT_ID,
734        node: NodeHierarchyItemId::NONE,
735    };
736}
737// -- layout callback
738
739/// Callback function pointer (has to be a function pointer in
740/// order to be compatible with C APIs later on).
741///
742/// IMPORTANT: The callback needs to deallocate the `RefAnyPtr` and `LayoutCallbackInfoPtr`,
743/// otherwise that memory is leaked. If you use the official auto-generated
744/// bindings, this is already done for you.
745///
746/// NOTE: The original callback was `fn(&self, LayoutCallbackInfo) -> Dom`
747/// which then evolved to `fn(&RefAny, LayoutCallbackInfo) -> Dom`.
748/// The indirection is necessary because of the memory management
749/// around the C API
750///
751/// See azul-core/ui_state.rs:298 for how the memory is managed
752/// across the callback boundary.
753pub type LayoutCallbackType = extern "C" fn(&mut RefAny, &mut LayoutCallbackInfo) -> StyledDom;
754
755#[repr(C)]
756pub struct LayoutCallbackInner {
757    pub cb: LayoutCallbackType,
758}
759impl_callback!(LayoutCallbackInner);
760
761extern "C" fn default_layout_callback(_: &mut RefAny, _: &mut LayoutCallbackInfo) -> StyledDom {
762    StyledDom::default()
763}
764
765/// In order to interact with external VMs (Java, Python, etc.)
766/// the callback is often stored as a "function object"
767///
768/// In order to callback into external languages, the layout
769/// callback has to be able to carry some extra data
770/// (the first argument), which usually contains the function object
771/// i.e. in the Python VM a PyCallable / PyAny
772pub type MarshaledLayoutCallbackType = extern "C" fn(
773    /* marshal_data */ &mut RefAny,
774    /* app_data */ &mut RefAny,
775    &mut LayoutCallbackInfo,
776) -> StyledDom;
777
778#[derive(Debug, Clone, PartialEq)]
779#[repr(C, u8)]
780pub enum LayoutCallback {
781    Raw(LayoutCallbackInner),
782    Marshaled(MarshaledLayoutCallback),
783}
784
785impl Default for LayoutCallback {
786    fn default() -> Self {
787        Self::Raw(LayoutCallbackInner {
788            cb: default_layout_callback,
789        })
790    }
791}
792
793#[derive(Debug, Clone, PartialEq)]
794#[repr(C)]
795pub struct MarshaledLayoutCallback {
796    pub marshal_data: RefAny,
797    pub cb: MarshaledLayoutCallbackInner,
798}
799
800#[repr(C)]
801pub struct MarshaledLayoutCallbackInner {
802    pub cb: MarshaledLayoutCallbackType,
803}
804
805impl_callback!(MarshaledLayoutCallbackInner);
806
807// -- normal callback
808
809/// Stores a function pointer that is executed when the given UI element is hit
810///
811/// Must return an `Update` that denotes if the screen should be redrawn.
812/// The style is not affected by this, so if you make changes to the window's style
813/// inside the function, the screen will not be automatically redrawn, unless you return
814/// an `Update::Redraw` from the function
815#[repr(C)]
816pub struct Callback {
817    pub cb: CallbackType,
818}
819impl_callback!(Callback);
820
821impl_option!(
822    Callback,
823    OptionCallback,
824    [Debug, Eq, Copy, Clone, PartialEq, PartialOrd, Ord, Hash]
825);
826
827#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
828#[repr(C)]
829pub struct InlineTextHit {
830    // if the unicode_codepoint is None, it's usually a mark glyph that was hit
831    pub unicode_codepoint: OptionChar, // Option<char>
832
833    // position of the cursor relative to X
834    pub hit_relative_to_inline_text: LogicalPosition,
835    pub hit_relative_to_line: LogicalPosition,
836    pub hit_relative_to_text_content: LogicalPosition,
837    pub hit_relative_to_glyph: LogicalPosition,
838
839    // relative to text
840    pub line_index_relative_to_text: usize,
841    pub word_index_relative_to_text: usize,
842    pub text_content_index_relative_to_text: usize,
843    pub glyph_index_relative_to_text: usize,
844    pub char_index_relative_to_text: usize,
845
846    // relative to line
847    pub word_index_relative_to_line: usize,
848    pub text_content_index_relative_to_line: usize,
849    pub glyph_index_relative_to_line: usize,
850    pub char_index_relative_to_line: usize,
851
852    // relative to text content (word)
853    pub glyph_index_relative_to_word: usize,
854    pub char_index_relative_to_word: usize,
855}
856
857impl_vec!(InlineTextHit, InlineTextHitVec, InlineTextHitVecDestructor);
858impl_vec_clone!(InlineTextHit, InlineTextHitVec, InlineTextHitVecDestructor);
859impl_vec_debug!(InlineTextHit, InlineTextHitVec);
860impl_vec_partialeq!(InlineTextHit, InlineTextHitVec);
861impl_vec_partialord!(InlineTextHit, InlineTextHitVec);
862
863/// inline text so that hit-testing is easier
864#[derive(Debug, Clone, PartialEq, PartialOrd)]
865#[repr(C)]
866pub struct InlineText {
867    /// List of lines, relative to (0.0, 0.0) representing the top left corner of the line
868    pub lines: InlineLineVec,
869    /// Size of the text content, may be larger than the
870    /// position of lines due to descending glyphs
871    pub content_size: LogicalSize,
872    /// Size of the font used to layout this line
873    pub font_size_px: f32,
874    /// Index of the last word
875    pub last_word_index: usize,
876    /// NOTE: descender is NEGATIVE (pixels from baseline to font size)
877    pub baseline_descender_px: f32,
878}
879
880impl_option!(
881    InlineText,
882    OptionInlineText,
883    copy = false,
884    [Debug, Clone, PartialEq, PartialOrd]
885);
886
887impl InlineText {
888    /// Returns the final, positioned glyphs from an inline text
889    ///
890    /// NOTE: It seems that at least in webrender, the glyphs have to be
891    /// positioned in relation to the screen (instead of relative to the parent container)
892    ///
893    /// The text_origin gets added to each glyph
894    ///
895    /// NOTE: The lines in the text are relative to the TOP left corner (of the text, i.e.
896    /// relative to the text_origin), but the word position is relative to the BOTTOM left
897    /// corner (of the line bounds)
898    pub fn get_layouted_glyphs(&self) -> LayoutedGlyphs {
899        use crate::display_list::GlyphInstance;
900
901        let default: InlineGlyphVec = Vec::new().into();
902        let default_ref = &default;
903
904        // descender_px is NEGATIVE
905        let baseline_descender_px = LogicalPosition::new(0.0, self.baseline_descender_px);
906
907        LayoutedGlyphs {
908            glyphs: self
909                .lines
910                .iter()
911                .flat_map(move |line| {
912                    // bottom left corner of line rect
913                    let line_origin = line.bounds.origin;
914
915                    line.words.iter().flat_map(move |word| {
916                        let (glyphs, mut word_origin) = match word {
917                            InlineWord::Tab | InlineWord::Return | InlineWord::Space => {
918                                (default_ref, LogicalPosition::zero())
919                            }
920                            InlineWord::Word(text_contents) => {
921                                (&text_contents.glyphs, text_contents.bounds.origin)
922                            }
923                        };
924
925                        word_origin.y = 0.0;
926
927                        glyphs.iter().map(move |glyph| GlyphInstance {
928                            index: glyph.glyph_index,
929                            point: {
930                                line_origin
931                                    + baseline_descender_px
932                                    + word_origin
933                                    + glyph.bounds.origin
934                            },
935                            size: glyph.bounds.size,
936                        })
937                    })
938                })
939                .collect::<Vec<GlyphInstance>>(),
940        }
941    }
942
943    /// Hit tests all glyphs, returns the hit glyphs - note that the result may
944    /// be empty (no glyphs hit), or it may contain more than one result
945    /// (overlapping glyphs - more than one glyph hit)
946    ///
947    /// Usually the result will contain a single `InlineTextHit`
948    pub fn hit_test(&self, position: LogicalPosition) -> Vec<InlineTextHit> {
949        let bounds = LogicalRect::new(LogicalPosition::zero(), self.content_size);
950
951        let hit_relative_to_inline_text = match bounds.hit_test(&position) {
952            Some(s) => s,
953            None => return Vec::new(),
954        };
955
956        let mut global_char_hit = 0;
957        let mut global_word_hit = 0;
958        let mut global_glyph_hit = 0;
959        let mut global_text_content_hit = 0;
960
961        // NOTE: this function cannot exit early, since it has to
962        // iterate through all lines
963
964        let font_size_px = self.font_size_px;
965        let descender_px = self.baseline_descender_px;
966
967        self.lines
968        .iter() // TODO: par_iter
969        .enumerate()
970        .flat_map(|(line_index, line)| {
971
972            let char_at_line_start = global_char_hit;
973            let word_at_line_start = global_word_hit;
974            let glyph_at_line_start = global_glyph_hit;
975            let text_content_at_line_start = global_text_content_hit;
976
977            let mut line_bounds = line.bounds.clone();
978            line_bounds.origin.y -= line.bounds.size.height;
979
980            line_bounds.hit_test(&hit_relative_to_inline_text)
981            .map(|mut hit_relative_to_line| {
982
983                line.words
984                .iter() // TODO: par_iter
985                .flat_map(|word| {
986
987                    let char_at_text_content_start = global_char_hit;
988                    let glyph_at_text_content_start = global_glyph_hit;
989
990                    let word_result = word
991                    .get_text_content()
992                    .and_then(|text_content| {
993
994                        let mut text_content_bounds = text_content.bounds.clone();
995                        text_content_bounds.origin.y = 0.0;
996
997                        text_content_bounds
998                        .hit_test(&hit_relative_to_line)
999                        .map(|mut hit_relative_to_text_content| {
1000
1001                            text_content.glyphs
1002                            .iter() // TODO: par_iter
1003                            .flat_map(|glyph| {
1004
1005                                let mut glyph_bounds = glyph.bounds;
1006                                glyph_bounds.origin.y = text_content.bounds.size.height + descender_px - glyph.bounds.size.height;
1007
1008                                let result = glyph_bounds
1009                                .hit_test(&hit_relative_to_text_content)
1010                                .map(|hit_relative_to_glyph| {
1011                                    InlineTextHit {
1012                                        unicode_codepoint: glyph.unicode_codepoint,
1013
1014                                        hit_relative_to_inline_text,
1015                                        hit_relative_to_line,
1016                                        hit_relative_to_text_content,
1017                                        hit_relative_to_glyph,
1018
1019                                        line_index_relative_to_text: line_index,
1020                                        word_index_relative_to_text: global_word_hit,
1021                                        text_content_index_relative_to_text: global_text_content_hit,
1022                                        glyph_index_relative_to_text: global_glyph_hit,
1023                                        char_index_relative_to_text: global_char_hit,
1024
1025                                        word_index_relative_to_line: global_word_hit - word_at_line_start,
1026                                        text_content_index_relative_to_line: global_text_content_hit - text_content_at_line_start,
1027                                        glyph_index_relative_to_line: global_glyph_hit - glyph_at_line_start,
1028                                        char_index_relative_to_line: global_char_hit - char_at_line_start,
1029
1030                                        glyph_index_relative_to_word: global_glyph_hit - glyph_at_text_content_start,
1031                                        char_index_relative_to_word: global_char_hit - char_at_text_content_start,
1032                                    }
1033                                });
1034
1035                                if glyph.has_codepoint() {
1036                                    global_char_hit += 1;
1037                                }
1038
1039                                global_glyph_hit += 1;
1040
1041                                result
1042                            })
1043                            .collect::<Vec<_>>()
1044                        })
1045                    }).unwrap_or_default();
1046
1047                    if word.has_text_content() {
1048                        global_text_content_hit += 1;
1049                    }
1050
1051                    global_word_hit += 1;
1052
1053                    word_result.into_iter()
1054                })
1055                .collect::<Vec<_>>()
1056            })
1057            .unwrap_or_default()
1058            .into_iter()
1059
1060        })
1061        .collect::<Vec<_>>()
1062    }
1063}
1064
1065#[derive(Debug, Clone, PartialEq, PartialOrd)]
1066#[repr(C)]
1067pub struct InlineLine {
1068    pub words: InlineWordVec,
1069    pub bounds: LogicalRect,
1070}
1071
1072impl_vec!(InlineLine, InlineLineVec, InlineLineVecDestructor);
1073impl_vec_clone!(InlineLine, InlineLineVec, InlineLineVecDestructor);
1074impl_vec_debug!(InlineLine, InlineLineVec);
1075impl_vec_partialeq!(InlineLine, InlineLineVec);
1076impl_vec_partialord!(InlineLine, InlineLineVec);
1077
1078#[derive(Debug, Clone, PartialEq, PartialOrd)]
1079#[repr(C, u8)]
1080pub enum InlineWord {
1081    Tab,
1082    Return,
1083    Space,
1084    Word(InlineTextContents),
1085}
1086
1087impl InlineWord {
1088    pub fn has_text_content(&self) -> bool {
1089        self.get_text_content().is_some()
1090    }
1091    pub fn get_text_content(&self) -> Option<&InlineTextContents> {
1092        match self {
1093            InlineWord::Tab | InlineWord::Return | InlineWord::Space => None,
1094            InlineWord::Word(tc) => Some(tc),
1095        }
1096    }
1097}
1098
1099impl_vec!(InlineWord, InlineWordVec, InlineWordVecDestructor);
1100impl_vec_clone!(InlineWord, InlineWordVec, InlineWordVecDestructor);
1101impl_vec_debug!(InlineWord, InlineWordVec);
1102impl_vec_partialeq!(InlineWord, InlineWordVec);
1103impl_vec_partialord!(InlineWord, InlineWordVec);
1104
1105#[derive(Debug, Clone, PartialEq, PartialOrd)]
1106#[repr(C)]
1107pub struct InlineTextContents {
1108    pub glyphs: InlineGlyphVec,
1109    pub bounds: LogicalRect,
1110}
1111
1112#[derive(Debug, Clone, PartialEq, PartialOrd)]
1113#[repr(C)]
1114pub struct InlineGlyph {
1115    pub bounds: LogicalRect,
1116    pub unicode_codepoint: OptionChar,
1117    pub glyph_index: u32,
1118}
1119
1120impl InlineGlyph {
1121    pub fn has_codepoint(&self) -> bool {
1122        self.unicode_codepoint.is_some()
1123    }
1124}
1125
1126impl_vec!(InlineGlyph, InlineGlyphVec, InlineGlyphVecDestructor);
1127impl_vec_clone!(InlineGlyph, InlineGlyphVec, InlineGlyphVecDestructor);
1128impl_vec_debug!(InlineGlyph, InlineGlyphVec);
1129impl_vec_partialeq!(InlineGlyph, InlineGlyphVec);
1130impl_vec_partialord!(InlineGlyph, InlineGlyphVec);
1131
1132/// Information about the callback that is passed to the callback whenever a callback is invoked
1133#[derive(Debug)]
1134#[repr(C)]
1135pub struct CallbackInfo {
1136    /// Pointer to the layout results vector start
1137    layout_results: *const LayoutResult,
1138    /// Number of layout results
1139    layout_results_count: usize,
1140    /// Necessary to query FontRefs from callbacks
1141    renderer_resources: *const RendererResources,
1142    /// Previous window state
1143    previous_window_state: *const Option<FullWindowState>,
1144    /// State of the current window that the callback was called on (read only!)
1145    current_window_state: *const FullWindowState,
1146    /// User-modifiable state of the window that the callback was called on
1147    modifiable_window_state: *mut WindowState,
1148    /// An Rc to the OpenGL context, in order to be able to render to OpenGL textures
1149    gl_context: *const OptionGlContextPtr,
1150    /// Cache to add / remove / query image RefAnys from / to CSS ids
1151    image_cache: *mut ImageCache,
1152    /// System font cache (can be regenerated / refreshed in callbacks)
1153    system_fonts: *mut FcFontCache,
1154    /// Currently running timers (polling functions, run on the main thread)
1155    timers: *mut FastHashMap<TimerId, Timer>,
1156    /// Currently running threads (asynchronous functions running each on a different thread)
1157    threads: *mut FastHashMap<ThreadId, Thread>,
1158    /// Timers removed by the callback
1159    timers_removed: *mut FastBTreeSet<TimerId>,
1160    /// Threads removed by the callback
1161    threads_removed: *mut FastBTreeSet<ThreadId>,
1162    /// Handle of the current window
1163    current_window_handle: *const RawWindowHandle,
1164    /// Used to spawn new windows from callbacks. You can use `get_current_window_handle()` to
1165    /// spawn child windows.
1166    new_windows: *mut Vec<WindowCreateOptions>,
1167    /// Callbacks for creating threads and getting the system time (since this crate uses no_std)
1168    system_callbacks: *const ExternalSystemCallbacks,
1169    /// Sets whether the event should be propagated to the parent hit node or not
1170    stop_propagation: *mut bool,
1171    /// The callback can change the focus_target - note that the focus_target is set before the
1172    /// next frames' layout() function is invoked, but the current frames callbacks are not
1173    /// affected.
1174    focus_target: *mut Option<FocusTarget>,
1175    /// Mutable reference to a list of words / text items that were changed in the callback
1176    words_changed_in_callbacks: *mut BTreeMap<DomId, BTreeMap<NodeId, AzString>>,
1177    /// Mutable reference to a list of images that were changed in the callback
1178    images_changed_in_callbacks:
1179        *mut BTreeMap<DomId, BTreeMap<NodeId, (ImageRef, UpdateImageType)>>,
1180    /// Mutable reference to a list of image clip masks that were changed in the callback
1181    image_masks_changed_in_callbacks: *mut BTreeMap<DomId, BTreeMap<NodeId, ImageMask>>,
1182    /// Mutable reference to a list of CSS property changes, so that the callbacks can change CSS
1183    /// properties
1184    css_properties_changed_in_callbacks: *mut BTreeMap<DomId, BTreeMap<NodeId, Vec<CssProperty>>>,
1185    /// Immutable (!) reference to where the nodes are currently scrolled (current position)
1186    current_scroll_states: *const BTreeMap<DomId, BTreeMap<NodeHierarchyItemId, ScrollPosition>>,
1187    /// Mutable map where a user can set where he wants the nodes to be scrolled to (for the next
1188    /// frame)
1189    nodes_scrolled_in_callback:
1190        *mut BTreeMap<DomId, BTreeMap<NodeHierarchyItemId, LogicalPosition>>,
1191    /// The ID of the DOM + the node that was hit. You can use this to query
1192    /// information about the node, but please don't hard-code any if / else
1193    /// statements based on the `NodeId`
1194    hit_dom_node: DomNodeId,
1195    /// The (x, y) position of the mouse cursor, **relative to top left of the element that was
1196    /// hit**.
1197    cursor_relative_to_item: OptionLogicalPosition,
1198    /// The (x, y) position of the mouse cursor, **relative to top left of the window**.
1199    cursor_in_viewport: OptionLogicalPosition,
1200    /// Extension for future ABI stability (referenced data)
1201    _abi_ref: *const c_void,
1202    /// Extension for future ABI stability (mutable data)
1203    _abi_mut: *mut c_void,
1204}
1205
1206impl CallbackInfo {
1207    // this function is necessary to get rid of the lifetimes and to make CallbackInfo C-compatible
1208    //
1209    // since the call_callbacks() function is the only function
1210    #[inline]
1211    pub fn new<'a, 'b>(
1212        layout_results: &'a [LayoutResult],
1213        renderer_resources: &'a RendererResources,
1214        previous_window_state: &'a Option<FullWindowState>,
1215        current_window_state: &'a FullWindowState,
1216        modifiable_window_state: &'a mut WindowState,
1217        gl_context: &'a OptionGlContextPtr,
1218        image_cache: &'a mut ImageCache,
1219        system_fonts: &'a mut FcFontCache,
1220        timers: &'a mut FastHashMap<TimerId, Timer>,
1221        threads: &'a mut FastHashMap<ThreadId, Thread>,
1222        timers_removed: &'a mut FastBTreeSet<TimerId>,
1223        threads_removed: &'a mut FastBTreeSet<ThreadId>,
1224        current_window_handle: &'a RawWindowHandle,
1225        new_windows: &'a mut Vec<WindowCreateOptions>,
1226        system_callbacks: &'a ExternalSystemCallbacks,
1227        stop_propagation: &'a mut bool,
1228        focus_target: &'a mut Option<FocusTarget>,
1229        words_changed_in_callbacks: &'a mut BTreeMap<DomId, BTreeMap<NodeId, AzString>>,
1230        images_changed_in_callbacks: &'a mut BTreeMap<
1231            DomId,
1232            BTreeMap<NodeId, (ImageRef, UpdateImageType)>,
1233        >,
1234        image_masks_changed_in_callbacks: &'a mut BTreeMap<DomId, BTreeMap<NodeId, ImageMask>>,
1235        css_properties_changed_in_callbacks: &'a mut BTreeMap<
1236            DomId,
1237            BTreeMap<NodeId, Vec<CssProperty>>,
1238        >,
1239        current_scroll_states: &'a BTreeMap<DomId, BTreeMap<NodeHierarchyItemId, ScrollPosition>>,
1240        nodes_scrolled_in_callback: &'a mut BTreeMap<
1241            DomId,
1242            BTreeMap<NodeHierarchyItemId, LogicalPosition>,
1243        >,
1244        hit_dom_node: DomNodeId,
1245        cursor_relative_to_item: OptionLogicalPosition,
1246        cursor_in_viewport: OptionLogicalPosition,
1247    ) -> Self {
1248        Self {
1249            layout_results: layout_results.as_ptr(),
1250            layout_results_count: layout_results.len(),
1251            renderer_resources: renderer_resources as *const RendererResources,
1252            previous_window_state: previous_window_state as *const Option<FullWindowState>,
1253            current_window_state: current_window_state as *const FullWindowState,
1254            modifiable_window_state: modifiable_window_state as *mut WindowState,
1255            gl_context: gl_context as *const OptionGlContextPtr,
1256            image_cache: image_cache as *mut ImageCache,
1257            system_fonts: system_fonts as *mut FcFontCache,
1258            timers: timers as *mut FastHashMap<TimerId, Timer>,
1259            threads: threads as *mut FastHashMap<ThreadId, Thread>,
1260            timers_removed: timers_removed as *mut FastBTreeSet<TimerId>,
1261            threads_removed: threads_removed as *mut FastBTreeSet<ThreadId>,
1262            new_windows: new_windows as *mut Vec<WindowCreateOptions>,
1263            current_window_handle: current_window_handle as *const RawWindowHandle,
1264            system_callbacks: system_callbacks as *const ExternalSystemCallbacks,
1265            stop_propagation: stop_propagation as *mut bool,
1266            focus_target: focus_target as *mut Option<FocusTarget>,
1267            words_changed_in_callbacks: words_changed_in_callbacks
1268                as *mut BTreeMap<DomId, BTreeMap<NodeId, AzString>>,
1269            images_changed_in_callbacks: images_changed_in_callbacks
1270                as *mut BTreeMap<DomId, BTreeMap<NodeId, (ImageRef, UpdateImageType)>>,
1271            image_masks_changed_in_callbacks: image_masks_changed_in_callbacks
1272                as *mut BTreeMap<DomId, BTreeMap<NodeId, ImageMask>>,
1273            css_properties_changed_in_callbacks: css_properties_changed_in_callbacks
1274                as *mut BTreeMap<DomId, BTreeMap<NodeId, Vec<CssProperty>>>,
1275            current_scroll_states: current_scroll_states
1276                as *const BTreeMap<DomId, BTreeMap<NodeHierarchyItemId, ScrollPosition>>,
1277            nodes_scrolled_in_callback: nodes_scrolled_in_callback
1278                as *mut BTreeMap<DomId, BTreeMap<NodeHierarchyItemId, LogicalPosition>>,
1279            hit_dom_node,
1280            cursor_relative_to_item,
1281            cursor_in_viewport,
1282            _abi_ref: core::ptr::null(),
1283            _abi_mut: core::ptr::null_mut(),
1284        }
1285    }
1286
1287    fn internal_get_layout_results<'a>(&'a self) -> &'a [LayoutResult] {
1288        unsafe { core::slice::from_raw_parts(self.layout_results, self.layout_results_count) }
1289    }
1290    fn internal_get_renderer_resources<'a>(&'a self) -> &'a RendererResources {
1291        unsafe { &*self.renderer_resources }
1292    }
1293    fn internal_get_previous_window_state<'a>(&'a self) -> &'a Option<FullWindowState> {
1294        unsafe { &*self.previous_window_state }
1295    }
1296    fn internal_get_current_window_state<'a>(&'a self) -> &'a FullWindowState {
1297        unsafe { &*self.current_window_state }
1298    }
1299    fn internal_get_modifiable_window_state<'a>(&'a mut self) -> &'a mut WindowState {
1300        unsafe { &mut *self.modifiable_window_state }
1301    }
1302    fn internal_get_gl_context<'a>(&'a self) -> &'a OptionGlContextPtr {
1303        unsafe { &*self.gl_context }
1304    }
1305    fn internal_get_image_cache<'a>(&'a mut self) -> &'a mut ImageCache {
1306        unsafe { &mut *self.image_cache }
1307    }
1308    fn internal_get_image_cache_ref<'a>(&'a self) -> &'a ImageCache {
1309        unsafe { &*self.image_cache }
1310    }
1311    fn internal_get_system_fonts<'a>(&'a mut self) -> &'a mut FcFontCache {
1312        unsafe { &mut *self.system_fonts }
1313    }
1314    fn internal_get_timers<'a>(&'a mut self) -> &'a mut FastHashMap<TimerId, Timer> {
1315        unsafe { &mut *self.timers }
1316    }
1317    fn internal_get_threads<'a>(&'a mut self) -> &'a mut FastHashMap<ThreadId, Thread> {
1318        unsafe { &mut *self.threads }
1319    }
1320    fn internal_get_timers_removed<'a>(&'a mut self) -> &'a mut FastBTreeSet<TimerId> {
1321        unsafe { &mut *self.timers_removed }
1322    }
1323    fn internal_get_threads_removed<'a>(&'a mut self) -> &'a mut FastBTreeSet<ThreadId> {
1324        unsafe { &mut *self.threads_removed }
1325    }
1326    fn internal_get_new_windows<'a>(&'a mut self) -> &'a mut Vec<WindowCreateOptions> {
1327        unsafe { &mut *self.new_windows }
1328    }
1329    fn internal_get_current_window_handle<'a>(&'a self) -> &'a RawWindowHandle {
1330        unsafe { &*self.current_window_handle }
1331    }
1332    fn internal_get_extern_system_callbacks<'a>(&'a self) -> &'a ExternalSystemCallbacks {
1333        unsafe { &*self.system_callbacks }
1334    }
1335    fn internal_get_stop_propagation<'a>(&'a mut self) -> &'a mut bool {
1336        unsafe { &mut *self.stop_propagation }
1337    }
1338    fn internal_get_focus_target<'a>(&'a mut self) -> &'a mut Option<FocusTarget> {
1339        unsafe { &mut *self.focus_target }
1340    }
1341    fn internal_get_current_scroll_states<'a>(
1342        &'a self,
1343    ) -> &'a BTreeMap<DomId, BTreeMap<NodeHierarchyItemId, ScrollPosition>> {
1344        unsafe { &*self.current_scroll_states }
1345    }
1346    fn internal_get_css_properties_changed_in_callbacks<'a>(
1347        &'a mut self,
1348    ) -> &'a mut BTreeMap<DomId, BTreeMap<NodeId, Vec<CssProperty>>> {
1349        unsafe { &mut *self.css_properties_changed_in_callbacks }
1350    }
1351    fn internal_get_nodes_scrolled_in_callback<'a>(
1352        &'a mut self,
1353    ) -> &'a mut BTreeMap<DomId, BTreeMap<NodeHierarchyItemId, LogicalPosition>> {
1354        unsafe { &mut *self.nodes_scrolled_in_callback }
1355    }
1356    fn internal_get_hit_dom_node<'a>(&'a self) -> DomNodeId {
1357        self.hit_dom_node
1358    }
1359    fn internal_get_cursor_relative_to_item<'a>(&'a self) -> OptionLogicalPosition {
1360        self.cursor_relative_to_item
1361    }
1362    fn internal_get_cursor_in_viewport<'a>(&'a self) -> OptionLogicalPosition {
1363        self.cursor_in_viewport
1364    }
1365    fn internal_get_words_changed_in_callbacks<'a>(
1366        &'a mut self,
1367    ) -> &'a mut BTreeMap<DomId, BTreeMap<NodeId, AzString>> {
1368        unsafe { &mut *self.words_changed_in_callbacks }
1369    }
1370    fn internal_get_images_changed_in_callbacks<'a>(
1371        &'a mut self,
1372    ) -> &'a mut BTreeMap<DomId, BTreeMap<NodeId, (ImageRef, UpdateImageType)>> {
1373        unsafe { &mut *self.images_changed_in_callbacks }
1374    }
1375    fn internal_get_image_masks_changed_in_callbacks<'a>(
1376        &'a mut self,
1377    ) -> &'a mut BTreeMap<DomId, BTreeMap<NodeId, ImageMask>> {
1378        unsafe { &mut *self.image_masks_changed_in_callbacks }
1379    }
1380
1381    // public functions
1382    pub fn get_hit_node(&self) -> DomNodeId {
1383        self.internal_get_hit_dom_node()
1384    }
1385    pub fn get_system_time_fn(&self) -> GetSystemTimeCallback {
1386        self.internal_get_extern_system_callbacks()
1387            .get_system_time_fn
1388    }
1389    pub fn get_thread_create_fn(&self) -> CreateThreadCallback {
1390        self.internal_get_extern_system_callbacks().create_thread_fn
1391    }
1392    pub fn get_cursor_relative_to_node(&self) -> OptionLogicalPosition {
1393        self.internal_get_cursor_relative_to_item()
1394    }
1395    pub fn get_cursor_relative_to_viewport(&self) -> OptionLogicalPosition {
1396        self.internal_get_cursor_in_viewport()
1397    }
1398    pub fn get_current_window_state(&self) -> WindowState {
1399        self.internal_get_current_window_state().clone().into()
1400    }
1401    pub fn get_current_window_flags(&self) -> WindowFlags {
1402        self.internal_get_current_window_state().flags.clone()
1403    }
1404    pub fn get_current_keyboard_state(&self) -> KeyboardState {
1405        self.internal_get_current_window_state()
1406            .keyboard_state
1407            .clone()
1408    }
1409    pub fn get_current_mouse_state(&self) -> MouseState {
1410        self.internal_get_current_window_state().mouse_state.clone()
1411    }
1412    pub fn get_previous_window_state(&self) -> Option<WindowState> {
1413        Some(
1414            self.internal_get_previous_window_state()
1415                .as_ref()?
1416                .clone()
1417                .into(),
1418        )
1419    }
1420    pub fn get_previous_keyboard_state(&self) -> Option<KeyboardState> {
1421        Some(
1422            self.internal_get_previous_window_state()
1423                .as_ref()?
1424                .keyboard_state
1425                .clone(),
1426        )
1427    }
1428    pub fn get_previous_mouse_state(&self) -> Option<MouseState> {
1429        Some(
1430            self.internal_get_previous_window_state()
1431                .as_ref()?
1432                .mouse_state
1433                .clone(),
1434        )
1435    }
1436    pub fn get_current_window_handle(&self) -> RawWindowHandle {
1437        self.internal_get_current_window_handle().clone()
1438    }
1439    pub fn get_current_time(&self) -> Instant {
1440        (self
1441            .internal_get_extern_system_callbacks()
1442            .get_system_time_fn
1443            .cb)()
1444    }
1445    pub fn get_gl_context(&self) -> OptionGlContextPtr {
1446        self.internal_get_gl_context().clone()
1447    }
1448
1449    pub fn get_scroll_position(&self, node_id: DomNodeId) -> Option<LogicalPosition> {
1450        self.internal_get_current_scroll_states()
1451            .get(&node_id.dom)?
1452            .get(&node_id.node)
1453            .map(|sp| {
1454                LogicalPosition::new(
1455                    sp.children_rect.origin.x - sp.parent_rect.origin.x,
1456                    sp.children_rect.origin.y - sp.parent_rect.origin.y,
1457                )
1458            })
1459    }
1460
1461    pub fn set_scroll_position(&mut self, node_id: DomNodeId, scroll_position: LogicalPosition) {
1462        self.internal_get_nodes_scrolled_in_callback()
1463            .entry(node_id.dom)
1464            .or_insert_with(|| BTreeMap::new())
1465            .insert(node_id.node, scroll_position);
1466    }
1467
1468    pub fn get_parent(&self, node_id: DomNodeId) -> Option<DomNodeId> {
1469        let nid = node_id.node.into_crate_internal()?;
1470        self.internal_get_layout_results()
1471            .get(node_id.dom.inner)?
1472            .styled_dom
1473            .node_hierarchy
1474            .as_container()
1475            .get(node_id.node.into_crate_internal()?)?
1476            .parent_id()
1477            .map(|nid| DomNodeId {
1478                dom: node_id.dom,
1479                node: NodeHierarchyItemId::from_crate_internal(Some(nid)),
1480            })
1481    }
1482
1483    pub fn get_previous_sibling(&self, node_id: DomNodeId) -> Option<DomNodeId> {
1484        let nid = node_id.node.into_crate_internal()?;
1485        self.internal_get_layout_results()
1486            .get(node_id.dom.inner)?
1487            .styled_dom
1488            .node_hierarchy
1489            .as_container()
1490            .get(node_id.node.into_crate_internal()?)?
1491            .previous_sibling_id()
1492            .map(|nid| DomNodeId {
1493                dom: node_id.dom,
1494                node: NodeHierarchyItemId::from_crate_internal(Some(nid)),
1495            })
1496    }
1497
1498    pub fn get_next_sibling(&self, node_id: DomNodeId) -> Option<DomNodeId> {
1499        let nid = node_id.node.into_crate_internal()?;
1500        self.internal_get_layout_results()
1501            .get(node_id.dom.inner)?
1502            .styled_dom
1503            .node_hierarchy
1504            .as_container()
1505            .get(node_id.node.into_crate_internal()?)?
1506            .next_sibling_id()
1507            .map(|nid| DomNodeId {
1508                dom: node_id.dom,
1509                node: NodeHierarchyItemId::from_crate_internal(Some(nid)),
1510            })
1511    }
1512
1513    pub fn get_first_child(&self, node_id: DomNodeId) -> Option<DomNodeId> {
1514        let nid = node_id.node.into_crate_internal()?;
1515        self.internal_get_layout_results()
1516            .get(node_id.dom.inner)?
1517            .styled_dom
1518            .node_hierarchy
1519            .as_container()
1520            .get(nid)?
1521            .first_child_id(nid)
1522            .map(|nid| DomNodeId {
1523                dom: node_id.dom,
1524                node: NodeHierarchyItemId::from_crate_internal(Some(nid)),
1525            })
1526    }
1527
1528    pub fn get_last_child(&self, node_id: DomNodeId) -> Option<DomNodeId> {
1529        let nid = node_id.node.into_crate_internal()?;
1530        self.internal_get_layout_results()
1531            .get(node_id.dom.inner)?
1532            .styled_dom
1533            .node_hierarchy
1534            .as_container()
1535            .get(node_id.node.into_crate_internal()?)?
1536            .last_child_id()
1537            .map(|nid| DomNodeId {
1538                dom: node_id.dom,
1539                node: NodeHierarchyItemId::from_crate_internal(Some(nid)),
1540            })
1541    }
1542
1543    pub fn get_dataset(&mut self, node_id: DomNodeId) -> Option<RefAny> {
1544        self.internal_get_layout_results()
1545            .get(node_id.dom.inner)?
1546            .styled_dom
1547            .node_data
1548            .as_container()
1549            .get(node_id.node.into_crate_internal()?)?
1550            .dataset
1551            .as_ref()
1552            .map(|s| s.clone())
1553    }
1554
1555    pub fn get_node_id_of_root_dataset(&mut self, search_key: RefAny) -> Option<DomNodeId> {
1556        let mut found: Option<(u64, DomNodeId)> = None;
1557
1558        for (dom_node_id, layout_result) in self.internal_get_layout_results().iter().enumerate() {
1559            for (node_id, node_data) in layout_result
1560                .styled_dom
1561                .node_data
1562                .as_container()
1563                .iter()
1564                .enumerate()
1565            {
1566                if let Some(dataset) = node_data.dataset.as_ref() {
1567                    // dataset RefAny has to point to the same instance
1568                    if dataset._internal_ptr as usize != search_key._internal_ptr as usize {
1569                        continue;
1570                    }
1571
1572                    let node_id = DomNodeId {
1573                        dom: DomId { inner: dom_node_id },
1574                        node: NodeHierarchyItemId::from_crate_internal(Some(NodeId::new(node_id))),
1575                    };
1576
1577                    if (dataset.instance_id as u64)
1578                        < found.as_ref().map(|s| s.0).unwrap_or(u64::MAX)
1579                    {
1580                        found = Some((dataset.instance_id, node_id))
1581                    }
1582                }
1583            }
1584        }
1585
1586        found.map(|s| s.1)
1587    }
1588
1589    pub fn set_window_state(&mut self, new_state: WindowState) {
1590        *self.internal_get_modifiable_window_state() = new_state;
1591    }
1592
1593    pub fn set_window_flags(&mut self, new_flags: WindowFlags) {
1594        self.internal_get_modifiable_window_state().flags = new_flags;
1595    }
1596
1597    pub fn set_css_property(&mut self, node_id: DomNodeId, prop: CssProperty) {
1598        if let Some(nid) = node_id.node.into_crate_internal() {
1599            self.internal_get_css_properties_changed_in_callbacks()
1600                .entry(node_id.dom)
1601                .or_insert_with(|| BTreeMap::new())
1602                .entry(nid)
1603                .or_insert_with(|| Vec::new())
1604                .push(prop);
1605        }
1606    }
1607
1608    pub fn set_focus(&mut self, target: FocusTarget) {
1609        *self.internal_get_focus_target() = Some(target);
1610    }
1611
1612    pub fn get_string_contents(&self, node_id: DomNodeId) -> Option<AzString> {
1613        self.internal_get_layout_results()
1614            .get(node_id.dom.inner)?
1615            .words_cache
1616            .get(&node_id.node.into_crate_internal()?)
1617            .map(|words| words.internal_str.clone())
1618    }
1619
1620    pub fn set_string_contents(&mut self, node_id: DomNodeId, new_string_contents: AzString) {
1621        if let Some(nid) = node_id.node.into_crate_internal() {
1622            self.internal_get_words_changed_in_callbacks()
1623                .entry(node_id.dom)
1624                .or_insert_with(|| BTreeMap::new())
1625                .insert(nid, new_string_contents);
1626        }
1627    }
1628
1629    pub fn get_inline_text(&self, node_id: DomNodeId) -> Option<InlineText> {
1630        let nid = node_id.node.into_crate_internal()?;
1631        let layout_result = self.internal_get_layout_results().get(node_id.dom.inner)?;
1632        let words = layout_result.words_cache.get(&nid)?;
1633        let shaped_words = layout_result.shaped_words_cache.get(&nid)?;
1634        let word_positions = layout_result.positioned_words_cache.get(&nid)?;
1635        let positioned_rectangles = layout_result.rects.as_ref();
1636        let positioned_rectangle = positioned_rectangles.get(nid)?;
1637        let (_, inline_text_layout) = positioned_rectangle.resolved_text_layout_options.as_ref()?;
1638        Some(crate::app_resources::get_inline_text(
1639            &words,
1640            &shaped_words,
1641            &word_positions.0,
1642            &inline_text_layout,
1643        ))
1644    }
1645
1646    /// Returns the FontRef for the given NodeId
1647    pub fn get_font_ref(&self, node_id: DomNodeId) -> Option<FontRef> {
1648        use crate::styled_dom::StyleFontFamiliesHash;
1649
1650        let layout_result = self.internal_get_layout_results().get(node_id.dom.inner)?;
1651        let renderer_resources = self.internal_get_renderer_resources();
1652
1653        let node_data = layout_result.styled_dom.node_data.as_container();
1654        let node_data = node_data.get(node_id.node.into_crate_internal()?)?;
1655
1656        if !node_data.is_text_node() {
1657            return None;
1658        }
1659
1660        let nid = node_id.node.into_crate_internal()?;
1661        let styled_nodes = layout_result.styled_dom.styled_nodes.as_container();
1662        styled_nodes
1663            .get(nid)
1664            .map(|s| {
1665                layout_result
1666                    .styled_dom
1667                    .css_property_cache
1668                    .ptr
1669                    .get_font_id_or_default(node_data, &nid, &s.state)
1670            })
1671            .map(|css_font_families| StyleFontFamiliesHash::new(css_font_families.as_ref()))
1672            .and_then(|css_font_families_hash| {
1673                renderer_resources.get_font_family(&css_font_families_hash)
1674            })
1675            .and_then(|css_font_family| renderer_resources.get_font_key(&css_font_family))
1676            .and_then(|font_key| renderer_resources.get_registered_font(&font_key))
1677            .map(|f| f.0.clone())
1678    }
1679
1680    pub fn get_text_layout_options(&self, node_id: DomNodeId) -> Option<ResolvedTextLayoutOptions> {
1681        let layout_result = self.internal_get_layout_results().get(node_id.dom.inner)?;
1682        let nid = node_id.node.into_crate_internal()?;
1683        let positioned_rectangles = layout_result.rects.as_ref();
1684        let positioned_rectangle = positioned_rectangles.get(nid)?;
1685        let (text_layout_options, _) =
1686            positioned_rectangle.resolved_text_layout_options.as_ref()?;
1687        Some(text_layout_options.clone())
1688    }
1689
1690    pub fn get_computed_css_property(
1691        &self,
1692        node_id: DomNodeId,
1693        property_type: CssPropertyType,
1694    ) -> Option<CssProperty> {
1695        let layout_result = self.internal_get_layout_results().get(node_id.dom.inner)?;
1696
1697        /*
1698            if node_id.dom != self.get_hit_node().dom {
1699                return None;
1700            }
1701            let nid = node_id.node.into_crate_internal()?;
1702            let css_property_cache = self.internal_get_css_property_cache();
1703            let styled_nodes = self.internal_get_styled_node_states();
1704            let styled_node_state = styled_nodes.internal.get(nid)?; //
1705            let node_data = self.internal_get_
1706        */
1707
1708        // TODO: can't access self.styled_dom.node_data[node_id].classes because
1709        // self.styled_dom.node_data[node_id].dataset may be borrowed
1710
1711        None
1712    }
1713
1714    pub fn stop_propagation(&mut self) {
1715        *self.internal_get_stop_propagation() = true;
1716    }
1717
1718    pub fn create_window(&mut self, window: WindowCreateOptions) {
1719        self.internal_get_new_windows().push(window);
1720    }
1721
1722    /// Starts a thread, returns Some(thread_id) if the `thread_initialize_data` is the only copy
1723    pub fn start_thread(
1724        &mut self,
1725        thread_initialize_data: RefAny,
1726        writeback_data: RefAny,
1727        callback: ThreadCallbackType,
1728    ) -> Option<ThreadId> {
1729        if thread_initialize_data.has_no_copies() {
1730            let callback = ThreadCallback { cb: callback };
1731            let thread_id = ThreadId::unique();
1732            let thread = (self
1733                .internal_get_extern_system_callbacks()
1734                .create_thread_fn
1735                .cb)(thread_initialize_data, writeback_data, callback);
1736            self.internal_get_threads().insert(thread_id, thread);
1737            Some(thread_id)
1738        } else {
1739            None
1740        }
1741    }
1742
1743    #[cfg(feature = "std")]
1744    pub fn send_thread_msg(&mut self, thread_id: ThreadId, msg: ThreadSendMsg) -> bool {
1745        if let Some(thread) = self.internal_get_threads().get_mut(&thread_id) {
1746            if let Some(s) = thread.ptr.lock().ok() {
1747                s.sender.send(msg).is_ok()
1748            } else {
1749                false
1750            }
1751        } else {
1752            false
1753        }
1754    }
1755
1756    /// Removes and stops a thread, sending one last `ThreadSendMsg::TerminateThread`
1757    pub fn stop_thread(&mut self, thread_id: ThreadId) -> bool {
1758        self.internal_get_threads_removed().insert(thread_id)
1759    }
1760
1761    pub fn start_timer(&mut self, timer: Timer) -> TimerId {
1762        let timer_id = TimerId::unique();
1763        // TODO: perform sanity checks (timer should not be created in the past, etc.)
1764        self.internal_get_timers().insert(timer_id, timer);
1765        timer_id
1766    }
1767
1768    pub fn start_animation(
1769        &mut self,
1770        dom_node_id: DomNodeId,
1771        animation: Animation,
1772    ) -> Option<TimerId> {
1773        use crate::task::SystemTimeDiff;
1774
1775        let layout_result = self
1776            .internal_get_layout_results()
1777            .get(dom_node_id.dom.inner)?;
1778        let nid = dom_node_id.node.into_crate_internal()?;
1779
1780        // timer duration may not be the animation duration if the animatio is infinitely long
1781        let timer_duration = if animation.repeat == AnimationRepeat::NoRepeat {
1782            Some(animation.duration.clone())
1783        } else {
1784            None // infinite
1785        };
1786
1787        let parent_id = layout_result
1788            .styled_dom
1789            .node_hierarchy
1790            .as_container()
1791            .get(nid)?
1792            .parent_id()
1793            .unwrap_or(NodeId::ZERO);
1794        let current_size = layout_result.rects.as_ref().get(nid)?.size;
1795        let parent_size = layout_result.rects.as_ref().get(nid)?.size;
1796
1797        if animation.from.get_type() != animation.to.get_type() {
1798            return None;
1799        }
1800
1801        let timer_id = TimerId::unique();
1802
1803        let now = self.get_current_time();
1804
1805        let animation_data = AnimationData {
1806            from: animation.from,
1807            to: animation.to,
1808            start: now.clone(),
1809            repeat: animation.repeat,
1810            interpolate: animation.easing,
1811            duration: animation.duration,
1812            relayout_on_finish: animation.relayout_on_finish,
1813            parent_rect_width: parent_size.width,
1814            parent_rect_height: parent_size.height,
1815            current_rect_width: current_size.width,
1816            current_rect_height: current_size.height,
1817            get_system_time_fn: self
1818                .internal_get_extern_system_callbacks()
1819                .get_system_time_fn
1820                .clone(),
1821        };
1822
1823        let timer = Timer {
1824            data: RefAny::new(animation_data),
1825            node_id: Some(dom_node_id).into(),
1826            created: now,
1827            run_count: 0,
1828            last_run: None.into(),
1829            delay: None.into(),
1830            interval: Some(AzDuration::System(SystemTimeDiff::from_millis(10))).into(),
1831            timeout: timer_duration.into(),
1832            callback: TimerCallback {
1833                cb: drive_animation_func,
1834            },
1835        };
1836
1837        self.internal_get_timers().insert(timer_id, timer);
1838
1839        Some(timer_id)
1840    }
1841
1842    pub fn stop_timer(&mut self, timer_id: TimerId) -> bool {
1843        self.internal_get_timers_removed().insert(timer_id)
1844    }
1845
1846    pub fn get_node_position(&self, node_id: DomNodeId) -> Option<PositionInfo> {
1847        let layout_result = self.internal_get_layout_results().get(node_id.dom.inner)?;
1848        let nid = node_id.node.into_crate_internal()?;
1849        let positioned_rectangles = layout_result.rects.as_ref();
1850        let positioned_rectangle = positioned_rectangles.get(nid)?;
1851        Some(positioned_rectangle.position)
1852    }
1853
1854    pub fn get_node_size(&self, node_id: DomNodeId) -> Option<LogicalSize> {
1855        let layout_result = self.internal_get_layout_results().get(node_id.dom.inner)?;
1856        let nid = node_id.node.into_crate_internal()?;
1857        let positioned_rectangles = layout_result.rects.as_ref();
1858        let positioned_rectangle = positioned_rectangles.get(nid)?;
1859        Some(positioned_rectangle.size)
1860    }
1861
1862    /// Adds an image to the internal image cache
1863    pub fn add_image(&mut self, css_id: AzString, image: ImageRef) {
1864        self.internal_get_image_cache()
1865            .add_css_image_id(css_id, image);
1866    }
1867
1868    pub fn has_image(&self, css_id: &AzString) -> bool {
1869        self.internal_get_image_cache_ref()
1870            .get_css_image_id(css_id)
1871            .is_some()
1872    }
1873
1874    pub fn get_image(&self, css_id: &AzString) -> Option<ImageRef> {
1875        self.internal_get_image_cache_ref()
1876            .get_css_image_id(css_id)
1877            .cloned()
1878    }
1879
1880    /// Deletes an image from the internal image cache
1881    pub fn delete_image(&mut self, css_id: &AzString) {
1882        self.internal_get_image_cache().delete_css_image_id(css_id);
1883    }
1884
1885    pub fn update_image(
1886        &mut self,
1887        node_id: DomNodeId,
1888        new_image: ImageRef,
1889        image_type: UpdateImageType,
1890    ) {
1891        if let Some(nid) = node_id.node.into_crate_internal() {
1892            self.internal_get_images_changed_in_callbacks()
1893                .entry(node_id.dom)
1894                .or_insert_with(|| BTreeMap::new())
1895                .insert(nid, (new_image, image_type));
1896        }
1897    }
1898
1899    pub fn update_image_mask(&mut self, node_id: DomNodeId, new_image_mask: ImageMask) {
1900        if let Some(nid) = node_id.node.into_crate_internal() {
1901            self.internal_get_image_masks_changed_in_callbacks()
1902                .entry(node_id.dom)
1903                .or_insert_with(|| BTreeMap::new())
1904                .insert(nid, new_image_mask);
1905        }
1906    }
1907}
1908
1909impl Clone for CallbackInfo {
1910    fn clone(&self) -> Self {
1911        Self {
1912            layout_results: self.layout_results,
1913            layout_results_count: self.layout_results_count,
1914            renderer_resources: self.renderer_resources,
1915            previous_window_state: self.previous_window_state,
1916            current_window_state: self.current_window_state,
1917            modifiable_window_state: self.modifiable_window_state,
1918            gl_context: self.gl_context,
1919            image_cache: self.image_cache,
1920            system_fonts: self.system_fonts,
1921            timers: self.timers,
1922            threads: self.threads,
1923            timers_removed: self.timers_removed,
1924            threads_removed: self.threads_removed,
1925            current_window_handle: self.current_window_handle,
1926            new_windows: self.new_windows,
1927            system_callbacks: self.system_callbacks,
1928            stop_propagation: self.stop_propagation,
1929            focus_target: self.focus_target,
1930            words_changed_in_callbacks: self.words_changed_in_callbacks,
1931            images_changed_in_callbacks: self.images_changed_in_callbacks,
1932            image_masks_changed_in_callbacks: self.image_masks_changed_in_callbacks,
1933            css_properties_changed_in_callbacks: self.css_properties_changed_in_callbacks,
1934            current_scroll_states: self.current_scroll_states,
1935            nodes_scrolled_in_callback: self.nodes_scrolled_in_callback,
1936            hit_dom_node: self.hit_dom_node,
1937            cursor_relative_to_item: self.cursor_relative_to_item,
1938            cursor_in_viewport: self.cursor_in_viewport,
1939            _abi_ref: self._abi_ref,
1940            _abi_mut: self._abi_mut,
1941        }
1942    }
1943}
1944
1945#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1946#[repr(C)]
1947pub enum UpdateImageType {
1948    Background,
1949    Content,
1950}
1951
1952#[derive(Debug, Clone, PartialEq)]
1953pub struct AnimationData {
1954    pub from: CssProperty,
1955    pub to: CssProperty,
1956    pub start: AzInstant,
1957    pub duration: AzDuration,
1958    pub repeat: AnimationRepeat,
1959    pub interpolate: AnimationInterpolationFunction,
1960    pub relayout_on_finish: bool,
1961    pub parent_rect_width: f32,
1962    pub parent_rect_height: f32,
1963    pub current_rect_width: f32,
1964    pub current_rect_height: f32,
1965    pub get_system_time_fn: GetSystemTimeCallback,
1966}
1967
1968#[derive(Debug, Clone, PartialEq)]
1969#[repr(C)]
1970pub struct Animation {
1971    pub from: CssProperty,
1972    pub to: CssProperty,
1973    pub duration: AzDuration,
1974    pub repeat: AnimationRepeat,
1975    pub repeat_times: AnimationRepeatCount,
1976    pub easing: AnimationInterpolationFunction,
1977    pub relayout_on_finish: bool,
1978}
1979
1980#[derive(Debug, Copy, Clone, PartialEq)]
1981#[repr(C)]
1982pub enum AnimationRepeat {
1983    NoRepeat,
1984    Loop,
1985    PingPong,
1986}
1987
1988#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Hash)]
1989#[repr(C, u8)]
1990pub enum AnimationRepeatCount {
1991    Times(usize),
1992    Infinite,
1993}
1994
1995// callback that drives an animation
1996extern "C" fn drive_animation_func(
1997    anim_data: &mut RefAny,
1998    info: &mut TimerCallbackInfo,
1999) -> TimerCallbackReturn {
2000    let mut anim_data = match anim_data.downcast_mut::<AnimationData>() {
2001        Some(s) => s,
2002        None => {
2003            return TimerCallbackReturn {
2004                should_update: Update::DoNothing,
2005                should_terminate: TerminateTimer::Terminate,
2006            };
2007        }
2008    };
2009
2010    let mut anim_data = &mut *anim_data;
2011
2012    let node_id = match info.node_id.into_option() {
2013        Some(s) => s,
2014        None => {
2015            return TimerCallbackReturn {
2016                should_update: Update::DoNothing,
2017                should_terminate: TerminateTimer::Terminate,
2018            };
2019        }
2020    };
2021
2022    // calculate the interpolated CSS property
2023    let resolver = InterpolateResolver {
2024        parent_rect_width: anim_data.parent_rect_width,
2025        parent_rect_height: anim_data.parent_rect_height,
2026        current_rect_width: anim_data.current_rect_width,
2027        current_rect_height: anim_data.current_rect_height,
2028        interpolate_func: anim_data.interpolate,
2029    };
2030
2031    let anim_next_end = anim_data
2032        .start
2033        .add_optional_duration(Some(&anim_data.duration));
2034    let now = (anim_data.get_system_time_fn.cb)();
2035    let t = now.linear_interpolate(anim_data.start.clone(), anim_next_end.clone());
2036    let interpolated_css = anim_data.from.interpolate(&anim_data.to, t, &resolver);
2037
2038    // actual animation happens here
2039    info.callback_info
2040        .set_css_property(node_id, interpolated_css);
2041
2042    // if the timer has finished one iteration, what next?
2043    if now > anim_next_end {
2044        match anim_data.repeat {
2045            AnimationRepeat::Loop => {
2046                // reset timer
2047                anim_data.start = now;
2048            }
2049            AnimationRepeat::PingPong => {
2050                use core::mem;
2051                // swap start and end and reset timer
2052                mem::swap(&mut anim_data.from, &mut anim_data.to);
2053                anim_data.start = now;
2054            }
2055            AnimationRepeat::NoRepeat => {
2056                // remove / cancel timer
2057                return TimerCallbackReturn {
2058                    should_terminate: TerminateTimer::Terminate,
2059                    should_update: if anim_data.relayout_on_finish {
2060                        Update::RefreshDom
2061                    } else {
2062                        Update::DoNothing
2063                    },
2064                };
2065            }
2066        }
2067    }
2068
2069    // if the timer has finished externally, what next?
2070    if info.is_about_to_finish {
2071        TimerCallbackReturn {
2072            should_terminate: TerminateTimer::Terminate,
2073            should_update: if anim_data.relayout_on_finish {
2074                Update::RefreshDom
2075            } else {
2076                Update::DoNothing
2077            },
2078        }
2079    } else {
2080        TimerCallbackReturn {
2081            should_terminate: TerminateTimer::Continue,
2082            should_update: Update::DoNothing,
2083        }
2084    }
2085}
2086
2087pub type CallbackType = extern "C" fn(&mut RefAny, &mut CallbackInfo) -> Update;
2088
2089// -- opengl callback
2090
2091/// Callbacks that returns a rendered OpenGL texture
2092#[repr(C)]
2093pub struct RenderImageCallback {
2094    pub cb: RenderImageCallbackType,
2095}
2096impl_callback!(RenderImageCallback);
2097
2098#[derive(Debug)]
2099#[repr(C)]
2100pub struct RenderImageCallbackInfo {
2101    /// The ID of the DOM node that the ImageCallback was attached to
2102    callback_node_id: DomNodeId,
2103    /// Bounds of the laid-out node
2104    bounds: HidpiAdjustedBounds,
2105    /// Optional OpenGL context pointer
2106    gl_context: *const OptionGlContextPtr,
2107    image_cache: *const ImageCache,
2108    system_fonts: *const FcFontCache,
2109    node_hierarchy: *const NodeHierarchyItemVec,
2110    words_cache: *const BTreeMap<NodeId, Words>,
2111    shaped_words_cache: *const BTreeMap<NodeId, ShapedWords>,
2112    positioned_words_cache: *const BTreeMap<NodeId, (WordPositions, FontInstanceKey)>,
2113    positioned_rects: *const NodeDataContainer<PositionedRectangle>,
2114    /// Extension for future ABI stability (referenced data)
2115    _abi_ref: *const c_void,
2116    /// Extension for future ABI stability (mutable data)
2117    _abi_mut: *mut c_void,
2118}
2119
2120// same as the implementations on CallbackInfo, just slightly adjusted for the
2121// RenderImageCallbackInfo
2122impl Clone for RenderImageCallbackInfo {
2123    fn clone(&self) -> Self {
2124        Self {
2125            callback_node_id: self.callback_node_id,
2126            bounds: self.bounds,
2127            gl_context: self.gl_context,
2128            image_cache: self.image_cache,
2129            system_fonts: self.system_fonts,
2130            node_hierarchy: self.node_hierarchy,
2131            words_cache: self.words_cache,
2132            shaped_words_cache: self.shaped_words_cache,
2133            positioned_words_cache: self.positioned_words_cache,
2134            positioned_rects: self.positioned_rects,
2135            _abi_ref: self._abi_ref,
2136            _abi_mut: self._abi_mut,
2137        }
2138    }
2139}
2140
2141impl RenderImageCallbackInfo {
2142    pub fn new<'a>(
2143        gl_context: &'a OptionGlContextPtr,
2144        image_cache: &'a ImageCache,
2145        system_fonts: &'a FcFontCache,
2146        node_hierarchy: &'a NodeHierarchyItemVec,
2147        words_cache: &'a BTreeMap<NodeId, Words>,
2148        shaped_words_cache: &'a BTreeMap<NodeId, ShapedWords>,
2149        positioned_words_cache: &'a BTreeMap<NodeId, (WordPositions, FontInstanceKey)>,
2150        positioned_rects: &'a NodeDataContainer<PositionedRectangle>,
2151        bounds: HidpiAdjustedBounds,
2152        callback_node_id: DomNodeId,
2153    ) -> Self {
2154        Self {
2155            callback_node_id,
2156            gl_context: gl_context as *const OptionGlContextPtr,
2157            image_cache: image_cache as *const ImageCache,
2158            system_fonts: system_fonts as *const FcFontCache,
2159            node_hierarchy: node_hierarchy as *const NodeHierarchyItemVec,
2160            words_cache: words_cache as *const BTreeMap<NodeId, Words>,
2161            shaped_words_cache: shaped_words_cache as *const BTreeMap<NodeId, ShapedWords>,
2162            positioned_words_cache: positioned_words_cache
2163                as *const BTreeMap<NodeId, (WordPositions, FontInstanceKey)>,
2164            positioned_rects: positioned_rects as *const NodeDataContainer<PositionedRectangle>,
2165            bounds,
2166            _abi_ref: core::ptr::null(),
2167            _abi_mut: core::ptr::null_mut(),
2168        }
2169    }
2170
2171    fn internal_get_gl_context<'a>(&'a self) -> &'a OptionGlContextPtr {
2172        unsafe { &*self.gl_context }
2173    }
2174    fn internal_get_image_cache<'a>(&'a self) -> &'a ImageCache {
2175        unsafe { &*self.image_cache }
2176    }
2177    fn internal_get_system_fonts<'a>(&'a self) -> &'a FcFontCache {
2178        unsafe { &*self.system_fonts }
2179    }
2180    fn internal_get_bounds<'a>(&'a self) -> HidpiAdjustedBounds {
2181        self.bounds
2182    }
2183    fn internal_get_node_hierarchy<'a>(&'a self) -> &'a NodeHierarchyItemVec {
2184        unsafe { &*self.node_hierarchy }
2185    }
2186    fn internal_get_words_cache<'a>(&'a self) -> &'a BTreeMap<NodeId, Words> {
2187        unsafe { &*self.words_cache }
2188    }
2189    fn internal_get_shaped_words_cache<'a>(&'a self) -> &'a BTreeMap<NodeId, ShapedWords> {
2190        unsafe { &*self.shaped_words_cache }
2191    }
2192    fn internal_get_positioned_words_cache<'a>(
2193        &'a self,
2194    ) -> &'a BTreeMap<NodeId, (WordPositions, FontInstanceKey)> {
2195        unsafe { &*self.positioned_words_cache }
2196    }
2197    fn internal_get_positioned_rectangles<'a>(
2198        &'a self,
2199    ) -> &'a NodeDataContainer<PositionedRectangle> {
2200        unsafe { &*self.positioned_rects }
2201    }
2202
2203    pub fn get_gl_context(&self) -> OptionGlContextPtr {
2204        self.internal_get_gl_context().clone()
2205    }
2206    pub fn get_bounds(&self) -> HidpiAdjustedBounds {
2207        self.internal_get_bounds()
2208    }
2209    pub fn get_callback_node_id(&self) -> DomNodeId {
2210        self.callback_node_id
2211    }
2212
2213    // fn get_font()
2214    // fn get_image()
2215
2216    pub fn get_inline_text(&self, node_id: DomNodeId) -> Option<InlineText> {
2217        if node_id.dom != self.get_callback_node_id().dom {
2218            return None;
2219        }
2220
2221        let nid = node_id.node.into_crate_internal()?;
2222        let words = self.internal_get_words_cache();
2223        let words = words.get(&nid)?;
2224        let shaped_words = self.internal_get_shaped_words_cache();
2225        let shaped_words = shaped_words.get(&nid)?;
2226        let word_positions = self.internal_get_positioned_words_cache();
2227        let word_positions = word_positions.get(&nid)?;
2228        let positioned_rectangle = self.internal_get_positioned_rectangles();
2229        let positioned_rectangle = positioned_rectangle.as_ref();
2230        let positioned_rectangle = positioned_rectangle.get(nid)?;
2231        let (_, inline_text_layout) = positioned_rectangle.resolved_text_layout_options.as_ref()?;
2232
2233        Some(crate::app_resources::get_inline_text(
2234            &words,
2235            &shaped_words,
2236            &word_positions.0,
2237            &inline_text_layout,
2238        ))
2239    }
2240
2241    pub fn get_parent(&self, node_id: DomNodeId) -> Option<DomNodeId> {
2242        if node_id.dom != self.get_callback_node_id().dom {
2243            None
2244        } else {
2245            self.internal_get_node_hierarchy()
2246                .as_container()
2247                .get(node_id.node.into_crate_internal()?)?
2248                .parent_id()
2249                .map(|nid| DomNodeId {
2250                    dom: node_id.dom,
2251                    node: NodeHierarchyItemId::from_crate_internal(Some(nid)),
2252                })
2253        }
2254    }
2255
2256    pub fn get_previous_sibling(&self, node_id: DomNodeId) -> Option<DomNodeId> {
2257        if node_id.dom != self.get_callback_node_id().dom {
2258            None
2259        } else {
2260            self.internal_get_node_hierarchy()
2261                .as_container()
2262                .get(node_id.node.into_crate_internal()?)?
2263                .previous_sibling_id()
2264                .map(|nid| DomNodeId {
2265                    dom: node_id.dom,
2266                    node: NodeHierarchyItemId::from_crate_internal(Some(nid)),
2267                })
2268        }
2269    }
2270
2271    pub fn get_next_sibling(&self, node_id: DomNodeId) -> Option<DomNodeId> {
2272        if node_id.dom != self.get_callback_node_id().dom {
2273            None
2274        } else {
2275            self.internal_get_node_hierarchy()
2276                .as_container()
2277                .get(node_id.node.into_crate_internal()?)?
2278                .next_sibling_id()
2279                .map(|nid| DomNodeId {
2280                    dom: node_id.dom,
2281                    node: NodeHierarchyItemId::from_crate_internal(Some(nid)),
2282                })
2283        }
2284    }
2285
2286    pub fn get_first_child(&self, node_id: DomNodeId) -> Option<DomNodeId> {
2287        if node_id.dom != self.get_callback_node_id().dom {
2288            None
2289        } else {
2290            let nid = node_id.node.into_crate_internal()?;
2291            self.internal_get_node_hierarchy()
2292                .as_container()
2293                .get(nid)?
2294                .first_child_id(nid)
2295                .map(|nid| DomNodeId {
2296                    dom: node_id.dom,
2297                    node: NodeHierarchyItemId::from_crate_internal(Some(nid)),
2298                })
2299        }
2300    }
2301
2302    pub fn get_last_child(&self, node_id: DomNodeId) -> Option<DomNodeId> {
2303        if node_id.dom != self.get_callback_node_id().dom {
2304            None
2305        } else {
2306            self.internal_get_node_hierarchy()
2307                .as_container()
2308                .get(node_id.node.into_crate_internal()?)?
2309                .last_child_id()
2310                .map(|nid| DomNodeId {
2311                    dom: node_id.dom,
2312                    node: NodeHierarchyItemId::from_crate_internal(Some(nid)),
2313                })
2314        }
2315    }
2316}
2317
2318/// Callback that - given the width and height of the expected image - renders an image
2319pub type RenderImageCallbackType =
2320    extern "C" fn(&mut RefAny, &mut RenderImageCallbackInfo) -> ImageRef;
2321
2322// -- iframe callback
2323
2324pub type IFrameCallbackType =
2325    extern "C" fn(&mut RefAny, &mut IFrameCallbackInfo) -> IFrameCallbackReturn;
2326
2327/// Callback that, given a rectangle area on the screen, returns the DOM
2328/// appropriate for that bounds (useful for infinite lists)
2329#[repr(C)]
2330pub struct IFrameCallback {
2331    pub cb: IFrameCallbackType,
2332}
2333impl_callback!(IFrameCallback);
2334
2335#[derive(Debug)]
2336#[repr(C)]
2337pub struct IFrameCallbackInfo {
2338    pub system_fonts: *const FcFontCache,
2339    pub image_cache: *const ImageCache,
2340    pub window_theme: WindowTheme,
2341    pub bounds: HidpiAdjustedBounds,
2342    pub scroll_size: LogicalSize,
2343    pub scroll_offset: LogicalPosition,
2344    pub virtual_scroll_size: LogicalSize,
2345    pub virtual_scroll_offset: LogicalPosition,
2346    /// Extension for future ABI stability (referenced data)
2347    _abi_ref: *const c_void,
2348    /// Extension for future ABI stability (mutable data)
2349    _abi_mut: *mut c_void,
2350}
2351
2352impl Clone for IFrameCallbackInfo {
2353    fn clone(&self) -> Self {
2354        Self {
2355            system_fonts: self.system_fonts,
2356            image_cache: self.image_cache,
2357            window_theme: self.window_theme,
2358            bounds: self.bounds,
2359            scroll_size: self.scroll_size,
2360            scroll_offset: self.scroll_offset,
2361            virtual_scroll_size: self.virtual_scroll_size,
2362            virtual_scroll_offset: self.virtual_scroll_offset,
2363            _abi_ref: self._abi_ref,
2364            _abi_mut: self._abi_mut,
2365        }
2366    }
2367}
2368
2369impl IFrameCallbackInfo {
2370    pub fn new<'a>(
2371        system_fonts: &'a FcFontCache,
2372        image_cache: &'a ImageCache,
2373        window_theme: WindowTheme,
2374        bounds: HidpiAdjustedBounds,
2375        scroll_size: LogicalSize,
2376        scroll_offset: LogicalPosition,
2377        virtual_scroll_size: LogicalSize,
2378        virtual_scroll_offset: LogicalPosition,
2379    ) -> Self {
2380        Self {
2381            system_fonts: system_fonts as *const FcFontCache,
2382            image_cache: image_cache as *const ImageCache,
2383            window_theme,
2384            bounds,
2385            scroll_size,
2386            scroll_offset,
2387            virtual_scroll_size,
2388            virtual_scroll_offset,
2389            _abi_ref: core::ptr::null(),
2390            _abi_mut: core::ptr::null_mut(),
2391        }
2392    }
2393
2394    pub fn get_bounds(&self) -> HidpiAdjustedBounds {
2395        self.bounds
2396    }
2397
2398    // fn get_font()
2399    // fn get_image()
2400
2401    fn internal_get_system_fonts<'a>(&'a self) -> &'a FcFontCache {
2402        unsafe { &*self.system_fonts }
2403    }
2404    fn internal_get_image_cache<'a>(&'a self) -> &'a ImageCache {
2405        unsafe { &*self.image_cache }
2406    }
2407}
2408
2409#[derive(Debug, Clone, PartialEq)]
2410#[repr(C)]
2411pub struct IFrameCallbackReturn {
2412    pub dom: StyledDom,
2413    pub scroll_size: LogicalSize,
2414    pub scroll_offset: LogicalPosition,
2415    pub virtual_scroll_size: LogicalSize,
2416    pub virtual_scroll_offset: LogicalPosition,
2417}
2418
2419impl Default for IFrameCallbackReturn {
2420    fn default() -> IFrameCallbackReturn {
2421        IFrameCallbackReturn {
2422            dom: StyledDom::default(),
2423            scroll_size: LogicalSize::zero(),
2424            scroll_offset: LogicalPosition::zero(),
2425            virtual_scroll_size: LogicalSize::zero(),
2426            virtual_scroll_offset: LogicalPosition::zero(),
2427        }
2428    }
2429}
2430
2431// --  thread callback
2432pub type ThreadCallbackType = extern "C" fn(RefAny, ThreadSender, ThreadReceiver);
2433
2434#[repr(C)]
2435pub struct ThreadCallback {
2436    pub cb: ThreadCallbackType,
2437}
2438impl_callback!(ThreadCallback);
2439
2440// -- timer callback
2441
2442/// Callback that can runs on every frame on the main thread - can modify the app data model
2443#[repr(C)]
2444pub struct TimerCallback {
2445    pub cb: TimerCallbackType,
2446}
2447impl_callback!(TimerCallback);
2448
2449#[derive(Debug)]
2450#[repr(C)]
2451pub struct TimerCallbackInfo {
2452    /// Callback info for this timer
2453    pub callback_info: CallbackInfo,
2454    /// If the timer is attached to a DOM node, this will contain the node ID
2455    pub node_id: OptionDomNodeId,
2456    /// Time when the frame was started rendering
2457    pub frame_start: Instant,
2458    /// How many times this callback has been called
2459    pub call_count: usize,
2460    /// Set to true ONCE on the LAST invocation of the timer (if the timer has a timeout set)
2461    /// This is useful to rebuild the DOM once the timer (usually an animation) has finished.
2462    pub is_about_to_finish: bool,
2463    /// Extension for future ABI stability (referenced data)
2464    pub(crate) _abi_ref: *const c_void,
2465    /// Extension for future ABI stability (mutable data)
2466    pub(crate) _abi_mut: *mut c_void,
2467}
2468
2469impl Clone for TimerCallbackInfo {
2470    fn clone(&self) -> Self {
2471        Self {
2472            callback_info: self.callback_info.clone(),
2473            node_id: self.node_id,
2474            frame_start: self.frame_start.clone(),
2475            call_count: self.call_count,
2476            is_about_to_finish: self.is_about_to_finish,
2477            _abi_ref: self._abi_ref,
2478            _abi_mut: self._abi_mut,
2479        }
2480    }
2481}
2482
2483pub type WriteBackCallbackType = extern "C" fn(
2484    /* original data */ &mut RefAny,
2485    /* data to write back */ &mut RefAny,
2486    &mut CallbackInfo,
2487) -> Update;
2488
2489/// Callback that can runs when a thread receives a `WriteBack` message
2490#[repr(C)]
2491pub struct WriteBackCallback {
2492    pub cb: WriteBackCallbackType,
2493}
2494impl_callback!(WriteBackCallback);
2495
2496#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
2497#[repr(C)]
2498pub struct TimerCallbackReturn {
2499    pub should_update: Update,
2500    pub should_terminate: TerminateTimer,
2501}
2502
2503pub type TimerCallbackType = extern "C" fn(
2504    /* timer internal data */ &mut RefAny,
2505    &mut TimerCallbackInfo,
2506) -> TimerCallbackReturn;
2507
2508/// Gives the `layout()` function access to the `RendererResources` and the `Window`
2509/// (for querying images and fonts, as well as width / height)
2510#[derive(Debug)]
2511#[repr(C)]
2512pub struct LayoutCallbackInfo {
2513    /// Window size (so that apps can return a different UI depending on
2514    /// the window size - mobile / desktop view). Should be later removed
2515    /// in favor of "resize" handlers and @media queries.
2516    pub window_size: WindowSize,
2517    /// Registers whether the UI is dependent on the window theme
2518    pub theme: WindowTheme,
2519    /// Allows the layout() function to reference image IDs
2520    image_cache: *const ImageCache,
2521    /// OpenGL context so that the layout() function can render textures
2522    pub gl_context: *const OptionGlContextPtr,
2523    /// Reference to the system font cache
2524    system_fonts: *const FcFontCache,
2525    /// Extension for future ABI stability (referenced data)
2526    _abi_ref: *const c_void,
2527    /// Extension for future ABI stability (mutable data)
2528    _abi_mut: *mut c_void,
2529}
2530
2531impl Clone for LayoutCallbackInfo {
2532    fn clone(&self) -> Self {
2533        Self {
2534            window_size: self.window_size,
2535            theme: self.theme,
2536            image_cache: self.image_cache,
2537            gl_context: self.gl_context,
2538            system_fonts: self.system_fonts,
2539            _abi_ref: self._abi_ref,
2540            _abi_mut: self._abi_mut,
2541        }
2542    }
2543}
2544
2545impl LayoutCallbackInfo {
2546    pub fn new<'a>(
2547        window_size: WindowSize,
2548        theme: WindowTheme,
2549        image_cache: &'a ImageCache,
2550        gl_context: &'a OptionGlContextPtr,
2551        fc_cache: &'a FcFontCache,
2552    ) -> Self {
2553        Self {
2554            window_size,
2555            theme,
2556            image_cache: image_cache as *const ImageCache,
2557            gl_context: gl_context as *const OptionGlContextPtr,
2558            system_fonts: fc_cache as *const FcFontCache,
2559            _abi_ref: core::ptr::null(),
2560            _abi_mut: core::ptr::null_mut(),
2561        }
2562    }
2563
2564    fn internal_get_image_cache<'a>(&'a self) -> &'a ImageCache {
2565        unsafe { &*self.image_cache }
2566    }
2567    fn internal_get_system_fonts<'a>(&'a self) -> &'a FcFontCache {
2568        unsafe { &*self.system_fonts }
2569    }
2570    fn internal_get_gl_context<'a>(&'a self) -> &'a OptionGlContextPtr {
2571        unsafe { &*self.gl_context }
2572    }
2573
2574    pub fn get_gl_context(&self) -> OptionGlContextPtr {
2575        self.internal_get_gl_context().clone()
2576    }
2577
2578    pub fn get_system_fonts(&self) -> Vec<AzStringPair> {
2579        let fc_cache = self.internal_get_system_fonts();
2580
2581        fc_cache
2582            .list()
2583            .iter()
2584            .filter_map(|(pattern, font_id)| {
2585                let source = fc_cache.get_font_by_id(font_id)?;
2586                match source {
2587                    FontSource::Memory(f) => None,
2588                    FontSource::Disk(d) => Some((pattern.name.as_ref()?.clone(), d.path.clone())),
2589                }
2590            })
2591            .map(|(k, v)| AzStringPair {
2592                key: k.into(),
2593                value: v.into(),
2594            })
2595            .collect()
2596    }
2597
2598    pub fn get_image(&self, image_id: &AzString) -> Option<ImageRef> {
2599        self.internal_get_image_cache()
2600            .get_css_image_id(image_id)
2601            .cloned()
2602    }
2603}
2604
2605/// Information about the bounds of a laid-out div rectangle.
2606///
2607/// Necessary when invoking `IFrameCallbacks` and `RenderImageCallbacks`, so
2608/// that they can change what their content is based on their size.
2609#[derive(Debug, Copy, Clone)]
2610#[repr(C)]
2611pub struct HidpiAdjustedBounds {
2612    pub logical_size: LogicalSize,
2613    pub hidpi_factor: f32,
2614}
2615
2616impl HidpiAdjustedBounds {
2617    #[inline(always)]
2618    pub fn from_bounds(bounds: LayoutSize, hidpi_factor: f32) -> Self {
2619        let logical_size = LogicalSize::new(bounds.width as f32, bounds.height as f32);
2620        Self {
2621            logical_size,
2622            hidpi_factor,
2623        }
2624    }
2625
2626    pub fn get_physical_size(&self) -> PhysicalSize<u32> {
2627        // NOTE: hidpi factor, not system_hidpi_factor!
2628        self.get_logical_size().to_physical(self.hidpi_factor)
2629    }
2630
2631    pub fn get_logical_size(&self) -> LogicalSize {
2632        self.logical_size
2633    }
2634
2635    pub fn get_hidpi_factor(&self) -> f32 {
2636        self.hidpi_factor
2637    }
2638}
2639
2640/// Defines the focus_targeted node ID for the next frame
2641#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2642#[repr(C, u8)]
2643pub enum FocusTarget {
2644    Id(DomNodeId),
2645    Path(FocusTargetPath),
2646    Previous,
2647    Next,
2648    First,
2649    Last,
2650    NoFocus,
2651}
2652
2653#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2654#[repr(C)]
2655pub struct FocusTargetPath {
2656    pub dom: DomId,
2657    pub css_path: CssPath,
2658}
2659
2660impl FocusTarget {
2661    pub fn resolve(
2662        &self,
2663        layout_results: &[LayoutResult],
2664        current_focus: Option<DomNodeId>,
2665    ) -> Result<Option<DomNodeId>, UpdateFocusWarning> {
2666        use crate::{callbacks::FocusTarget::*, style::matches_html_element};
2667
2668        if layout_results.is_empty() {
2669            return Ok(None);
2670        }
2671
2672        macro_rules! search_for_focusable_node_id {
2673            (
2674                $layout_results:expr,
2675                $start_dom_id:expr,
2676                $start_node_id:expr,
2677                $get_next_node_fn:ident
2678            ) => {{
2679                let mut start_dom_id = $start_dom_id;
2680                let mut start_node_id = $start_node_id;
2681
2682                let min_dom_id = DomId::ROOT_ID;
2683                let max_dom_id = DomId {
2684                    inner: layout_results.len() - 1,
2685                };
2686
2687                // iterate through all DOMs
2688                loop {
2689                    // 'outer_dom_iter
2690
2691                    let layout_result = $layout_results
2692                        .get(start_dom_id.inner)
2693                        .ok_or(UpdateFocusWarning::FocusInvalidDomId(start_dom_id.clone()))?;
2694
2695                    let node_id_valid = layout_result
2696                        .styled_dom
2697                        .node_data
2698                        .as_container()
2699                        .get(start_node_id)
2700                        .is_some();
2701
2702                    if !node_id_valid {
2703                        return Err(UpdateFocusWarning::FocusInvalidNodeId(
2704                            NodeHierarchyItemId::from_crate_internal(Some(start_node_id.clone())),
2705                        ));
2706                    }
2707
2708                    if layout_result.styled_dom.node_data.is_empty() {
2709                        return Err(UpdateFocusWarning::FocusInvalidDomId(start_dom_id.clone()));
2710                        // ???
2711                    }
2712
2713                    let max_node_id = NodeId::new(layout_result.styled_dom.node_data.len() - 1);
2714                    let min_node_id = NodeId::ZERO;
2715
2716                    // iterate through nodes in DOM
2717                    loop {
2718                        let current_node_id =
2719                            NodeId::new(start_node_id.index().$get_next_node_fn(1))
2720                                .max(min_node_id)
2721                                .min(max_node_id);
2722
2723                        if layout_result.styled_dom.node_data.as_container()[current_node_id]
2724                            .is_focusable()
2725                        {
2726                            return Ok(Some(DomNodeId {
2727                                dom: start_dom_id,
2728                                node: NodeHierarchyItemId::from_crate_internal(Some(
2729                                    current_node_id,
2730                                )),
2731                            }));
2732                        }
2733
2734                        if current_node_id == min_node_id && current_node_id < start_node_id {
2735                            // going in decreasing (previous) direction
2736                            if start_dom_id == min_dom_id {
2737                                // root node / root dom encountered
2738                                return Ok(None);
2739                            } else {
2740                                start_dom_id.inner -= 1;
2741                                start_node_id = NodeId::new(
2742                                    $layout_results[start_dom_id.inner]
2743                                        .styled_dom
2744                                        .node_data
2745                                        .len()
2746                                        - 1,
2747                                );
2748                                break; // continue 'outer_dom_iter
2749                            }
2750                        } else if current_node_id == max_node_id && current_node_id > start_node_id
2751                        {
2752                            // going in increasing (next) direction
2753                            if start_dom_id == max_dom_id {
2754                                // last dom / last node encountered
2755                                return Ok(None);
2756                            } else {
2757                                start_dom_id.inner += 1;
2758                                start_node_id = NodeId::ZERO;
2759                                break; // continue 'outer_dom_iter
2760                            }
2761                        } else {
2762                            start_node_id = current_node_id;
2763                        }
2764                    }
2765                }
2766            }};
2767        }
2768
2769        match self {
2770            Path(FocusTargetPath { dom, css_path }) => {
2771                let layout_result = layout_results
2772                    .get(dom.inner)
2773                    .ok_or(UpdateFocusWarning::FocusInvalidDomId(dom.clone()))?;
2774                let html_node_tree = &layout_result.styled_dom.cascade_info;
2775                let node_hierarchy = &layout_result.styled_dom.node_hierarchy;
2776                let node_data = &layout_result.styled_dom.node_data;
2777                let resolved_node_id = html_node_tree
2778                    .as_container()
2779                    .linear_iter()
2780                    .find(|node_id| {
2781                        matches_html_element(
2782                            css_path,
2783                            *node_id,
2784                            &node_hierarchy.as_container(),
2785                            &node_data.as_container(),
2786                            &html_node_tree.as_container(),
2787                            None,
2788                        )
2789                    })
2790                    .ok_or(UpdateFocusWarning::CouldNotFindFocusNode(css_path.clone()))?;
2791                Ok(Some(DomNodeId {
2792                    dom: dom.clone(),
2793                    node: NodeHierarchyItemId::from_crate_internal(Some(resolved_node_id)),
2794                }))
2795            }
2796            Id(dom_node_id) => {
2797                let layout_result = layout_results.get(dom_node_id.dom.inner).ok_or(
2798                    UpdateFocusWarning::FocusInvalidDomId(dom_node_id.dom.clone()),
2799                )?;
2800                let node_is_valid = dom_node_id
2801                    .node
2802                    .into_crate_internal()
2803                    .map(|o| {
2804                        layout_result
2805                            .styled_dom
2806                            .node_data
2807                            .as_container()
2808                            .get(o)
2809                            .is_some()
2810                    })
2811                    .unwrap_or(false);
2812
2813                if !node_is_valid {
2814                    Err(UpdateFocusWarning::FocusInvalidNodeId(
2815                        dom_node_id.node.clone(),
2816                    ))
2817                } else {
2818                    Ok(Some(dom_node_id.clone()))
2819                }
2820            }
2821            Previous => {
2822                let last_layout_dom_id = DomId {
2823                    inner: layout_results.len() - 1,
2824                };
2825
2826                // select the previous focusable element or `None`
2827                // if this was the first focusable element in the DOM
2828                let (current_focus_dom, current_focus_node_id) = match current_focus {
2829                    Some(s) => match s.node.into_crate_internal() {
2830                        Some(n) => (s.dom, n),
2831                        None => {
2832                            if let Some(layout_result) = layout_results.get(s.dom.inner) {
2833                                (
2834                                    s.dom,
2835                                    NodeId::new(layout_result.styled_dom.node_data.len() - 1),
2836                                )
2837                            } else {
2838                                (
2839                                    last_layout_dom_id,
2840                                    NodeId::new(
2841                                        layout_results[last_layout_dom_id.inner]
2842                                            .styled_dom
2843                                            .node_data
2844                                            .len()
2845                                            - 1,
2846                                    ),
2847                                )
2848                            }
2849                        }
2850                    },
2851                    None => (
2852                        last_layout_dom_id,
2853                        NodeId::new(
2854                            layout_results[last_layout_dom_id.inner]
2855                                .styled_dom
2856                                .node_data
2857                                .len()
2858                                - 1,
2859                        ),
2860                    ),
2861                };
2862
2863                search_for_focusable_node_id!(
2864                    layout_results,
2865                    current_focus_dom,
2866                    current_focus_node_id,
2867                    saturating_sub
2868                );
2869            }
2870            Next => {
2871                // select the previous focusable element or `None`
2872                // if this was the first focusable element in the DOM, select the first focusable
2873                // element
2874                let (current_focus_dom, current_focus_node_id) = match current_focus {
2875                    Some(s) => match s.node.into_crate_internal() {
2876                        Some(n) => (s.dom, n),
2877                        None => {
2878                            if layout_results.get(s.dom.inner).is_some() {
2879                                (s.dom, NodeId::ZERO)
2880                            } else {
2881                                (DomId::ROOT_ID, NodeId::ZERO)
2882                            }
2883                        }
2884                    },
2885                    None => (DomId::ROOT_ID, NodeId::ZERO),
2886                };
2887
2888                search_for_focusable_node_id!(
2889                    layout_results,
2890                    current_focus_dom,
2891                    current_focus_node_id,
2892                    saturating_add
2893                );
2894            }
2895            First => {
2896                let (current_focus_dom, current_focus_node_id) = (DomId::ROOT_ID, NodeId::ZERO);
2897                search_for_focusable_node_id!(
2898                    layout_results,
2899                    current_focus_dom,
2900                    current_focus_node_id,
2901                    saturating_add
2902                );
2903            }
2904            Last => {
2905                let last_layout_dom_id = DomId {
2906                    inner: layout_results.len() - 1,
2907                };
2908                let (current_focus_dom, current_focus_node_id) = (
2909                    last_layout_dom_id,
2910                    NodeId::new(
2911                        layout_results[last_layout_dom_id.inner]
2912                            .styled_dom
2913                            .node_data
2914                            .len()
2915                            - 1,
2916                    ),
2917                );
2918                search_for_focusable_node_id!(
2919                    layout_results,
2920                    current_focus_dom,
2921                    current_focus_node_id,
2922                    saturating_add
2923                );
2924            }
2925            NoFocus => Ok(None),
2926        }
2927    }
2928}