#[cfg(not(feature = "std"))]
use alloc::string::ToString;
use alloc::{alloc::Layout, boxed::Box, collections::BTreeMap, sync::Arc, vec::Vec};
use core::{
ffi::c_void,
fmt,
sync::atomic::{AtomicUsize, Ordering as AtomicOrdering},
};
#[cfg(feature = "std")]
use std::hash::Hash;
use azul_css::{
css::{CssPath, CssPropertyValue},
props::{
basic::{
AnimationInterpolationFunction, FontRef, InterpolateResolver, LayoutRect, LayoutSize,
},
property::{CssProperty, CssPropertyType},
},
system::SystemStyle,
AzString,
};
use rust_fontconfig::{FcFontCache, OwnedFontSource};
use crate::{
dom::{Dom, DomId, DomNodeId, EventFilter, OptionDom},
geom::{LogicalPosition, LogicalRect, LogicalSize, OptionLogicalPosition, PhysicalSize},
gl::OptionGlContextPtr,
hit_test::OverflowingScrollNode,
id::{NodeDataContainer, NodeDataContainerRef, NodeDataContainerRefMut, NodeId},
prop_cache::CssPropertyCache,
refany::{OptionRefAny, RefAny},
resources::{
DpiScaleFactor, FontInstanceKey, IdNamespace, ImageCache, ImageMask, ImageRef,
RendererResources,
},
styled_dom::{
NodeHierarchyItemId, NodeHierarchyItemVec, StyledNode,
StyledNodeVec,
},
task::{
Duration as AzDuration, GetSystemTimeCallback, Instant as AzInstant, Instant,
TerminateTimer, ThreadId, ThreadReceiver, ThreadSendMsg, TimerId,
},
window::{
AzStringPair, KeyboardState, MouseState, OptionChar, RawWindowHandle, UpdateFocusWarning,
WindowFlags, WindowSize, WindowTheme,
},
FastBTreeSet, OrderedMap,
};
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Update {
DoNothing,
RefreshDom,
RefreshDomAllWindows,
}
impl Update {
pub fn max_self(&mut self, other: Self) {
if *self == Update::DoNothing && other != Update::DoNothing {
*self = other;
} else if *self == Update::RefreshDom && other == Update::RefreshDomAllWindows {
*self = other;
}
}
}
pub type LayoutCallbackType = extern "C" fn(RefAny, LayoutCallbackInfo) -> crate::dom::Dom;
extern "C" fn default_layout_callback(_: RefAny, _: LayoutCallbackInfo) -> crate::dom::Dom {
crate::dom::Dom::create_body()
}
#[repr(C)]
pub struct LayoutCallback {
pub cb: LayoutCallbackType,
pub ctx: OptionRefAny,
}
impl_callback!(LayoutCallback, LayoutCallbackType);
impl LayoutCallback {
pub fn create<I: Into<Self>>(cb: I) -> Self {
cb.into()
}
}
crate::impl_managed_callback! {
wrapper: LayoutCallback,
info_ty: LayoutCallbackInfo,
return_ty: crate::dom::Dom,
default_ret: crate::dom::Dom::create_body(),
invoker_static: LAYOUT_CALLBACK_INVOKER,
invoker_ty: AzLayoutCallbackInvoker,
thunk_fn: az_layout_callback_thunk,
setter_fn: AzApp_setLayoutCallbackInvoker,
from_handle_fn: AzLayoutCallback_createFromHostHandle,
}
impl Default for LayoutCallback {
fn default() -> Self {
Self {
cb: default_layout_callback,
ctx: OptionRefAny::None,
}
}
}
pub type VirtualViewCallbackType = extern "C" fn(RefAny, VirtualViewCallbackInfo) -> VirtualViewReturn;
#[repr(C)]
pub struct VirtualViewCallback {
pub cb: VirtualViewCallbackType,
pub ctx: OptionRefAny,
}
impl_callback!(VirtualViewCallback, VirtualViewCallbackType);
crate::impl_managed_callback! {
wrapper: VirtualViewCallback,
info_ty: VirtualViewCallbackInfo,
return_ty: VirtualViewReturn,
default_ret: VirtualViewReturn::default(),
invoker_static: VIRTUAL_VIEW_CALLBACK_INVOKER,
invoker_ty: AzVirtualViewCallbackInvoker,
thunk_fn: az_virtual_view_callback_thunk,
setter_fn: AzApp_setVirtualViewCallbackInvoker,
from_handle_fn: AzVirtualViewCallback_createFromHostHandle,
}
impl VirtualViewCallback {
pub fn create(cb: VirtualViewCallbackType) -> Self {
Self {
cb,
ctx: OptionRefAny::None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(C, u8)]
pub enum VirtualViewCallbackReason {
InitialRender,
DomRecreated,
BoundsExpanded,
EdgeScrolled(EdgeType),
ScrollBeyondContent,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(C)]
pub enum EdgeType {
Top,
Bottom,
Left,
Right,
}
#[derive(Debug)]
#[repr(C)]
pub struct VirtualViewCallbackInfo {
pub reason: VirtualViewCallbackReason,
pub system_fonts: *const FcFontCache,
pub image_cache: *const ImageCache,
pub window_theme: WindowTheme,
pub bounds: HidpiAdjustedBounds,
pub scroll_size: LogicalSize,
pub scroll_offset: LogicalPosition,
pub virtual_scroll_size: LogicalSize,
pub virtual_scroll_offset: LogicalPosition,
callable_ptr: *const OptionRefAny,
_abi_mut: *mut c_void,
}
impl Clone for VirtualViewCallbackInfo {
fn clone(&self) -> Self {
Self {
reason: self.reason,
system_fonts: self.system_fonts,
image_cache: self.image_cache,
window_theme: self.window_theme,
bounds: self.bounds,
scroll_size: self.scroll_size,
scroll_offset: self.scroll_offset,
virtual_scroll_size: self.virtual_scroll_size,
virtual_scroll_offset: self.virtual_scroll_offset,
callable_ptr: self.callable_ptr,
_abi_mut: self._abi_mut,
}
}
}
impl VirtualViewCallbackInfo {
pub fn new<'a>(
reason: VirtualViewCallbackReason,
system_fonts: &'a FcFontCache,
image_cache: &'a ImageCache,
window_theme: WindowTheme,
bounds: HidpiAdjustedBounds,
scroll_size: LogicalSize,
scroll_offset: LogicalPosition,
virtual_scroll_size: LogicalSize,
virtual_scroll_offset: LogicalPosition,
) -> Self {
Self {
reason,
system_fonts: system_fonts as *const FcFontCache,
image_cache: image_cache as *const ImageCache,
window_theme,
bounds,
scroll_size,
scroll_offset,
virtual_scroll_size,
virtual_scroll_offset,
callable_ptr: core::ptr::null(),
_abi_mut: core::ptr::null_mut(),
}
}
pub fn set_callable_ptr(&mut self, callable: &OptionRefAny) {
self.callable_ptr = callable as *const OptionRefAny;
}
pub fn get_ctx(&self) -> OptionRefAny {
if self.callable_ptr.is_null() {
OptionRefAny::None
} else {
unsafe { (*self.callable_ptr).clone() }
}
}
pub fn get_bounds(&self) -> HidpiAdjustedBounds {
self.bounds
}
fn internal_get_system_fonts<'a>(&'a self) -> &'a FcFontCache {
unsafe { &*self.system_fonts }
}
fn internal_get_image_cache<'a>(&'a self) -> &'a ImageCache {
unsafe { &*self.image_cache }
}
}
#[derive(Debug, Clone, PartialEq)]
#[repr(C)]
pub struct VirtualViewReturn {
pub dom: OptionDom,
pub scroll_size: LogicalSize,
pub scroll_offset: LogicalPosition,
pub virtual_scroll_size: LogicalSize,
pub virtual_scroll_offset: LogicalPosition,
}
impl Default for VirtualViewReturn {
fn default() -> VirtualViewReturn {
VirtualViewReturn {
dom: OptionDom::None,
scroll_size: LogicalSize::zero(),
scroll_offset: LogicalPosition::zero(),
virtual_scroll_size: LogicalSize::zero(),
virtual_scroll_offset: LogicalPosition::zero(),
}
}
}
impl VirtualViewReturn {
pub fn with_dom(
dom: Dom,
scroll_size: LogicalSize,
scroll_offset: LogicalPosition,
virtual_scroll_size: LogicalSize,
virtual_scroll_offset: LogicalPosition,
) -> Self {
Self {
dom: OptionDom::Some(dom),
scroll_size,
scroll_offset,
virtual_scroll_size,
virtual_scroll_offset,
}
}
pub fn keep_current(
scroll_size: LogicalSize,
scroll_offset: LogicalPosition,
virtual_scroll_size: LogicalSize,
virtual_scroll_offset: LogicalPosition,
) -> Self {
Self {
dom: OptionDom::None,
scroll_size,
scroll_offset,
virtual_scroll_size,
virtual_scroll_offset,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub struct TimerCallbackReturn {
pub should_update: Update,
pub should_terminate: TerminateTimer,
}
impl TimerCallbackReturn {
pub fn create(should_update: Update, should_terminate: TerminateTimer) -> Self {
Self {
should_update,
should_terminate,
}
}
pub fn continue_unchanged() -> Self {
Self {
should_update: Update::DoNothing,
should_terminate: TerminateTimer::Continue,
}
}
pub fn continue_and_refresh_dom() -> Self {
Self {
should_update: Update::RefreshDom,
should_terminate: TerminateTimer::Continue,
}
}
pub fn terminate_unchanged() -> Self {
Self {
should_update: Update::DoNothing,
should_terminate: TerminateTimer::Terminate,
}
}
pub fn terminate_and_refresh_dom() -> Self {
Self {
should_update: Update::RefreshDom,
should_terminate: TerminateTimer::Terminate,
}
}
}
impl Default for TimerCallbackReturn {
fn default() -> Self {
Self::continue_unchanged()
}
}
#[derive(Debug)]
#[repr(C)]
pub struct LayoutCallbackInfoRefData<'a> {
pub image_cache: &'a ImageCache,
pub gl_context: &'a OptionGlContextPtr,
pub system_fonts: &'a FcFontCache,
pub system_style: Arc<SystemStyle>,
pub active_route: Option<&'a crate::resources::RouteMatch>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub enum RelayoutReason {
Initial,
RefreshDom,
Resize,
ThemeChange,
RouteChange,
Other,
}
impl Default for RelayoutReason {
fn default() -> Self { RelayoutReason::Initial }
}
#[repr(C)]
pub struct LayoutCallbackInfo {
ref_data: *const LayoutCallbackInfoRefData<'static>,
pub window_size: WindowSize,
pub theme: WindowTheme,
pub relayout_reason: RelayoutReason,
callable_ptr: *const OptionRefAny,
_abi_mut: *mut core::ffi::c_void,
}
impl Clone for LayoutCallbackInfo {
fn clone(&self) -> Self {
Self {
ref_data: self.ref_data,
window_size: self.window_size,
theme: self.theme,
relayout_reason: self.relayout_reason,
callable_ptr: self.callable_ptr,
_abi_mut: self._abi_mut,
}
}
}
impl core::fmt::Debug for LayoutCallbackInfo {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("LayoutCallbackInfo")
.field("window_size", &self.window_size)
.field("theme", &self.theme)
.field("relayout_reason", &self.relayout_reason)
.finish_non_exhaustive()
}
}
impl LayoutCallbackInfo {
pub fn new<'a>(
ref_data: &'a LayoutCallbackInfoRefData<'a>,
window_size: WindowSize,
theme: WindowTheme,
) -> Self {
Self::new_with_reason(ref_data, window_size, theme, RelayoutReason::Initial)
}
pub fn new_with_reason<'a>(
ref_data: &'a LayoutCallbackInfoRefData<'a>,
window_size: WindowSize,
theme: WindowTheme,
relayout_reason: RelayoutReason,
) -> Self {
Self {
ref_data: ref_data as *const LayoutCallbackInfoRefData<'a>
as *const LayoutCallbackInfoRefData<'static>,
window_size,
theme,
relayout_reason,
callable_ptr: core::ptr::null(),
_abi_mut: core::ptr::null_mut(),
}
}
pub fn relayout_reason(&self) -> RelayoutReason {
self.relayout_reason
}
pub fn set_callable_ptr(&mut self, callable: &OptionRefAny) {
self.callable_ptr = callable as *const OptionRefAny;
}
pub fn get_ctx(&self) -> OptionRefAny {
if self.callable_ptr.is_null() {
OptionRefAny::None
} else {
unsafe { (*self.callable_ptr).clone() }
}
}
pub fn get_system_style(&self) -> Arc<SystemStyle> {
unsafe { (*self.ref_data).system_style.clone() }
}
fn internal_get_image_cache<'a>(&'a self) -> &'a ImageCache {
unsafe { (*self.ref_data).image_cache }
}
fn internal_get_system_fonts<'a>(&'a self) -> &'a FcFontCache {
unsafe { (*self.ref_data).system_fonts }
}
fn internal_get_gl_context<'a>(&'a self) -> &'a OptionGlContextPtr {
unsafe { (*self.ref_data).gl_context }
}
pub fn get_gl_context(&self) -> OptionGlContextPtr {
self.internal_get_gl_context().clone()
}
pub fn get_system_fonts(&self) -> Vec<AzStringPair> {
let fc_cache = self.internal_get_system_fonts();
fc_cache
.list()
.into_iter()
.filter_map(|(pattern, font_id)| {
let source = fc_cache.get_font_by_id(&font_id)?;
match source {
OwnedFontSource::Memory(_) => None,
OwnedFontSource::Disk(d) => Some((pattern.name.as_ref()?.clone(), d.path.clone())),
}
})
.map(|(k, v)| AzStringPair {
key: k.into(),
value: v.into(),
})
.collect()
}
pub fn get_image(&self, image_id: &AzString) -> Option<ImageRef> {
self.internal_get_image_cache()
.get_css_image_id(image_id)
.cloned()
}
pub fn get_active_route(&self) -> Option<&crate::resources::RouteMatch> {
unsafe { (*self.ref_data).active_route }
}
pub fn get_route_param(&self, key: &str) -> Option<&AzString> {
self.get_active_route()?.get_param(key)
}
pub fn window_width_less_than(&self, px: f32) -> bool {
self.window_size.dimensions.width < px
}
pub fn window_width_greater_than(&self, px: f32) -> bool {
self.window_size.dimensions.width > px
}
pub fn window_width_between(&self, min_px: f32, max_px: f32) -> bool {
let width = self.window_size.dimensions.width;
width >= min_px && width <= max_px
}
pub fn window_height_less_than(&self, px: f32) -> bool {
self.window_size.dimensions.height < px
}
pub fn window_height_greater_than(&self, px: f32) -> bool {
self.window_size.dimensions.height > px
}
pub fn window_height_between(&self, min_px: f32, max_px: f32) -> bool {
let height = self.window_size.dimensions.height;
height >= min_px && height <= max_px
}
pub fn get_window_width(&self) -> f32 {
self.window_size.dimensions.width
}
pub fn get_window_height(&self) -> f32 {
self.window_size.dimensions.height
}
pub fn get_dpi_factor(&self) -> f32 {
self.window_size.dpi as f32 / 96.0
}
}
#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub struct HidpiAdjustedBounds {
pub logical_size: LogicalSize,
pub hidpi_factor: DpiScaleFactor,
}
impl HidpiAdjustedBounds {
#[inline(always)]
pub fn from_bounds(bounds: LayoutSize, hidpi_factor: DpiScaleFactor) -> Self {
let logical_size = LogicalSize::new(bounds.width as f32, bounds.height as f32);
Self {
logical_size,
hidpi_factor,
}
}
pub fn get_physical_size(&self) -> PhysicalSize<u32> {
self.get_logical_size()
.to_physical(self.get_hidpi_factor().inner.get())
}
pub fn get_logical_size(&self) -> LogicalSize {
self.logical_size
}
pub fn get_hidpi_factor(&self) -> DpiScaleFactor {
self.hidpi_factor
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C, u8)]
pub enum FocusTarget {
Id(DomNodeId),
Path(FocusTargetPath),
Previous,
Next,
First,
Last,
NoFocus,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub struct FocusTargetPath {
pub dom: DomId,
pub css_path: CssPath,
}
pub type CoreCallbackType = usize;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub struct CoreCallback {
pub cb: CoreCallbackType,
pub ctx: OptionRefAny,
}
impl From<CoreCallbackType> for CoreCallback {
fn from(cb: CoreCallbackType) -> Self {
CoreCallback {
cb,
ctx: OptionRefAny::None,
}
}
}
impl_option!(
CoreCallback,
OptionCoreCallback,
[Debug, Eq, Clone, PartialEq, PartialOrd, Ord, Hash]
);
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub struct CoreCallbackData {
pub event: EventFilter,
pub callback: CoreCallback,
pub refany: RefAny,
}
impl_option!(
CoreCallbackData,
OptionCoreCallbackData,
copy = false,
[Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
);
impl_vec!(CoreCallbackData, CoreCallbackDataVec, CoreCallbackDataVecDestructor, CoreCallbackDataVecDestructorType, CoreCallbackDataVecSlice, OptionCoreCallbackData);
impl_vec_clone!(
CoreCallbackData,
CoreCallbackDataVec,
CoreCallbackDataVecDestructor
);
impl_vec_mut!(CoreCallbackData, CoreCallbackDataVec);
impl_vec_debug!(CoreCallbackData, CoreCallbackDataVec);
impl_vec_partialord!(CoreCallbackData, CoreCallbackDataVec);
impl_vec_ord!(CoreCallbackData, CoreCallbackDataVec);
impl_vec_partialeq!(CoreCallbackData, CoreCallbackDataVec);
impl_vec_eq!(CoreCallbackData, CoreCallbackDataVec);
impl_vec_hash!(CoreCallbackData, CoreCallbackDataVec);
impl CoreCallbackDataVec {
#[inline]
pub fn as_container<'a>(&'a self) -> NodeDataContainerRef<'a, CoreCallbackData> {
NodeDataContainerRef {
internal: self.as_ref(),
}
}
#[inline]
pub fn as_container_mut<'a>(&'a mut self) -> NodeDataContainerRefMut<'a, CoreCallbackData> {
NodeDataContainerRefMut {
internal: self.as_mut(),
}
}
}
pub type CoreRenderImageCallbackType = usize;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub struct CoreRenderImageCallback {
pub cb: CoreRenderImageCallbackType,
pub ctx: OptionRefAny,
}
impl From<CoreRenderImageCallbackType> for CoreRenderImageCallback {
fn from(cb: CoreRenderImageCallbackType) -> Self {
CoreRenderImageCallback {
cb,
ctx: OptionRefAny::None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub struct CoreImageCallback {
pub refany: RefAny,
pub callback: CoreRenderImageCallback,
}
impl_option!(
CoreImageCallback,
OptionCoreImageCallback,
copy = false,
[Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
);