1use std::{
2 fmt,
3 sync::atomic::{AtomicUsize, Ordering},
4 collections::BTreeMap,
5 rc::Rc,
6 any::Any,
7 hash::Hash,
8 cell::{Ref as StdRef, RefMut as StdRefMut, RefCell},
9 ffi::c_void,
10};
11use azul_css::{LayoutPoint, LayoutRect, LayoutSize, CssPath};
12#[cfg(feature = "css_parser")]
13use azul_css_parser::CssPathParseError;
14use crate::{
15 FastHashMap,
16 app_resources::{AppResources, IdNamespace, Words, WordPositions, ScaledWords, LayoutedGlyphs},
17 dom::{Dom, DomPtr, DomId, TagId, NodeType, NodeData},
18 display_list::CachedDisplayList,
19 ui_state::UiState,
20 ui_description::UiDescription,
21 ui_solver::{PositionedRectangle, LayoutedRectangle, ScrolledNodes, LayoutResult},
22 id_tree::{NodeId, Node, NodeHierarchy},
23 window::{
24 WindowSize, WindowState, FullWindowState, CallbacksOfHitTest,
25 KeyboardState, MouseState, LogicalSize, PhysicalSize,
26 UpdateFocusWarning, CallCallbacksResult, ScrollStates,
27 },
28 task::{Timer, TerminateTimer, Task, TimerId},
29};
30
31#[cfg(feature = "opengl")]
32use crate::gl::Texture;
33#[cfg(feature = "opengl")]
34use gleam::gl::Gl;
35
36pub type UpdateScreen = Option<()>;
46#[allow(non_upper_case_globals)]
49pub const Redraw: Option<()> = Some(());
50#[allow(non_upper_case_globals)]
52pub const DontRedraw: Option<()> = None;
53
54#[derive(Default, Debug, PartialEq, Eq, PartialOrd, Ord)]
136pub struct Ref<T: 'static>(Rc<RefCell<T>>);
137
138impl<T: 'static> Clone for Ref<T> {
139 fn clone(&self) -> Self {
140 Ref(self.0.clone())
141 }
142}
143
144impl<T: 'static + Hash> Hash for Ref<T> {
145 fn hash<H>(&self, state: &mut H) where H: ::std::hash::Hasher {
146 let self_ptr = Rc::into_raw(self.0.clone()) as *const c_void as usize;
147 state.write_usize(self_ptr);
148 self.0.borrow().hash(state)
149 }
150}
151
152impl<T: 'static> Ref<T> {
153
154 pub fn new(data: T) -> Self {
155 Ref(Rc::new(RefCell::new(data)))
156 }
157
158 pub fn borrow(&self) -> StdRef<T> {
159 self.0.borrow()
160 }
161
162 pub fn borrow_mut(&mut self) -> StdRefMut<T> {
163 self.0.borrow_mut()
164 }
165
166 pub fn get_type_name(&self) -> &'static str {
167 use std::any;
168 any::type_name::<T>()
169 }
170
171 pub fn upcast(self) -> RefAny {
172 use std::any;
173 RefAny {
174 ptr: self.0 as Rc<dyn Any>,
175 type_name: any::type_name::<T>(),
176 }
177 }
178}
179
180impl<T: 'static> From<Ref<T>> for RefAny {
181 fn from(r: Ref<T>) -> Self {
182 r.upcast()
183 }
184}
185
186#[derive(Debug)]
187pub struct RefAny {
188 ptr: Rc<dyn Any>,
189 type_name: &'static str,
190}
191
192impl Clone for RefAny {
193 fn clone(&self) -> Self {
194 RefAny {
195 ptr: self.ptr.clone(),
196 type_name: self.type_name,
197 }
198 }
199}
200
201#[no_mangle] #[repr(C)] pub struct RefAnyPtr { pub ptr: *mut c_void }
203
204impl ::std::hash::Hash for RefAny {
205 fn hash<H>(&self, state: &mut H) where H: ::std::hash::Hasher {
206 let self_ptr = Rc::into_raw(self.ptr.clone()) as *const c_void as usize;
207 state.write_usize(self_ptr);
208 }
209}
210
211impl PartialEq for RefAny {
212 fn eq(&self, rhs: &Self) -> bool {
213 Rc::ptr_eq(&self.ptr, &rhs.ptr)
214 }
215}
216
217impl PartialOrd for RefAny {
218 fn partial_cmp(&self, rhs: &Self) -> Option<::std::cmp::Ordering> {
219 Some(self.cmp(rhs))
220 }
221}
222
223impl Ord for RefAny {
224 fn cmp(&self, rhs: &Self) -> ::std::cmp::Ordering {
225 let self_ptr = Rc::into_raw(self.ptr.clone()) as *const c_void as usize;
226 let rhs_ptr = Rc::into_raw(rhs.ptr.clone()) as *const c_void as usize;
227 self_ptr.cmp(&rhs_ptr)
228 }
229}
230
231impl Eq for RefAny { }
232
233impl RefAny {
234
235 pub fn downcast<T: 'static>(&self) -> Option<&RefCell<T>> {
237 self.ptr.downcast_ref::<RefCell<T>>()
238 }
239
240 pub fn get_type_name(&self) -> &'static str {
243 self.type_name
244 }
245}
246
247pub type PipelineSourceId = u32;
252
253pub type LayoutCallback = fn(RefAnyPtr, LayoutInfoPtr) -> DomPtr;
264
265#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
267pub struct ScrollPosition {
268 pub scroll_frame_rect: LayoutRect,
270 pub parent_rect: LayoutedRectangle,
272 pub scroll_location: LayoutPoint,
274}
275
276#[derive(Copy, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
277pub struct DocumentId {
278 pub namespace_id: IdNamespace,
279 pub id: u32
280}
281
282impl ::std::fmt::Display for DocumentId {
283 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
284 write!(f, "DocumentId {{ ns: {}, id: {} }}", self.namespace_id, self.id)
285 }
286}
287
288impl ::std::fmt::Debug for DocumentId {
289 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
290 write!(f, "{}", self)
291 }
292}
293
294
295#[derive(Copy, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
296pub struct PipelineId(pub PipelineSourceId, pub u32);
297
298impl ::std::fmt::Display for PipelineId {
299 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
300 write!(f, "PipelineId({}, {})", self.0, self.1)
301 }
302}
303
304impl ::std::fmt::Debug for PipelineId {
305 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
306 write!(f, "{}", self)
307 }
308}
309
310static LAST_PIPELINE_ID: AtomicUsize = AtomicUsize::new(0);
311
312impl PipelineId {
313 pub const DUMMY: PipelineId = PipelineId(0, 0);
314
315 pub fn new() -> Self {
316 PipelineId(LAST_PIPELINE_ID.fetch_add(1, Ordering::SeqCst) as u32, 0)
317 }
318}
319
320#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
321pub struct HitTestItem {
322 pub pipeline: PipelineId,
324 pub tag: TagId,
326 pub point_in_viewport: LayoutPoint,
329 pub point_relative_to_item: LayoutPoint,
332}
333
334#[macro_export]
346macro_rules! impl_callback {($callback_value:ident) => (
347
348 impl ::std::fmt::Display for $callback_value {
349 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
350 write!(f, "{:?}", self)
351 }
352 }
353
354 impl ::std::fmt::Debug for $callback_value {
355 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
356 let callback = stringify!($callback_value);
357 write!(f, "{} @ 0x{:x}", callback, self.0 as usize)
358 }
359 }
360
361 impl Clone for $callback_value {
362 fn clone(&self) -> Self {
363 $callback_value(self.0.clone())
364 }
365 }
366
367 impl ::std::hash::Hash for $callback_value {
368 fn hash<H>(&self, state: &mut H) where H: ::std::hash::Hasher {
369 state.write_usize(self.0 as usize);
370 }
371 }
372
373 impl PartialEq for $callback_value {
374 fn eq(&self, rhs: &Self) -> bool {
375 self.0 as usize == rhs.0 as usize
376 }
377 }
378
379 impl PartialOrd for $callback_value {
380 fn partial_cmp(&self, other: &Self) -> Option<::std::cmp::Ordering> {
381 Some((self.0 as usize).cmp(&(other.0 as usize)))
382 }
383 }
384
385 impl Ord for $callback_value {
386 fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
387 (self.0 as usize).cmp(&(other.0 as usize))
388 }
389 }
390
391 impl Eq for $callback_value { }
392
393 impl Copy for $callback_value { }
394)}
395
396macro_rules! impl_get_gl_context {() => {
397 #[cfg(feature = "opengl")]
399 pub fn get_gl_context(&self) -> Rc<dyn Gl> {
400 self.gl_context.clone()
401 }
402};}
403
404pub struct Callback(pub CallbackType);
413impl_callback!(Callback);
414
415pub struct CallbackInfo<'a> {
417 pub state: &'a RefAny,
419 pub current_window_state: &'a FullWindowState,
421 pub modifiable_window_state: &'a mut WindowState,
423 pub layout_result: &'a BTreeMap<DomId, LayoutResult>,
425 pub scrolled_nodes: &'a BTreeMap<DomId, ScrolledNodes>,
427 pub cached_display_list: &'a CachedDisplayList,
429 #[cfg(feature = "opengl")]
431 pub gl_context: Rc<dyn Gl>,
432 pub resources : &'a mut AppResources,
434 pub timers: &'a mut FastHashMap<TimerId, Timer>,
436 pub tasks: &'a mut Vec<Task>,
438 pub ui_state: &'a BTreeMap<DomId, UiState>,
440 pub stop_propagation: &'a mut bool,
442 pub focus_target: &'a mut Option<FocusTarget>,
445 pub current_scroll_states: &'a BTreeMap<DomId, BTreeMap<NodeId, ScrollPosition>>,
447 pub nodes_scrolled_in_callback: &'a mut BTreeMap<DomId, BTreeMap<NodeId, LayoutPoint>>,
449 pub hit_dom_node: (DomId, NodeId),
453 pub cursor_relative_to_item: Option<(f32, f32)>,
455 pub cursor_in_viewport: Option<(f32, f32)>,
457}
458pub type CallbackReturn = UpdateScreen;
459pub type CallbackType = fn(CallbackInfo) -> CallbackReturn;
460
461impl<'a> fmt::Debug for CallbackInfo<'a> {
462 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
463 write!(f, "CallbackInfo {{
464 data: {{ .. }}, \
465 current_window_state: {:?}, \
466 modifiable_window_state: {:?}, \
467 layout_result: {:?}, \
468 scrolled_nodes: {:?}, \
469 cached_display_list: {:?}, \
470 gl_context: {{ .. }}, \
471 resources: {{ .. }}, \
472 timers: {{ .. }}, \
473 tasks: {{ .. }}, \
474 ui_state: {:?}, \
475 focus_target: {:?}, \
476 current_scroll_states: {:?}, \
477 nodes_scrolled_in_callback: {:?}, \
478 hit_dom_node: {:?}, \
479 cursor_relative_to_item: {:?}, \
480 cursor_in_viewport: {:?}, \
481 }}",
482 self.current_window_state,
483 self.modifiable_window_state,
484 self.layout_result,
485 self.scrolled_nodes,
486 self.cached_display_list,
487 self.ui_state,
488 self.focus_target,
489 self.current_scroll_states,
490 self.nodes_scrolled_in_callback,
491 self.hit_dom_node,
492 self.cursor_relative_to_item,
493 self.cursor_in_viewport,
494 )
495 }
496}
497
498impl<'a> CallbackInfo<'a> {
499 pub fn stop_propagation(&mut self) {
503 *self.stop_propagation = true;
504 }
505}
506
507#[cfg(feature = "opengl")]
511pub struct GlCallback(pub GlCallbackType);
512#[cfg(feature = "opengl")]
513impl_callback!(GlCallback);
514
515pub struct GlCallbackInfo<'a> {
516 pub state: &'a RefAny,
517 pub layout_info: LayoutInfo<'a>,
518 pub bounds: HidpiAdjustedBounds,
519}
520
521#[cfg(feature = "opengl")]
522pub type GlCallbackReturn = Option<Texture>;
523#[cfg(feature = "opengl")]
524pub type GlCallbackType = fn(GlCallbackInfo) -> GlCallbackReturn;
525
526pub struct IFrameCallback(pub IFrameCallbackType);
531impl_callback!(IFrameCallback);
532
533pub struct IFrameCallbackInfo<'a> {
534 pub state: &'a RefAny,
535 pub layout_info: LayoutInfo<'a>,
536 pub bounds: HidpiAdjustedBounds,
537}
538
539pub type IFrameCallbackReturn = Option<Dom>; pub type IFrameCallbackType = fn(IFrameCallbackInfo) -> IFrameCallbackReturn;
541
542pub struct TimerCallback(pub TimerCallbackType);
546impl_callback!(TimerCallback);
547pub struct TimerCallbackInfo<'a> {
548 pub state: &'a mut RefAny,
549 pub app_resources: &'a mut AppResources,
550}
551pub type TimerCallbackReturn = (UpdateScreen, TerminateTimer);
552pub type TimerCallbackType = fn(TimerCallbackInfo) -> TimerCallbackReturn;
553
554#[no_mangle] #[repr(C)] pub struct LayoutInfoPtr { pub ptr: *mut c_void }
556
557pub struct LayoutInfo<'a> {
560 pub window_size: &'a WindowSize,
564 pub window_size_width_stops: &'a mut Vec<f32>,
572 pub window_size_height_stops: &'a mut Vec<f32>,
574 #[cfg(feature = "opengl")]
577 pub gl_context: Rc<dyn Gl>,
578 pub resources: &'a AppResources,
580}
581
582impl<'a> LayoutInfo<'a> {
583 impl_get_gl_context!();
584}
585
586impl<'a> LayoutInfo<'a> {
587
588 pub fn window_width_larger_than(&mut self, width: f32) -> bool {
612 self.window_size_width_stops.push(width);
613 self.window_size.get_logical_size().width > width
614 }
615
616 pub fn window_width_smaller_than(&mut self, width: f32) -> bool {
617 self.window_size_width_stops.push(width);
618 self.window_size.get_logical_size().width < width
619 }
620
621 pub fn window_height_larger_than(&mut self, height: f32) -> bool {
622 self.window_size_height_stops.push(height);
623 self.window_size.get_logical_size().height > height
624 }
625
626 pub fn window_height_smaller_than(&mut self, height: f32) -> bool {
627 self.window_size_height_stops.push(height);
628 self.window_size.get_logical_size().height < height
629 }
630}
631
632#[derive(Debug, Copy, Clone)]
637pub struct HidpiAdjustedBounds {
638 pub logical_size: LogicalSize,
639 pub hidpi_factor: f32,
640}
641
642impl HidpiAdjustedBounds {
643
644 #[inline(always)]
645 pub fn from_bounds(bounds: LayoutSize, hidpi_factor: f32) -> Self {
646 let logical_size = LogicalSize::new(bounds.width, bounds.height);
647 Self {
648 logical_size,
649 hidpi_factor,
650 }
651 }
652
653 pub fn get_physical_size(&self) -> PhysicalSize<u32> {
654 self.get_logical_size().to_physical(self.hidpi_factor)
655 }
656
657 pub fn get_logical_size(&self) -> LogicalSize {
658 LogicalSize::new(
660 self.logical_size.width * self.hidpi_factor,
661 self.logical_size.height * self.hidpi_factor
662 )
663 }
664
665 pub fn get_hidpi_factor(&self) -> f32 {
666 self.hidpi_factor
667 }
668}
669
670#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
672pub enum FocusTarget {
673 Id((DomId, NodeId)),
674 Path((DomId, CssPath)),
675 NoFocus,
676}
677
678impl FocusTarget {
679 pub fn resolve(
680 &self,
681 ui_descriptions: &BTreeMap<DomId, UiDescription>,
682 ui_states: &BTreeMap<DomId, UiState>,
683 ) -> Result<Option<(DomId, NodeId)>, UpdateFocusWarning> {
684
685 use crate::callbacks::FocusTarget::*;
686 use crate::style::matches_html_element;
687
688 match self {
689 Id((dom_id, node_id)) => {
690 let ui_state = ui_states.get(&dom_id).ok_or(UpdateFocusWarning::FocusInvalidDomId(dom_id.clone()))?;
691 let _ = ui_state.dom.arena.node_data.get(*node_id).ok_or(UpdateFocusWarning::FocusInvalidNodeId(*node_id))?;
692 Ok(Some((dom_id.clone(), *node_id)))
693 },
694 NoFocus => Ok(None),
695 Path((dom_id, css_path)) => {
696 let ui_state = ui_states.get(&dom_id).ok_or(UpdateFocusWarning::FocusInvalidDomId(dom_id.clone()))?;
697 let ui_description = ui_descriptions.get(&dom_id).ok_or(UpdateFocusWarning::FocusInvalidDomId(dom_id.clone()))?;
698 let html_node_tree = &ui_description.html_tree;
699 let node_hierarchy = &ui_state.dom.arena.node_hierarchy;
700 let node_data = &ui_state.dom.arena.node_data;
701 let resolved_node_id = html_node_tree
702 .linear_iter()
703 .find(|node_id| matches_html_element(css_path, *node_id, &node_hierarchy, &node_data, &html_node_tree))
704 .ok_or(UpdateFocusWarning::CouldNotFindFocusNode(css_path.clone()))?;
705 Ok(Some((dom_id.clone(), resolved_node_id)))
706 },
707 }
708 }
709}
710
711impl<'a> CallbackInfo<'a> {
712 impl_callback_info_api!();
713 impl_task_api!();
714 impl_get_gl_context!();
715}
716
717pub struct ParentNodesIterator<'a> {
720 ui_state: &'a BTreeMap<DomId, UiState>,
721 current_item: (DomId, NodeId),
722}
723
724impl<'a> ParentNodesIterator<'a> {
725
726 pub fn current_node(&self) -> (DomId, NodeId) {
728 self.current_item.clone()
729 }
730
731 pub fn current_index_in_parent(&self) -> Option<usize> {
733 let node_layout = &self.ui_state[&self.current_item.0].dom.arena.node_hierarchy;
734 if node_layout[self.current_item.1].parent.is_some() {
735 Some(node_layout.get_index_in_parent(self.current_item.1))
736 } else {
737 None
738 }
739 }
740}
741
742impl<'a> Iterator for ParentNodesIterator<'a> {
743 type Item = (DomId, NodeId);
744
745 fn next(&mut self) -> Option<(DomId, NodeId)> {
747 let parent_node_id = self.ui_state[&self.current_item.0].dom.arena.node_hierarchy[self.current_item.1].parent?;
748 self.current_item.1 = parent_node_id;
749 Some((self.current_item.0.clone(), parent_node_id))
750 }
751}
752
753#[cfg(feature = "opengl")]
755pub fn call_callbacks(
756 callbacks_filter_list: &BTreeMap<DomId, CallbacksOfHitTest>,
757 ui_state_map: &BTreeMap<DomId, UiState>,
758 ui_description_map: &BTreeMap<DomId, UiDescription>,
759 timers: &mut FastHashMap<TimerId, Timer>,
760 tasks: &mut Vec<Task>,
761 scroll_states: &BTreeMap<DomId, BTreeMap<NodeId, ScrollPosition>>,
762 modifiable_scroll_states: &mut ScrollStates,
763 full_window_state: &mut FullWindowState,
764 layout_result: &BTreeMap<DomId, LayoutResult>,
765 scrolled_nodes: &BTreeMap<DomId, ScrolledNodes>,
766 cached_display_list: &CachedDisplayList,
767 gl_context: Rc<dyn Gl>,
768 resources: &mut AppResources,
769) -> CallCallbacksResult {
770
771 let mut ret = CallCallbacksResult {
772 needs_restyle_hover_active: callbacks_filter_list.values().any(|v| v.needs_redraw_anyways),
773 needs_relayout_hover_active: callbacks_filter_list.values().any(|v| v.needs_relayout_anyways),
774 needs_restyle_focus_changed: false,
775 should_scroll_render: false,
776 callbacks_update_screen: DontRedraw,
777 modified_window_state: full_window_state.clone().into(),
778 };
779 let mut new_focus_target = None;
780 let mut nodes_scrolled_in_callbacks = BTreeMap::new();
781
782 for (dom_id, callbacks_of_hit_test) in callbacks_filter_list.iter() {
785
786 let ui_state = match ui_state_map.get(dom_id) {
787 Some(s) => s,
788 None => continue,
789 };
790
791 let mut callbacks_grouped_by_event_type = BTreeMap::new();
794
795 for (node_id, determine_callback_result) in callbacks_of_hit_test.nodes_with_callbacks.iter() {
796 for (event_filter, callback) in determine_callback_result.normal_callbacks.iter() {
797 callbacks_grouped_by_event_type
798 .entry(event_filter)
799 .or_insert_with(|| Vec::new())
800 .push((node_id, callback));
801 }
802 }
803
804 'outer: for (event_filter, callback_nodes) in callbacks_grouped_by_event_type {
805
806 for (node_id, _) in callback_nodes {
810
811 let mut new_focus = None;
812 let mut stop_propagation = false;
813 let hit_item = &callbacks_of_hit_test.nodes_with_callbacks[&node_id].hit_test_item;
814
815 let callback = ui_state.get_dom().arena.node_data
816 .get(*node_id)
817 .map(|nd| nd.get_callbacks())
818 .and_then(|dc| dc.iter().find_map(|(evt, cb)| if evt == event_filter { Some(cb) } else { None }));
819
820 let (callback, callback_ptr) = match callback {
821 Some(s) => s,
822 None => continue,
823 };
824
825 let callback_return = (callback.0)(CallbackInfo {
827 state: callback_ptr,
828 current_window_state: &full_window_state,
829 modifiable_window_state: &mut ret.modified_window_state,
830 layout_result,
831 scrolled_nodes,
832 cached_display_list,
833 gl_context: gl_context.clone(),
834 resources,
835 timers,
836 tasks,
837 ui_state: ui_state_map,
838 stop_propagation: &mut stop_propagation,
839 focus_target: &mut new_focus,
840 current_scroll_states: scroll_states,
841 nodes_scrolled_in_callback: &mut nodes_scrolled_in_callbacks,
842 hit_dom_node: (dom_id.clone(), *node_id),
843 cursor_relative_to_item: hit_item.as_ref().map(|hi| (hi.point_relative_to_item.x, hi.point_relative_to_item.y)),
844 cursor_in_viewport: hit_item.as_ref().map(|hi| (hi.point_in_viewport.x, hi.point_in_viewport.y)),
845 });
846
847 if callback_return == Redraw {
848 ret.callbacks_update_screen = Redraw;
849 }
850
851 if let Some(new_focus) = new_focus.clone() {
852 new_focus_target = Some(new_focus);
853 }
854
855 if stop_propagation {
856 continue 'outer;
857 }
858 }
859 }
860 }
861
862 for (dom_id, callback_scrolled_nodes) in nodes_scrolled_in_callbacks {
864 let scrolled_nodes = match scrolled_nodes.get(&dom_id) {
865 Some(s) => s,
866 None => continue,
867 };
868
869 for (scroll_node_id, scroll_position) in &callback_scrolled_nodes {
870 let overflowing_node = match scrolled_nodes.overflowing_nodes.get(&scroll_node_id) {
871 Some(s) => s,
872 None => continue,
873 };
874
875 modifiable_scroll_states.set_scroll_position(&overflowing_node, *scroll_position);
876 ret.should_scroll_render = true;
877 }
878 }
879
880 let new_focus_node = new_focus_target.and_then(|ft| ft.resolve(&ui_description_map, &ui_state_map).ok()?);
881 let focus_has_not_changed = full_window_state.focused_node == new_focus_node;
882 if !focus_has_not_changed {
883 }
885
886 full_window_state.focused_node = new_focus_node;
888
889 ret
890}