#[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, LogicalRectVec, 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, WindowFrame, 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 == Self::DoNothing && other != Self::DoNothing)
|| (*self == Self::RefreshDom && other == Self::RefreshDomAllWindows)
{
*self = other;
}
}
}
pub type LayoutCallbackType = extern "C" fn(RefAny, LayoutCallbackInfo) -> Dom;
extern "C" fn default_layout_callback(_: RefAny, _: LayoutCallbackInfo) -> 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: Dom,
default_ret: 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, PartialOrd)]
#[repr(C)]
pub struct CaretTweenInfo {
pub past: LogicalRect,
pub current: LogicalRect,
pub t: f32,
}
pub type CaretTweenCallbackType = extern "C" fn(RefAny, CaretTweenInfo) -> LogicalRect;
#[repr(C)]
pub struct CaretTweenCallback {
pub cb: CaretTweenCallbackType,
pub ctx: OptionRefAny,
}
impl_callback!(CaretTweenCallback, CaretTweenCallbackType);
impl CaretTweenCallback {
pub fn create(cb: CaretTweenCallbackType) -> Self {
Self {
cb,
ctx: OptionRefAny::None,
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
#[repr(C)]
pub struct SelectionTweenInfo {
pub past: LogicalRectVec,
pub current: LogicalRectVec,
pub t: f32,
}
pub type SelectionTweenCallbackType = extern "C" fn(RefAny, SelectionTweenInfo) -> LogicalRectVec;
#[repr(C)]
pub struct SelectionTweenCallback {
pub cb: SelectionTweenCallbackType,
pub ctx: OptionRefAny,
}
impl_callback!(SelectionTweenCallback, SelectionTweenCallbackType);
impl SelectionTweenCallback {
pub fn create(cb: SelectionTweenCallbackType) -> Self {
Self {
cb,
ctx: OptionRefAny::None,
}
}
}
#[inline]
fn trapezoid_ease(t: f32) -> f32 {
const RAMP: f32 = 0.25;
const V: f32 = 1.0 / (1.0 - RAMP);
let t = t.clamp(0.0, 1.0);
if t < RAMP {
V * t * t / (2.0 * RAMP)
} else if t <= 1.0 - RAMP {
V * (RAMP / 2.0 + (t - RAMP))
} else {
let inv = 1.0 - t;
1.0 - V * inv * inv / (2.0 * RAMP)
}
}
#[inline]
#[allow(clippy::suboptimal_flops)]
fn lerp_rect(from: LogicalRect, to: LogicalRect, e: f32) -> LogicalRect {
LogicalRect {
origin: LogicalPosition {
x: from.origin.x + (to.origin.x - from.origin.x) * e,
y: from.origin.y + (to.origin.y - from.origin.y) * e,
},
size: LogicalSize {
width: from.size.width + (to.size.width - from.size.width) * e,
height: from.size.height + (to.size.height - from.size.height) * e,
},
}
}
#[must_use]
pub extern "C" fn default_caret_tween(_data: RefAny, info: CaretTweenInfo) -> LogicalRect {
lerp_rect(info.past, info.current, trapezoid_ease(info.t))
}
#[must_use]
pub extern "C" fn default_selection_tween(
_data: RefAny,
info: SelectionTweenInfo,
) -> LogicalRectVec {
let e = trapezoid_ease(info.t);
let past = info.past.as_ref();
let mut taken = alloc::vec![false; past.len()];
let out: Vec<LogicalRect> = info
.current
.as_ref()
.iter()
.map(|cur| {
take_same_line_rect(past, &mut taken, *cur).map_or(*cur, |p| lerp_rect(p, *cur, e))
})
.collect();
out.into()
}
fn take_same_line_rect(
past: &[LogicalRect],
taken: &mut [bool],
cur: LogicalRect,
) -> Option<LogicalRect> {
let cur_centre = cur.origin.y + cur.size.height / 2.0;
let mut best: Option<(usize, f32)> = None;
for (i, p) in past.iter().enumerate() {
if taken.get(i).copied().unwrap_or(true) {
continue;
}
let dy = (p.origin.y + p.size.height / 2.0 - cur_centre).abs();
let tolerance = p.size.height.min(cur.size.height) / 2.0;
if dy <= tolerance && best.is_none_or(|(_, best_dy)| dy < best_dy) {
best = Some((i, dy));
}
}
let (idx, _) = best?;
if let Some(slot) = taken.get_mut(idx) {
*slot = true;
}
past.get(idx).copied()
}
#[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 window_frame: WindowFrame,
pub bounds: HidpiAdjustedBounds,
pub materialized: LogicalRect,
pub virtual_rect: LogicalRect,
pub scroll_offset: LogicalPosition,
callable_ptr: *const OptionRefAny,
measure_dom_fn: *const c_void,
measure_dom_ctx: *mut c_void,
_abi_mut: *mut c_void,
}
pub type MeasureDomFn =
extern "C" fn(*mut c_void, *mut Dom, LogicalSize, MeasureDomMode) -> LogicalSize;
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[repr(C)]
pub enum MeasureDomMode {
Extent,
ShrinkToFit,
}
impl Clone for VirtualViewCallbackInfo {
#[allow(clippy::used_underscore_binding)] fn clone(&self) -> Self {
Self {
reason: self.reason,
system_fonts: self.system_fonts,
image_cache: self.image_cache,
window_theme: self.window_theme,
window_frame: self.window_frame,
bounds: self.bounds,
materialized: self.materialized,
virtual_rect: self.virtual_rect,
scroll_offset: self.scroll_offset,
callable_ptr: self.callable_ptr,
measure_dom_fn: self.measure_dom_fn,
measure_dom_ctx: self.measure_dom_ctx,
_abi_mut: self._abi_mut,
}
}
}
impl VirtualViewCallbackInfo {
#[must_use]
pub const fn new<'a>(
reason: VirtualViewCallbackReason,
system_fonts: &'a FcFontCache,
image_cache: &'a ImageCache,
window_theme: WindowTheme,
window_frame: WindowFrame,
bounds: HidpiAdjustedBounds,
materialized: LogicalRect,
virtual_rect: LogicalRect,
scroll_offset: LogicalPosition,
) -> Self {
Self {
reason,
system_fonts: core::ptr::from_ref::<FcFontCache>(system_fonts),
image_cache: core::ptr::from_ref::<ImageCache>(image_cache),
window_theme,
window_frame,
bounds,
materialized,
virtual_rect,
scroll_offset,
callable_ptr: core::ptr::null(),
measure_dom_fn: core::ptr::null(),
measure_dom_ctx: core::ptr::null_mut(),
_abi_mut: core::ptr::null_mut(),
}
}
pub const fn set_callable_ptr(&mut self, callable: &OptionRefAny) {
self.callable_ptr = core::ptr::from_ref::<OptionRefAny>(callable);
}
pub fn set_measure_dom_fn(&mut self, f: MeasureDomFn, ctx: *mut c_void) {
self.measure_dom_fn = f as *const c_void;
self.measure_dom_ctx = ctx;
}
#[must_use]
pub fn measure_dom(&self, dom: Dom, available: LogicalSize) -> LogicalSize {
if self.measure_dom_fn.is_null() {
return LogicalSize::zero();
}
let f: MeasureDomFn = unsafe { core::mem::transmute(self.measure_dom_fn) };
let mut dom = core::mem::ManuallyDrop::new(dom);
f(
self.measure_dom_ctx,
core::ptr::from_mut::<Dom>(&mut dom),
available,
MeasureDomMode::Extent,
)
}
#[must_use]
pub fn measure_dom_shrink_to_fit(&self, dom: Dom, bound: LogicalSize) -> LogicalSize {
if self.measure_dom_fn.is_null() {
return LogicalSize::zero();
}
let f: MeasureDomFn = unsafe { core::mem::transmute(self.measure_dom_fn) };
let mut dom = core::mem::ManuallyDrop::new(dom);
f(
self.measure_dom_ctx,
core::ptr::from_mut::<Dom>(&mut dom),
bound,
MeasureDomMode::ShrinkToFit,
)
}
#[must_use]
pub fn get_ctx(&self) -> OptionRefAny {
if self.callable_ptr.is_null() {
OptionRefAny::None
} else {
unsafe { (*self.callable_ptr).clone() }
}
}
#[must_use]
pub const fn get_bounds(&self) -> HidpiAdjustedBounds {
self.bounds
}
const fn internal_get_system_fonts(&self) -> &FcFontCache {
unsafe { &*self.system_fonts }
}
const fn internal_get_image_cache(&self) -> &ImageCache {
unsafe { &*self.image_cache }
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[repr(C)]
pub struct VirtualViewReturn {
pub dom: OptionDom,
pub materialized: LogicalRect,
pub virtual_rect: LogicalRect,
}
impl Default for VirtualViewReturn {
fn default() -> Self {
Self {
dom: OptionDom::None,
materialized: LogicalRect::zero(),
virtual_rect: LogicalRect::zero(),
}
}
}
impl VirtualViewReturn {
#[must_use]
pub const fn with_dom(dom: Dom, materialized: LogicalRect, virtual_rect: LogicalRect) -> Self {
Self {
dom: OptionDom::Some(dom),
materialized,
virtual_rect,
}
}
#[must_use]
pub const fn keep_current(materialized: LogicalRect, virtual_rect: LogicalRect) -> Self {
Self {
dom: OptionDom::None,
materialized,
virtual_rect,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub struct TimerCallbackReturn {
pub should_update: Update,
pub should_terminate: TerminateTimer,
}
impl TimerCallbackReturn {
#[must_use]
pub const fn create(should_update: Update, should_terminate: TerminateTimer) -> Self {
Self {
should_update,
should_terminate,
}
}
#[must_use]
pub const fn continue_unchanged() -> Self {
Self {
should_update: Update::DoNothing,
should_terminate: TerminateTimer::Continue,
}
}
#[must_use]
pub const fn continue_and_refresh_dom() -> Self {
Self {
should_update: Update::RefreshDom,
should_terminate: TerminateTimer::Continue,
}
}
#[must_use]
pub const fn terminate_unchanged() -> Self {
Self {
should_update: Update::DoNothing,
should_terminate: TerminateTimer::Terminate,
}
}
#[must_use]
pub const 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>,
pub monitors: crate::window::MonitorVec,
pub safe_area: azul_css::system::SafeAreaInsets,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
#[derive(Default)]
pub enum RelayoutReason {
#[default]
Initial,
RefreshDom,
Resize,
ThemeChange,
RouteChange,
Other,
}
#[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 c_void,
}
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct SizeQuery {
pub axis: SizeQueryAxis,
pub op: SizeQueryOp,
pub threshold_px: f32,
pub answer: bool,
}
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SizeQueryAxis {
Width,
Height,
}
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SizeQueryOp {
LessThan,
GreaterThan,
GreaterOrEqual,
LessOrEqual,
}
impl SizeQuery {
#[must_use]
pub fn answer_at(&self, size: LogicalSize) -> bool {
let dim = match self.axis {
SizeQueryAxis::Width => size.width,
SizeQueryAxis::Height => size.height,
};
match self.op {
SizeQueryOp::LessThan => dim < self.threshold_px,
SizeQueryOp::GreaterThan => dim > self.threshold_px,
SizeQueryOp::GreaterOrEqual => dim >= self.threshold_px,
SizeQueryOp::LessOrEqual => dim <= self.threshold_px,
}
}
#[must_use]
pub fn flips_at(&self, size: LogicalSize) -> bool {
self.answer_at(size) != self.answer
}
}
#[cfg(feature = "std")]
mod size_query_recorder {
use super::SizeQuery;
pub(super) const SIZE_QUERY_CAP: usize = 256;
std::thread_local! {
static RECORDED: core::cell::RefCell<(Vec<SizeQuery>, bool)> =
const { core::cell::RefCell::new((Vec::new(), false)) };
}
pub(super) fn record(q: SizeQuery) {
RECORDED.with(|r| {
let mut r = r.borrow_mut();
if r.0.len() >= SIZE_QUERY_CAP {
r.1 = true; } else {
r.0.push(q);
}
});
}
pub(super) fn take() -> (Vec<SizeQuery>, bool) {
RECORDED.with(|r| {
let mut r = r.borrow_mut();
let overflowed = r.1;
r.1 = false;
(core::mem::take(&mut r.0), overflowed)
})
}
}
#[cfg(feature = "std")]
fn record_size_query(q: SizeQuery) {
size_query_recorder::record(q);
}
#[cfg(not(feature = "std"))]
fn record_size_query(_q: SizeQuery) {}
#[cfg(feature = "std")]
#[must_use]
pub fn take_recorded_size_queries() -> (alloc::vec::Vec<SizeQuery>, bool) {
size_query_recorder::take()
}
#[cfg(not(feature = "std"))]
#[must_use]
pub fn take_recorded_size_queries() -> (alloc::vec::Vec<SizeQuery>, bool) {
(alloc::vec::Vec::new(), false)
}
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum SystemStyleDependency {
Theme,
Colors,
Fonts,
Metrics,
Icons,
Accessibility,
Everything,
}
impl SystemStyleDependency {
#[must_use]
pub const fn bit(self) -> u32 {
match self {
Self::Theme => 1 << 0,
Self::Colors => 1 << 1,
Self::Fonts => 1 << 2,
Self::Metrics => 1 << 3,
Self::Icons => 1 << 4,
Self::Accessibility => 1 << 5,
Self::Everything => u32::MAX,
}
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Hash)]
pub struct SystemStyleDependencies {
pub facets: u32,
}
impl SystemStyleDependencies {
#[must_use]
pub const fn empty() -> Self {
Self { facets: 0 }
}
#[must_use]
pub const fn all() -> Self {
Self { facets: u32::MAX }
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.facets == 0
}
pub const fn insert(&mut self, dep: SystemStyleDependency) {
self.facets |= dep.bit();
}
pub const fn union(&mut self, other: Self) {
self.facets |= other.facets;
}
#[must_use]
pub const fn contains(&self, dep: SystemStyleDependency) -> bool {
let bit = dep.bit();
self.facets & bit == bit
}
#[must_use]
pub fn dom_depends_on_change(
&self,
old: &azul_css::system::SystemStyle,
new: &azul_css::system::SystemStyle,
) -> bool {
if self.is_empty() {
return old != new;
}
if self.contains(SystemStyleDependency::Theme) && old.theme != new.theme {
return true;
}
if self.contains(SystemStyleDependency::Colors) && old.colors != new.colors {
return true;
}
if self.contains(SystemStyleDependency::Fonts) && old.fonts != new.fonts {
return true;
}
if self.contains(SystemStyleDependency::Metrics)
&& (old.metrics != new.metrics
|| old.input != new.input
|| old.focus_visuals != new.focus_visuals
|| old.scrollbar != new.scrollbar
|| old.scrollbar_preferences != new.scrollbar_preferences)
{
return true;
}
if self.contains(SystemStyleDependency::Icons)
&& (old.icon_style != new.icon_style
|| old.visual_hints != new.visual_hints
|| old.linux.icon_theme != new.linux.icon_theme)
{
return true;
}
if self.contains(SystemStyleDependency::Accessibility)
&& (old.accessibility != new.accessibility
|| old.animation != new.animation
|| old.prefers_reduced_motion != new.prefers_reduced_motion
|| old.prefers_high_contrast != new.prefers_high_contrast)
{
return true;
}
false
}
}
#[cfg(feature = "std")]
mod style_dep_recorder {
use super::SystemStyleDependencies;
std::thread_local! {
static DECLARED: core::cell::Cell<SystemStyleDependencies> =
const { core::cell::Cell::new(SystemStyleDependencies { facets: 0 }) };
}
pub(super) fn record(dep: super::SystemStyleDependency) {
DECLARED.with(|d| {
let mut set = d.get();
set.insert(dep);
d.set(set);
});
}
pub(super) fn take() -> SystemStyleDependencies {
DECLARED.with(core::cell::Cell::take)
}
}
#[cfg(feature = "std")]
fn record_style_dependency(dep: SystemStyleDependency) {
style_dep_recorder::record(dep);
}
#[cfg(not(feature = "std"))]
fn record_style_dependency(_dep: SystemStyleDependency) {}
#[cfg(feature = "std")]
#[must_use]
pub fn take_recorded_style_dependencies() -> SystemStyleDependencies {
style_dep_recorder::take()
}
#[cfg(not(feature = "std"))]
#[must_use]
pub fn take_recorded_style_dependencies() -> SystemStyleDependencies {
SystemStyleDependencies::empty()
}
impl Clone for LayoutCallbackInfo {
#[allow(clippy::used_underscore_binding)] 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 {
#[must_use]
pub const 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)
}
#[allow(clippy::unnecessary_cast)]
#[must_use]
pub const fn new_with_reason<'a>(
ref_data: &'a LayoutCallbackInfoRefData<'a>,
window_size: WindowSize,
theme: WindowTheme,
relayout_reason: RelayoutReason,
) -> Self {
Self {
ref_data: core::ptr::from_ref::<LayoutCallbackInfoRefData<'a>>(ref_data)
as *const LayoutCallbackInfoRefData<'static>,
window_size,
theme,
relayout_reason,
callable_ptr: core::ptr::null(),
_abi_mut: core::ptr::null_mut(),
}
}
#[must_use]
pub const fn relayout_reason(&self) -> RelayoutReason {
self.relayout_reason
}
#[must_use]
pub fn viewport_bigger_than(&self, width_px: f32) -> bool {
self.window_size.dimensions.width > width_px
}
#[must_use]
pub fn get_safe_area_insets(&self) -> azul_css::system::SafeAreaInsets {
unsafe { (*self.ref_data).safe_area }
}
pub const fn set_callable_ptr(&mut self, callable: &OptionRefAny) {
self.callable_ptr = core::ptr::from_ref::<OptionRefAny>(callable);
}
#[must_use]
pub fn get_ctx(&self) -> OptionRefAny {
if self.callable_ptr.is_null() {
OptionRefAny::None
} else {
unsafe { (*self.callable_ptr).clone() }
}
}
#[allow(clippy::unused_self)] pub fn depends_on_system_style(&self, dep: SystemStyleDependency) {
record_style_dependency(dep);
}
#[must_use]
pub fn get_theme(&self) -> WindowTheme {
self.depends_on_system_style(SystemStyleDependency::Theme);
self.theme
}
#[must_use]
pub fn get_system_style(&self) -> Arc<SystemStyle> {
self.depends_on_system_style(SystemStyleDependency::Everything);
self.get_system_style_untracked()
}
#[must_use]
pub fn get_system_style_untracked(&self) -> Arc<SystemStyle> {
unsafe { (*self.ref_data).system_style.clone() }
}
#[must_use]
pub fn get_monitors(&self) -> crate::window::MonitorVec {
unsafe { (*self.ref_data).monitors.clone() }
}
#[must_use]
pub fn get_max_monitor_size(&self) -> azul_css::props::basic::OptionLayoutSize {
let monitors = unsafe { &(*self.ref_data).monitors };
let mut best: Option<LayoutSize> = None;
for m in monitors.as_ref() {
let s = m.size;
let better = best.is_none_or(|b| (s.width * s.height) > (b.width * b.height));
if better {
best = Some(s);
}
}
best.into()
}
const fn internal_get_image_cache(&self) -> &ImageCache {
unsafe { (*self.ref_data).image_cache }
}
const fn internal_get_system_fonts(&self) -> &FcFontCache {
unsafe { (*self.ref_data).system_fonts }
}
const fn internal_get_gl_context(&self) -> &OptionGlContextPtr {
unsafe { (*self.ref_data).gl_context }
}
#[must_use]
pub fn get_gl_context(&self) -> OptionGlContextPtr {
self.internal_get_gl_context().clone()
}
#[must_use]
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)),
}
})
.map(|(k, v)| AzStringPair {
key: k.into(),
value: v.into(),
})
.collect()
}
#[must_use]
pub fn get_font_cache(&self) -> FcFontCache {
self.internal_get_system_fonts().clone()
}
#[must_use]
pub fn get_image(&self, image_id: &AzString) -> Option<ImageRef> {
self.internal_get_image_cache()
.get_css_image_id(image_id)
.cloned()
}
#[must_use]
pub const fn get_active_route(&self) -> Option<&crate::resources::RouteMatch> {
unsafe { (*self.ref_data).active_route }
}
#[must_use]
pub fn get_route_param(&self, key: &str) -> Option<&AzString> {
self.get_active_route()?.get_param(key)
}
#[must_use]
pub fn get_route_pattern(&self) -> AzString {
self.get_active_route().map_or_else(
|| AzString::from_const_str("/"),
|route| route.pattern.clone(),
)
}
#[allow(clippy::needless_pass_by_value)]
#[must_use]
pub fn get_route_param_or_empty(&self, key: AzString) -> AzString {
self.get_route_param(key.as_str())
.cloned()
.unwrap_or_else(|| AzString::from_const_str(""))
}
#[allow(clippy::unused_self)] fn record_width_query(&self, op: SizeQueryOp, threshold_px: f32, answer: bool) -> bool {
record_size_query(SizeQuery {
axis: SizeQueryAxis::Width,
op,
threshold_px,
answer,
});
answer
}
#[allow(clippy::unused_self)] fn record_height_query(&self, op: SizeQueryOp, threshold_px: f32, answer: bool) -> bool {
record_size_query(SizeQuery {
axis: SizeQueryAxis::Height,
op,
threshold_px,
answer,
});
answer
}
#[must_use]
pub fn window_width_less_than(&self, px: f32) -> bool {
let answer = self.window_size.dimensions.width < px;
self.record_width_query(SizeQueryOp::LessThan, px, answer)
}
#[must_use]
pub fn window_width_greater_than(&self, px: f32) -> bool {
let answer = self.window_size.dimensions.width > px;
self.record_width_query(SizeQueryOp::GreaterThan, px, answer)
}
#[must_use]
pub fn window_width_between(&self, min_px: f32, max_px: f32) -> bool {
let width = self.window_size.dimensions.width;
self.record_width_query(SizeQueryOp::GreaterOrEqual, min_px, width >= min_px)
& self.record_width_query(SizeQueryOp::LessOrEqual, max_px, width <= max_px)
}
#[must_use]
pub fn window_height_less_than(&self, px: f32) -> bool {
let answer = self.window_size.dimensions.height < px;
self.record_height_query(SizeQueryOp::LessThan, px, answer)
}
#[must_use]
pub fn window_height_greater_than(&self, px: f32) -> bool {
let answer = self.window_size.dimensions.height > px;
self.record_height_query(SizeQueryOp::GreaterThan, px, answer)
}
#[must_use]
pub fn window_height_between(&self, min_px: f32, max_px: f32) -> bool {
let height = self.window_size.dimensions.height;
self.record_height_query(SizeQueryOp::GreaterOrEqual, min_px, height >= min_px)
& self.record_height_query(SizeQueryOp::LessOrEqual, max_px, height <= max_px)
}
#[must_use]
pub const fn get_window_width(&self) -> f32 {
self.window_size.dimensions.width
}
#[must_use]
pub const fn get_window_height(&self) -> f32 {
self.window_size.dimensions.height
}
#[allow(clippy::cast_precision_loss)] #[must_use]
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]
#[allow(clippy::cast_precision_loss)] #[must_use]
pub const 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,
}
}
#[must_use]
pub fn get_physical_size(&self) -> PhysicalSize<u32> {
self.get_logical_size()
.to_physical(self.get_hidpi_factor().inner.get())
}
#[must_use]
pub const fn get_logical_size(&self) -> LogicalSize {
self.logical_size
}
#[must_use]
pub const 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,
Directional(FocusDirection),
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub enum FocusDirection {
Up,
Down,
Left,
Right,
}
#[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 {
Self {
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]
#[must_use]
pub fn as_container(&self) -> NodeDataContainerRef<'_, CoreCallbackData> {
NodeDataContainerRef {
internal: self.as_ref(),
}
}
#[inline]
pub fn as_container_mut(&mut self) -> NodeDataContainerRefMut<'_, 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 {
Self {
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]
);
#[cfg(test)]
#[path = "callbacks_test.rs"]
mod callbacks_test;