1use alloc::{
9 boxed::Box,
10 collections::{btree_map::BTreeMap, VecDeque},
11 sync::Arc,
12 vec::Vec,
13};
14
15#[cfg(feature = "std")]
16use std::sync::Mutex;
17
18use azul_core::{
19 animation::UpdateImageType,
20 callbacks::{CoreCallback, FocusTarget, FocusTargetPath, HidpiAdjustedBounds, Update},
21 dom::{DomId, DomIdVec, DomNodeId, IdOrClass, NodeId, NodeType},
22 events::CallbackResultRef,
23 geom::{LogicalPosition, LogicalRect, LogicalSize, OptionLogicalPosition},
24 gl::OptionGlContextPtr,
25 gpu::GpuValueCache,
26 hit_test::ScrollPosition,
27 id::NodeId as CoreNodeId,
28 impl_callback,
29 menu::Menu,
30 refany::{OptionRefAny, RefAny},
31 resources::{ImageCache, ImageMask, ImageRef, RendererResources},
32 selection::{Selection, SelectionRange, SelectionRangeVec, SelectionState, TextCursor},
33 styled_dom::{NodeHierarchyItemId, NodeIdVec, StyledDom},
34 task::{self, GetSystemTimeCallback, Instant, ThreadId, ThreadIdVec, TimerId, TimerIdVec},
35 window::{KeyboardState, MouseState, RawWindowHandle, WindowFlags, WindowSize},
36 FastBTreeSet, FastHashMap,
37};
38use azul_css::{
39 css::CssPath,
40 props::{
41 basic::FontRef,
42 property::{CssProperty, CssPropertyType, CssPropertyVec},
43 },
44 system::SystemStyle,
45 AzString, StringVec,
46};
47use rust_fontconfig::FcFontCache;
48
49#[cfg(feature = "icu")]
50use crate::icu::{
51 FormatLength, IcuDate, IcuDateTime, IcuLocalizerHandle, IcuResult,
52 IcuStringVec, IcuTime, ListType, PluralCategory,
53};
54
55use crate::{
56 hit_test::FullHitTest,
57 managers::{
58 drag_drop::DragDropManager,
59 file_drop::FileDropManager,
60 focus_cursor::FocusManager,
61 gesture::{GestureAndDragManager, InputSample, PenState},
62 gpu_state::GpuStateManager,
63 hover::{HoverManager, InputPointId},
64 iframe::IFrameManager,
65 scroll_state::{AnimatedScrollState, ScrollManager},
66 selection::{ClipboardContent, SelectionManager},
67 text_input::{PendingTextEdit, TextInputManager},
68 undo_redo::{UndoRedoManager, UndoableOperation},
69 },
70 text3::cache::{LayoutCache as TextLayoutCache, UnifiedLayout},
71 thread::{CreateThreadCallback, Thread},
72 timer::Timer,
73 window::{DomLayoutResult, LayoutWindow},
74 window_state::{FullWindowState, WindowCreateOptions},
75};
76
77use azul_css::{impl_option, impl_option_inner};
78
79#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
85#[repr(C)]
86pub struct PenTilt {
87 pub x_tilt: f32,
89 pub y_tilt: f32,
91}
92
93impl From<(f32, f32)> for PenTilt {
94 fn from((x, y): (f32, f32)) -> Self {
95 Self {
96 x_tilt: x,
97 y_tilt: y,
98 }
99 }
100}
101
102impl_option!(
103 PenTilt,
104 OptionPenTilt,
105 [Debug, Clone, Copy, PartialEq, PartialOrd]
106);
107
108#[derive(Debug, Clone, PartialEq)]
110#[repr(C)]
111pub struct SelectAllResult {
112 pub full_text: AzString,
114 pub selection_range: SelectionRange,
116}
117
118impl From<(alloc::string::String, SelectionRange)> for SelectAllResult {
119 fn from((text, range): (alloc::string::String, SelectionRange)) -> Self {
120 Self {
121 full_text: text.into(),
122 selection_range: range,
123 }
124 }
125}
126
127impl_option!(
128 SelectAllResult,
129 OptionSelectAllResult,
130 copy = false,
131 [Debug, Clone, PartialEq]
132);
133
134#[derive(Debug, Clone, PartialEq)]
136#[repr(C)]
137pub struct DeleteResult {
138 pub range_to_delete: SelectionRange,
140 pub deleted_text: AzString,
142}
143
144impl From<(SelectionRange, alloc::string::String)> for DeleteResult {
145 fn from((range, text): (SelectionRange, alloc::string::String)) -> Self {
146 Self {
147 range_to_delete: range,
148 deleted_text: text.into(),
149 }
150 }
151}
152
153impl_option!(
154 DeleteResult,
155 OptionDeleteResult,
156 copy = false,
157 [Debug, Clone, PartialEq]
158);
159
160#[derive(Debug, Clone)]
168pub enum CallbackChange {
169 ModifyWindowState { state: FullWindowState },
172 QueueWindowStateSequence { states: Vec<FullWindowState> },
176 CreateNewWindow { options: WindowCreateOptions },
178 CloseWindow,
180
181 SetFocusTarget { target: FocusTarget },
184
185 StopPropagation,
188 PreventDefault,
190
191 AddTimer { timer_id: TimerId, timer: Timer },
194 RemoveTimer { timer_id: TimerId },
196
197 AddThread { thread_id: ThreadId, thread: Thread },
200 RemoveThread { thread_id: ThreadId },
202
203 ChangeNodeText { node_id: DomNodeId, text: AzString },
206 ChangeNodeImage {
208 dom_id: DomId,
209 node_id: NodeId,
210 image: ImageRef,
211 update_type: UpdateImageType,
212 },
213 UpdateImageCallback { dom_id: DomId, node_id: NodeId },
216 UpdateIFrame { dom_id: DomId, node_id: NodeId },
219 ChangeNodeImageMask {
221 dom_id: DomId,
222 node_id: NodeId,
223 mask: ImageMask,
224 },
225 ChangeNodeCssProperties {
227 dom_id: DomId,
228 node_id: NodeId,
229 properties: CssPropertyVec,
230 },
231
232 ScrollTo {
235 dom_id: DomId,
236 node_id: NodeHierarchyItemId,
237 position: LogicalPosition,
238 },
239 ScrollIntoView {
242 node_id: DomNodeId,
243 options: crate::managers::scroll_into_view::ScrollIntoViewOptions,
244 },
245
246 AddImageToCache { id: AzString, image: ImageRef },
249 RemoveImageFromCache { id: AzString },
251
252 ReloadSystemFonts,
255
256 OpenMenu {
260 menu: Menu,
261 position: Option<LogicalPosition>,
263 },
264
265 ShowTooltip {
274 text: AzString,
275 position: LogicalPosition,
276 },
277 HideTooltip,
279
280 InsertText {
283 dom_id: DomId,
284 node_id: NodeId,
285 text: AzString,
286 },
287 DeleteBackward { dom_id: DomId, node_id: NodeId },
289 DeleteForward { dom_id: DomId, node_id: NodeId },
291 MoveCursor {
293 dom_id: DomId,
294 node_id: NodeId,
295 cursor: TextCursor,
296 },
297 SetSelection {
299 dom_id: DomId,
300 node_id: NodeId,
301 selection: Selection,
302 },
303 SetTextChangeset { changeset: PendingTextEdit },
306
307 MoveCursorLeft {
310 dom_id: DomId,
311 node_id: NodeId,
312 extend_selection: bool,
313 },
314 MoveCursorRight {
316 dom_id: DomId,
317 node_id: NodeId,
318 extend_selection: bool,
319 },
320 MoveCursorUp {
322 dom_id: DomId,
323 node_id: NodeId,
324 extend_selection: bool,
325 },
326 MoveCursorDown {
328 dom_id: DomId,
329 node_id: NodeId,
330 extend_selection: bool,
331 },
332 MoveCursorToLineStart {
334 dom_id: DomId,
335 node_id: NodeId,
336 extend_selection: bool,
337 },
338 MoveCursorToLineEnd {
340 dom_id: DomId,
341 node_id: NodeId,
342 extend_selection: bool,
343 },
344 MoveCursorToDocumentStart {
346 dom_id: DomId,
347 node_id: NodeId,
348 extend_selection: bool,
349 },
350 MoveCursorToDocumentEnd {
352 dom_id: DomId,
353 node_id: NodeId,
354 extend_selection: bool,
355 },
356
357 SetCopyContent {
360 target: DomNodeId,
361 content: ClipboardContent,
362 },
363 SetCutContent {
365 target: DomNodeId,
366 content: ClipboardContent,
367 },
368 SetSelectAllRange {
370 target: DomNodeId,
371 range: SelectionRange,
372 },
373
374 RequestHitTestUpdate { position: LogicalPosition },
381
382 ProcessTextSelectionClick {
391 position: LogicalPosition,
392 time_ms: u64,
393 },
394
395 SetCursorVisibility { visible: bool },
398 ResetCursorBlink,
400 StartCursorBlinkTimer,
402 StopCursorBlinkTimer,
404
405 ScrollActiveCursorIntoView,
409
410 CreateTextInput {
420 text: AzString,
422 },
423}
424
425pub type CallbackType = extern "C" fn(RefAny, CallbackInfo) -> Update;
427
428#[repr(C)]
432pub struct Callback {
433 pub cb: CallbackType,
434 pub ctx: OptionRefAny,
437}
438
439impl_callback!(Callback, CallbackType);
440
441impl Callback {
442 pub fn create<C: Into<Callback>>(cb: C) -> Self {
444 cb.into()
445 }
446
447 pub fn from_core(core: CoreCallback) -> Self {
454 Self {
455 cb: unsafe { core::mem::transmute(core.cb) },
456 ctx: OptionRefAny::None,
457 }
458 }
459
460 pub fn to_core(self) -> CoreCallback {
464 CoreCallback {
465 cb: self.cb as usize,
466 ctx: self.ctx,
467 }
468 }
469}
470
471impl From<Callback> for CoreCallback {
473 fn from(callback: Callback) -> Self {
474 callback.to_core()
475 }
476}
477
478#[inline]
483pub fn callback_type_to_core(cb: CallbackType) -> CoreCallback {
484 CoreCallback {
485 cb: cb as usize,
486 ctx: OptionRefAny::None,
487 }
488}
489
490impl Callback {
491 pub fn invoke(&self, data: RefAny, info: CallbackInfo) -> Update {
495 (self.cb)(data, info)
496 }
497}
498
499pub unsafe fn core_callback_to_fn(core: CoreCallback) -> CallbackType {
507 core::mem::transmute(core.cb)
508}
509
510#[derive(Debug, Eq, Clone, PartialEq, PartialOrd, Ord, Hash)]
515#[repr(C, u8)]
516pub enum OptionCallback {
517 None,
519 Some(Callback),
521}
522
523impl OptionCallback {
524 pub fn into_option(self) -> Option<Callback> {
526 match self {
527 OptionCallback::None => None,
528 OptionCallback::Some(c) => Some(c),
529 }
530 }
531
532 pub fn is_some(&self) -> bool {
534 matches!(self, OptionCallback::Some(_))
535 }
536
537 pub fn is_none(&self) -> bool {
539 matches!(self, OptionCallback::None)
540 }
541}
542
543impl From<Option<Callback>> for OptionCallback {
544 fn from(o: Option<Callback>) -> Self {
545 match o {
546 None => OptionCallback::None,
547 Some(c) => OptionCallback::Some(c),
548 }
549 }
550}
551
552impl From<OptionCallback> for Option<Callback> {
553 fn from(o: OptionCallback) -> Self {
554 o.into_option()
555 }
556}
557
558pub struct CallbackInfoRefData<'a> {
579 pub layout_window: &'a LayoutWindow,
581 pub renderer_resources: &'a RendererResources,
583 pub previous_window_state: &'a Option<FullWindowState>,
585 pub current_window_state: &'a FullWindowState,
587 pub gl_context: &'a OptionGlContextPtr,
589 pub current_scroll_manager: &'a BTreeMap<DomId, BTreeMap<NodeHierarchyItemId, ScrollPosition>>,
591 pub current_window_handle: &'a RawWindowHandle,
593 pub system_callbacks: &'a ExternalSystemCallbacks,
595 pub system_style: Arc<SystemStyle>,
598 #[cfg(feature = "icu")]
601 pub icu_localizer: IcuLocalizerHandle,
602 pub ctx: OptionRefAny,
605}
606
607#[derive(Debug, Clone, Copy)]
617#[repr(C)]
618pub struct CallbackInfo {
619 ref_data: *const CallbackInfoRefData<'static>,
623 hit_dom_node: DomNodeId,
626 cursor_relative_to_item: OptionLogicalPosition,
629 cursor_in_viewport: OptionLogicalPosition,
631 #[cfg(feature = "std")]
635 changes: *const Arc<Mutex<Vec<CallbackChange>>>,
636 #[cfg(not(feature = "std"))]
637 changes: *mut Vec<CallbackChange>,
638}
639
640impl CallbackInfo {
641 #[cfg(feature = "std")]
642 pub fn new<'a>(
643 ref_data: &'a CallbackInfoRefData<'a>,
644 changes: &'a Arc<Mutex<Vec<CallbackChange>>>,
645 hit_dom_node: DomNodeId,
646 cursor_relative_to_item: OptionLogicalPosition,
647 cursor_in_viewport: OptionLogicalPosition,
648 ) -> Self {
649 Self {
650 ref_data: unsafe { core::mem::transmute(ref_data) },
654
655 hit_dom_node,
657 cursor_relative_to_item,
658 cursor_in_viewport,
659
660 changes: changes as *const Arc<Mutex<Vec<CallbackChange>>>,
662 }
663 }
664
665 #[cfg(not(feature = "std"))]
666 pub fn new<'a>(
667 ref_data: &'a CallbackInfoRefData<'a>,
668 changes: &'a mut Vec<CallbackChange>,
669 hit_dom_node: DomNodeId,
670 cursor_relative_to_item: OptionLogicalPosition,
671 cursor_in_viewport: OptionLogicalPosition,
672 ) -> Self {
673 Self {
674 ref_data: unsafe { core::mem::transmute(ref_data) },
675 hit_dom_node,
676 cursor_relative_to_item,
677 cursor_in_viewport,
678 changes: changes as *mut Vec<CallbackChange>,
679 }
680 }
681
682 pub fn get_ctx(&self) -> OptionRefAny {
687 unsafe { (*self.ref_data).ctx.clone() }
688 }
689
690 pub fn get_gl_context(&self) -> OptionGlContextPtr {
692 unsafe { (*self.ref_data).gl_context.clone() }
693 }
694
695 #[cfg(feature = "std")]
700 pub fn push_change(&mut self, change: CallbackChange) {
701 unsafe {
703 if let Ok(mut changes) = (*self.changes).lock() {
704 changes.push(change);
705 }
706 }
707 }
708
709 #[cfg(not(feature = "std"))]
710 pub fn push_change(&mut self, change: CallbackChange) {
711 unsafe { (*self.changes).push(change) }
712 }
713
714 #[cfg(feature = "std")]
716 pub fn get_changes_ptr(&self) -> *const () {
717 self.changes as *const ()
718 }
719
720 #[cfg(feature = "std")]
722 pub fn take_changes(&self) -> Vec<CallbackChange> {
723 unsafe {
725 if let Ok(mut changes) = (*self.changes).lock() {
726 core::mem::take(&mut *changes)
727 } else {
728 Vec::new()
729 }
730 }
731 }
732
733 #[cfg(not(feature = "std"))]
734 pub fn take_changes(&self) -> Vec<CallbackChange> {
735 unsafe { core::mem::take(&mut *self.changes) }
736 }
737
738 pub fn add_timer(&mut self, timer_id: TimerId, timer: Timer) {
742 self.push_change(CallbackChange::AddTimer { timer_id, timer });
743 }
744
745 pub fn remove_timer(&mut self, timer_id: TimerId) {
747 self.push_change(CallbackChange::RemoveTimer { timer_id });
748 }
749
750 pub fn add_thread(&mut self, thread_id: ThreadId, thread: Thread) {
752 self.push_change(CallbackChange::AddThread { thread_id, thread });
753 }
754
755 pub fn remove_thread(&mut self, thread_id: ThreadId) {
757 self.push_change(CallbackChange::RemoveThread { thread_id });
758 }
759
760 pub fn stop_propagation(&mut self) {
762 self.push_change(CallbackChange::StopPropagation);
763 }
764
765 pub fn set_focus(&mut self, target: FocusTarget) {
767 self.push_change(CallbackChange::SetFocusTarget { target });
768 }
769
770 pub fn create_window(&mut self, options: WindowCreateOptions) {
772 self.push_change(CallbackChange::CreateNewWindow { options });
773 }
774
775 pub fn close_window(&mut self) {
777 self.push_change(CallbackChange::CloseWindow);
778 }
779
780 pub fn modify_window_state(&mut self, state: FullWindowState) {
782 self.push_change(CallbackChange::ModifyWindowState { state });
783 }
784
785 pub fn queue_window_state_sequence(&mut self, states: Vec<FullWindowState>) {
789 self.push_change(CallbackChange::QueueWindowStateSequence { states });
790 }
791
792 pub fn change_node_text(&mut self, node_id: DomNodeId, text: AzString) {
800 self.push_change(CallbackChange::ChangeNodeText { node_id, text });
801 }
802
803 pub fn change_node_image(
805 &mut self,
806 dom_id: DomId,
807 node_id: NodeId,
808 image: ImageRef,
809 update_type: UpdateImageType,
810 ) {
811 self.push_change(CallbackChange::ChangeNodeImage {
812 dom_id,
813 node_id,
814 image,
815 update_type,
816 });
817 }
818
819 pub fn update_image_callback(&mut self, dom_id: DomId, node_id: NodeId) {
827 self.push_change(CallbackChange::UpdateImageCallback { dom_id, node_id });
828 }
829
830 pub fn trigger_iframe_rerender(&mut self, dom_id: DomId, node_id: NodeId) {
841 self.push_change(CallbackChange::UpdateIFrame { dom_id, node_id });
842 }
843
844 pub fn get_node_id_by_id_attribute(&self, dom_id: DomId, id: &str) -> Option<NodeId> {
850 let layout_window = self.get_layout_window();
851 let layout_result = layout_window.layout_results.get(&dom_id)?;
852 let styled_dom = &layout_result.styled_dom;
853
854 for (node_idx, node_data) in styled_dom.node_data.as_ref().iter().enumerate() {
856 for id_or_class in node_data.ids_and_classes.as_ref() {
857 if let IdOrClass::Id(node_id_str) = id_or_class {
858 if node_id_str.as_str() == id {
859 return Some(NodeId::new(node_idx));
860 }
861 }
862 }
863 }
864
865 None
866 }
867
868 pub fn get_parent_node(&self, dom_id: DomId, node_id: NodeId) -> Option<NodeId> {
872 let layout_window = self.get_layout_window();
873 let layout_result = layout_window.layout_results.get(&dom_id)?;
874 let node_hierarchy = &layout_result.styled_dom.node_hierarchy;
875 let node = node_hierarchy.as_ref().get(node_id.index())?;
876 node.parent_id()
877 }
878
879 pub fn get_next_sibling_node(&self, dom_id: DomId, node_id: NodeId) -> Option<NodeId> {
883 let layout_window = self.get_layout_window();
884 let layout_result = layout_window.layout_results.get(&dom_id)?;
885 let node_hierarchy = &layout_result.styled_dom.node_hierarchy;
886 let node = node_hierarchy.as_ref().get(node_id.index())?;
887 node.next_sibling_id()
888 }
889
890 pub fn get_previous_sibling_node(&self, dom_id: DomId, node_id: NodeId) -> Option<NodeId> {
894 let layout_window = self.get_layout_window();
895 let layout_result = layout_window.layout_results.get(&dom_id)?;
896 let node_hierarchy = &layout_result.styled_dom.node_hierarchy;
897 let node = node_hierarchy.as_ref().get(node_id.index())?;
898 node.previous_sibling_id()
899 }
900
901 pub fn get_first_child_node(&self, dom_id: DomId, node_id: NodeId) -> Option<NodeId> {
905 let layout_window = self.get_layout_window();
906 let layout_result = layout_window.layout_results.get(&dom_id)?;
907 let node_hierarchy = &layout_result.styled_dom.node_hierarchy;
908 let node = node_hierarchy.as_ref().get(node_id.index())?;
909 node.first_child_id(node_id)
910 }
911
912 pub fn get_last_child_node(&self, dom_id: DomId, node_id: NodeId) -> Option<NodeId> {
916 let layout_window = self.get_layout_window();
917 let layout_result = layout_window.layout_results.get(&dom_id)?;
918 let node_hierarchy = &layout_result.styled_dom.node_hierarchy;
919 let node = node_hierarchy.as_ref().get(node_id.index())?;
920 node.last_child_id()
921 }
922
923 pub fn get_all_children_nodes(&self, dom_id: DomId, node_id: NodeId) -> NodeIdVec {
928 let layout_window = self.get_layout_window();
929 let layout_result = match layout_window.layout_results.get(&dom_id) {
930 Some(lr) => lr,
931 None => return NodeIdVec::from_const_slice(&[]),
932 };
933 let node_hierarchy = layout_result.styled_dom.node_hierarchy.as_container();
934 let hier_item = match node_hierarchy.get(node_id) {
935 Some(h) => h,
936 None => return NodeIdVec::from_const_slice(&[]),
937 };
938
939 let first_child = match hier_item.first_child_id(node_id) {
941 Some(fc) => fc,
942 None => return NodeIdVec::from_const_slice(&[]),
943 };
944
945 let mut children: Vec<NodeHierarchyItemId> = Vec::new();
947 children.push(NodeHierarchyItemId::from_crate_internal(Some(first_child)));
948
949 let mut current = first_child;
950 while let Some(next_sibling) = node_hierarchy
951 .get(current)
952 .and_then(|h| h.next_sibling_id())
953 {
954 children.push(NodeHierarchyItemId::from_crate_internal(Some(next_sibling)));
955 current = next_sibling;
956 }
957
958 NodeIdVec::from(children)
959 }
960
961 pub fn get_children_count(&self, dom_id: DomId, node_id: NodeId) -> usize {
965 let layout_window = self.get_layout_window();
966 let layout_result = match layout_window.layout_results.get(&dom_id) {
967 Some(lr) => lr,
968 None => return 0,
969 };
970 let node_hierarchy = layout_result.styled_dom.node_hierarchy.as_container();
971 let hier_item = match node_hierarchy.get(node_id) {
972 Some(h) => h,
973 None => return 0,
974 };
975
976 let first_child = match hier_item.first_child_id(node_id) {
978 Some(fc) => fc,
979 None => return 0,
980 };
981
982 let mut count = 1;
984 let mut current = first_child;
985 while let Some(next_sibling) = node_hierarchy
986 .get(current)
987 .and_then(|h| h.next_sibling_id())
988 {
989 count += 1;
990 current = next_sibling;
991 }
992
993 count
994 }
995
996 pub fn change_node_image_mask(&mut self, dom_id: DomId, node_id: NodeId, mask: ImageMask) {
998 self.push_change(CallbackChange::ChangeNodeImageMask {
999 dom_id,
1000 node_id,
1001 mask,
1002 });
1003 }
1004
1005 pub fn change_node_css_properties(
1007 &mut self,
1008 dom_id: DomId,
1009 node_id: NodeId,
1010 properties: CssPropertyVec,
1011 ) {
1012 self.push_change(CallbackChange::ChangeNodeCssProperties {
1013 dom_id,
1014 node_id,
1015 properties,
1016 });
1017 }
1018
1019 pub fn set_css_property(&mut self, node_id: DomNodeId, property: CssProperty) {
1028 let dom_id = node_id.dom;
1029 let internal_node_id = node_id
1030 .node
1031 .into_crate_internal()
1032 .expect("DomNodeId node should not be None");
1033 self.change_node_css_properties(dom_id, internal_node_id, vec![property].into());
1034 }
1035
1036 pub fn scroll_to(
1038 &mut self,
1039 dom_id: DomId,
1040 node_id: NodeHierarchyItemId,
1041 position: LogicalPosition,
1042 ) {
1043 self.push_change(CallbackChange::ScrollTo {
1044 dom_id,
1045 node_id,
1046 position,
1047 });
1048 }
1049
1050 pub fn scroll_node_into_view(
1066 &mut self,
1067 node_id: DomNodeId,
1068 options: crate::managers::scroll_into_view::ScrollIntoViewOptions,
1069 ) {
1070 self.push_change(CallbackChange::ScrollIntoView {
1071 node_id,
1072 options,
1073 });
1074 }
1075
1076 pub fn add_image_to_cache(&mut self, id: AzString, image: ImageRef) {
1078 self.push_change(CallbackChange::AddImageToCache { id, image });
1079 }
1080
1081 pub fn remove_image_from_cache(&mut self, id: AzString) {
1083 self.push_change(CallbackChange::RemoveImageFromCache { id });
1084 }
1085
1086 pub fn reload_system_fonts(&mut self) {
1090 self.push_change(CallbackChange::ReloadSystemFonts);
1091 }
1092
1093 pub fn get_text_changeset(&self) -> Option<&PendingTextEdit> {
1103 self.get_layout_window()
1104 .text_input_manager
1105 .get_pending_changeset()
1106 }
1107
1108 pub fn set_text_changeset(&mut self, changeset: PendingTextEdit) {
1116 self.push_change(CallbackChange::SetTextChangeset { changeset });
1117 }
1118
1119 pub fn create_text_input(&mut self, text: AzString) {
1135 println!("[CallbackInfo::create_text_input] Creating text input: '{}'", text.as_str());
1136 self.push_change(CallbackChange::CreateTextInput { text });
1137 }
1138
1139 pub fn prevent_default(&mut self) {
1144 self.push_change(CallbackChange::PreventDefault);
1145 }
1146
1147 pub fn set_cursor_visibility(&mut self, visible: bool) {
1154 self.push_change(CallbackChange::SetCursorVisibility { visible });
1155 }
1156
1157 pub fn reset_cursor_blink(&mut self) {
1163 self.push_change(CallbackChange::ResetCursorBlink);
1164 }
1165
1166 pub fn start_cursor_blink_timer(&mut self) {
1171 self.push_change(CallbackChange::StartCursorBlinkTimer);
1172 }
1173
1174 pub fn stop_cursor_blink_timer(&mut self) {
1178 self.push_change(CallbackChange::StopCursorBlinkTimer);
1179 }
1180
1181 pub fn scroll_active_cursor_into_view(&mut self) {
1186 self.push_change(CallbackChange::ScrollActiveCursorIntoView);
1187 }
1188
1189 pub fn open_menu(&mut self, menu: Menu) {
1198 self.push_change(CallbackChange::OpenMenu {
1199 menu,
1200 position: None,
1201 });
1202 }
1203
1204 pub fn open_menu_at(&mut self, menu: Menu, position: LogicalPosition) {
1210 self.push_change(CallbackChange::OpenMenu {
1211 menu,
1212 position: Some(position),
1213 });
1214 }
1215
1216 pub fn show_tooltip(&mut self, text: AzString) {
1232 let position = self
1233 .get_cursor_relative_to_viewport()
1234 .into_option()
1235 .unwrap_or_else(LogicalPosition::zero);
1236 self.push_change(CallbackChange::ShowTooltip { text, position });
1237 }
1238
1239 pub fn show_tooltip_at(&mut self, text: AzString, position: LogicalPosition) {
1245 self.push_change(CallbackChange::ShowTooltip { text, position });
1246 }
1247
1248 pub fn hide_tooltip(&mut self) {
1250 self.push_change(CallbackChange::HideTooltip);
1251 }
1252
1253 pub fn insert_text(&mut self, dom_id: DomId, node_id: NodeId, text: AzString) {
1265 self.push_change(CallbackChange::InsertText {
1266 dom_id,
1267 node_id,
1268 text,
1269 });
1270 }
1271
1272 pub fn move_cursor(&mut self, dom_id: DomId, node_id: NodeId, cursor: TextCursor) {
1279 self.push_change(CallbackChange::MoveCursor {
1280 dom_id,
1281 node_id,
1282 cursor,
1283 });
1284 }
1285
1286 pub fn set_selection(&mut self, dom_id: DomId, node_id: NodeId, selection: Selection) {
1293 self.push_change(CallbackChange::SetSelection {
1294 dom_id,
1295 node_id,
1296 selection,
1297 });
1298 }
1299
1300 pub fn open_menu_for_node(&mut self, menu: Menu, node_id: DomNodeId) -> bool {
1313 if let Some(rect) = self.get_node_rect(node_id) {
1315 let position = LogicalPosition::new(rect.origin.x, rect.origin.y + rect.size.height);
1317 self.push_change(CallbackChange::OpenMenu {
1318 menu,
1319 position: Some(position),
1320 });
1321 true
1322 } else {
1323 false
1324 }
1325 }
1326
1327 pub fn open_menu_for_hit_node(&mut self, menu: Menu) -> bool {
1339 let hit_node = self.get_hit_node();
1340 self.open_menu_for_node(menu, hit_node)
1341 }
1342
1343 pub fn get_layout_window(&self) -> &LayoutWindow {
1350 unsafe { (*self.ref_data).layout_window }
1351 }
1352
1353 fn get_inline_layout_for_node(&self, node_id: &DomNodeId) -> Option<&Arc<UnifiedLayout>> {
1364 let layout_window = self.get_layout_window();
1365
1366 let layout_result = layout_window.layout_results.get(&node_id.dom)?;
1368
1369 let dom_node_id = node_id.node.into_crate_internal()?;
1371
1372 let layout_indices = layout_result.layout_tree.dom_to_layout.get(&dom_node_id)?;
1374
1375 let layout_index = *layout_indices.first()?;
1378
1379 let layout_node = layout_result.layout_tree.nodes.get(layout_index)?;
1381 layout_node
1382 .inline_layout_result
1383 .as_ref()
1384 .map(|c| c.get_layout())
1385 }
1386
1387 pub fn get_node_size(&self, node_id: DomNodeId) -> Option<LogicalSize> {
1390 self.get_layout_window().get_node_size(node_id)
1391 }
1392
1393 pub fn get_node_position(&self, node_id: DomNodeId) -> Option<LogicalPosition> {
1394 self.get_layout_window().get_node_position(node_id)
1395 }
1396
1397 pub fn get_node_hit_test_bounds(&self, node_id: DomNodeId) -> Option<LogicalRect> {
1402 self.get_layout_window().get_node_hit_test_bounds(node_id)
1403 }
1404
1405 pub fn get_node_rect(&self, node_id: DomNodeId) -> Option<LogicalRect> {
1410 let position = self.get_node_position(node_id)?;
1411 let size = self.get_node_size(node_id)?;
1412 Some(LogicalRect::new(position, size))
1413 }
1414
1415 pub fn get_hit_node_rect(&self) -> Option<LogicalRect> {
1420 let hit_node = self.get_hit_node();
1421 self.get_node_rect(hit_node)
1422 }
1423
1424 pub fn get_timer(&self, timer_id: &TimerId) -> Option<&Timer> {
1428 self.get_layout_window().get_timer(timer_id)
1429 }
1430
1431 pub fn get_timer_ids(&self) -> TimerIdVec {
1433 self.get_layout_window().get_timer_ids()
1434 }
1435
1436 pub fn get_thread(&self, thread_id: &ThreadId) -> Option<&Thread> {
1440 self.get_layout_window().get_thread(thread_id)
1441 }
1442
1443 pub fn get_thread_ids(&self) -> ThreadIdVec {
1445 self.get_layout_window().get_thread_ids()
1446 }
1447
1448 pub fn get_gpu_cache(&self, dom_id: &DomId) -> Option<&GpuValueCache> {
1452 self.get_layout_window().get_gpu_cache(dom_id)
1453 }
1454
1455 pub fn get_layout_result(&self, dom_id: &DomId) -> Option<&DomLayoutResult> {
1459 self.get_layout_window().get_layout_result(dom_id)
1460 }
1461
1462 pub fn get_dom_ids(&self) -> DomIdVec {
1464 self.get_layout_window().get_dom_ids()
1465 }
1466
1467 pub fn get_hit_node(&self) -> DomNodeId {
1470 self.hit_dom_node
1471 }
1472
1473 fn is_node_anonymous(&self, dom_id: &DomId, node_id: NodeId) -> bool {
1475 let layout_window = self.get_layout_window();
1476 let layout_result = match layout_window.get_layout_result(dom_id) {
1477 Some(lr) => lr,
1478 None => return false,
1479 };
1480 let node_data_cont = layout_result.styled_dom.node_data.as_container();
1481 let node_data = match node_data_cont.get(node_id) {
1482 Some(nd) => nd,
1483 None => return false,
1484 };
1485 node_data.is_anonymous()
1486 }
1487
1488 pub fn get_parent(&self, node_id: DomNodeId) -> Option<DomNodeId> {
1489 let layout_window = self.get_layout_window();
1490 let layout_result = layout_window.get_layout_result(&node_id.dom)?;
1491 let node_id_internal = node_id.node.into_crate_internal()?;
1492 let node_hierarchy = layout_result.styled_dom.node_hierarchy.as_container();
1493 let hier_item = node_hierarchy.get(node_id_internal)?;
1494
1495 let mut current_parent_id = hier_item.parent_id()?;
1497 loop {
1498 if !self.is_node_anonymous(&node_id.dom, current_parent_id) {
1499 return Some(DomNodeId {
1500 dom: node_id.dom,
1501 node: NodeHierarchyItemId::from_crate_internal(Some(current_parent_id)),
1502 });
1503 }
1504
1505 let parent_hier_item = node_hierarchy.get(current_parent_id)?;
1507 current_parent_id = parent_hier_item.parent_id()?;
1508 }
1509 }
1510
1511 pub fn get_previous_sibling(&self, node_id: DomNodeId) -> Option<DomNodeId> {
1512 let layout_window = self.get_layout_window();
1513 let layout_result = layout_window.get_layout_result(&node_id.dom)?;
1514 let node_id_internal = node_id.node.into_crate_internal()?;
1515 let node_hierarchy = layout_result.styled_dom.node_hierarchy.as_container();
1516 let hier_item = node_hierarchy.get(node_id_internal)?;
1517
1518 let mut current_sibling_id = hier_item.previous_sibling_id()?;
1520 loop {
1521 if !self.is_node_anonymous(&node_id.dom, current_sibling_id) {
1522 return Some(DomNodeId {
1523 dom: node_id.dom,
1524 node: NodeHierarchyItemId::from_crate_internal(Some(current_sibling_id)),
1525 });
1526 }
1527
1528 let sibling_hier_item = node_hierarchy.get(current_sibling_id)?;
1530 current_sibling_id = sibling_hier_item.previous_sibling_id()?;
1531 }
1532 }
1533
1534 pub fn get_next_sibling(&self, node_id: DomNodeId) -> Option<DomNodeId> {
1535 let layout_window = self.get_layout_window();
1536 let layout_result = layout_window.get_layout_result(&node_id.dom)?;
1537 let node_id_internal = node_id.node.into_crate_internal()?;
1538 let node_hierarchy = layout_result.styled_dom.node_hierarchy.as_container();
1539 let hier_item = node_hierarchy.get(node_id_internal)?;
1540
1541 let mut current_sibling_id = hier_item.next_sibling_id()?;
1543 loop {
1544 if !self.is_node_anonymous(&node_id.dom, current_sibling_id) {
1545 return Some(DomNodeId {
1546 dom: node_id.dom,
1547 node: NodeHierarchyItemId::from_crate_internal(Some(current_sibling_id)),
1548 });
1549 }
1550
1551 let sibling_hier_item = node_hierarchy.get(current_sibling_id)?;
1553 current_sibling_id = sibling_hier_item.next_sibling_id()?;
1554 }
1555 }
1556
1557 pub fn get_first_child(&self, node_id: DomNodeId) -> Option<DomNodeId> {
1558 let layout_window = self.get_layout_window();
1559 let layout_result = layout_window.get_layout_result(&node_id.dom)?;
1560 let node_id_internal = node_id.node.into_crate_internal()?;
1561 let node_hierarchy = layout_result.styled_dom.node_hierarchy.as_container();
1562 let hier_item = node_hierarchy.get(node_id_internal)?;
1563
1564 let mut current_child_id = hier_item.first_child_id(node_id_internal)?;
1566 loop {
1567 if !self.is_node_anonymous(&node_id.dom, current_child_id) {
1568 return Some(DomNodeId {
1569 dom: node_id.dom,
1570 node: NodeHierarchyItemId::from_crate_internal(Some(current_child_id)),
1571 });
1572 }
1573
1574 let child_hier_item = node_hierarchy.get(current_child_id)?;
1576 current_child_id = child_hier_item.next_sibling_id()?;
1577 }
1578 }
1579
1580 pub fn get_last_child(&self, node_id: DomNodeId) -> Option<DomNodeId> {
1581 let layout_window = self.get_layout_window();
1582 let layout_result = layout_window.get_layout_result(&node_id.dom)?;
1583 let node_id_internal = node_id.node.into_crate_internal()?;
1584 let node_hierarchy = layout_result.styled_dom.node_hierarchy.as_container();
1585 let hier_item = node_hierarchy.get(node_id_internal)?;
1586
1587 let mut current_child_id = hier_item.last_child_id()?;
1589 loop {
1590 if !self.is_node_anonymous(&node_id.dom, current_child_id) {
1591 return Some(DomNodeId {
1592 dom: node_id.dom,
1593 node: NodeHierarchyItemId::from_crate_internal(Some(current_child_id)),
1594 });
1595 }
1596
1597 let child_hier_item = node_hierarchy.get(current_child_id)?;
1599 current_child_id = child_hier_item.previous_sibling_id()?;
1600 }
1601 }
1602
1603 pub fn get_dataset(&mut self, node_id: DomNodeId) -> Option<RefAny> {
1606 let layout_window = self.get_layout_window();
1607 let layout_result = layout_window.get_layout_result(&node_id.dom)?;
1608 let node_id_internal = node_id.node.into_crate_internal()?;
1609 let node_data_cont = layout_result.styled_dom.node_data.as_container();
1610 let node_data = node_data_cont.get(node_id_internal)?;
1611 node_data.get_dataset().clone().into_option()
1612 }
1613
1614 pub fn get_node_id_of_root_dataset(&mut self, search_key: RefAny) -> Option<DomNodeId> {
1615 let mut found: Option<(u64, DomNodeId)> = None;
1616 let search_type_id = search_key.get_type_id();
1617
1618 for dom_id in self.get_dom_ids().as_ref().iter().copied() {
1619 let layout_window = self.get_layout_window();
1620 let layout_result = match layout_window.get_layout_result(&dom_id) {
1621 Some(lr) => lr,
1622 None => continue,
1623 };
1624
1625 let node_data_cont = layout_result.styled_dom.node_data.as_container();
1626 for (node_idx, node_data) in node_data_cont.iter().enumerate() {
1627 if let Some(dataset) = node_data.get_dataset().clone().into_option() {
1628 if dataset.get_type_id() == search_type_id {
1629 let node_id = DomNodeId {
1630 dom: dom_id,
1631 node: NodeHierarchyItemId::from_crate_internal(Some(NodeId::new(
1632 node_idx,
1633 ))),
1634 };
1635 let instance_id = dataset.instance_id;
1636
1637 match found {
1638 None => found = Some((instance_id, node_id)),
1639 Some((prev_instance, _)) => {
1640 if instance_id < prev_instance {
1641 found = Some((instance_id, node_id));
1642 }
1643 }
1644 }
1645 }
1646 }
1647 }
1648 }
1649
1650 found.map(|s| s.1)
1651 }
1652
1653 pub fn get_string_contents(&self, node_id: DomNodeId) -> Option<AzString> {
1654 let layout_window = self.get_layout_window();
1655 let layout_result = layout_window.get_layout_result(&node_id.dom)?;
1656 let node_id_internal = node_id.node.into_crate_internal()?;
1657 let node_data_cont = layout_result.styled_dom.node_data.as_container();
1658 let node_data = node_data_cont.get(node_id_internal)?;
1659
1660 if let NodeType::Text(ref text) = node_data.get_node_type() {
1661 Some(text.clone())
1662 } else {
1663 None
1664 }
1665 }
1666
1667 pub fn get_node_tag_name(&self, node_id: DomNodeId) -> Option<AzString> {
1672 let layout_window = self.get_layout_window();
1673 let layout_result = layout_window.get_layout_result(&node_id.dom)?;
1674 let node_id_internal = node_id.node.into_crate_internal()?;
1675 let node_data_cont = layout_result.styled_dom.node_data.as_container();
1676 let node_data = node_data_cont.get(node_id_internal)?;
1677
1678 let tag = node_data.get_node_type().get_path();
1679 Some(tag.to_string().into())
1680 }
1681
1682 pub fn get_node_attribute(&self, node_id: DomNodeId, attr_name: &str) -> Option<AzString> {
1691 use azul_core::dom::AttributeType;
1692
1693 let layout_window = self.get_layout_window();
1694 let layout_result = layout_window.get_layout_result(&node_id.dom)?;
1695 let node_id_internal = node_id.node.into_crate_internal()?;
1696 let node_data_cont = layout_result.styled_dom.node_data.as_container();
1697 let node_data = node_data_cont.get(node_id_internal)?;
1698
1699 for attr in node_data.attributes.as_ref() {
1701 match (attr_name, attr) {
1702 ("id", AttributeType::Id(v)) => return Some(v.clone()),
1703 ("class", AttributeType::Class(v)) => return Some(v.clone()),
1704 ("aria-label", AttributeType::AriaLabel(v)) => return Some(v.clone()),
1705 ("aria-labelledby", AttributeType::AriaLabelledBy(v)) => return Some(v.clone()),
1706 ("aria-describedby", AttributeType::AriaDescribedBy(v)) => return Some(v.clone()),
1707 ("role", AttributeType::AriaRole(v)) => return Some(v.clone()),
1708 ("href", AttributeType::Href(v)) => return Some(v.clone()),
1709 ("rel", AttributeType::Rel(v)) => return Some(v.clone()),
1710 ("target", AttributeType::Target(v)) => return Some(v.clone()),
1711 ("src", AttributeType::Src(v)) => return Some(v.clone()),
1712 ("alt", AttributeType::Alt(v)) => return Some(v.clone()),
1713 ("title", AttributeType::Title(v)) => return Some(v.clone()),
1714 ("name", AttributeType::Name(v)) => return Some(v.clone()),
1715 ("value", AttributeType::Value(v)) => return Some(v.clone()),
1716 ("type", AttributeType::InputType(v)) => return Some(v.clone()),
1717 ("placeholder", AttributeType::Placeholder(v)) => return Some(v.clone()),
1718 ("max", AttributeType::Max(v)) => return Some(v.clone()),
1719 ("min", AttributeType::Min(v)) => return Some(v.clone()),
1720 ("step", AttributeType::Step(v)) => return Some(v.clone()),
1721 ("pattern", AttributeType::Pattern(v)) => return Some(v.clone()),
1722 ("autocomplete", AttributeType::Autocomplete(v)) => return Some(v.clone()),
1723 ("scope", AttributeType::Scope(v)) => return Some(v.clone()),
1724 ("lang", AttributeType::Lang(v)) => return Some(v.clone()),
1725 ("dir", AttributeType::Dir(v)) => return Some(v.clone()),
1726 ("required", AttributeType::Required) => return Some("true".into()),
1727 ("disabled", AttributeType::Disabled) => return Some("true".into()),
1728 ("readonly", AttributeType::Readonly) => return Some("true".into()),
1729 ("checked", AttributeType::Checked) => return Some("true".into()),
1730 ("selected", AttributeType::Selected) => return Some("true".into()),
1731 ("hidden", AttributeType::Hidden) => return Some("true".into()),
1732 ("focusable", AttributeType::Focusable) => return Some("true".into()),
1733 ("minlength", AttributeType::MinLength(v)) => return Some(v.to_string().into()),
1734 ("maxlength", AttributeType::MaxLength(v)) => return Some(v.to_string().into()),
1735 ("colspan", AttributeType::ColSpan(v)) => return Some(v.to_string().into()),
1736 ("rowspan", AttributeType::RowSpan(v)) => return Some(v.to_string().into()),
1737 ("tabindex", AttributeType::TabIndex(v)) => return Some(v.to_string().into()),
1738 ("contenteditable", AttributeType::ContentEditable(v)) => {
1739 return Some(v.to_string().into())
1740 }
1741 ("draggable", AttributeType::Draggable(v)) => return Some(v.to_string().into()),
1742 (name, AttributeType::Data(nv))
1744 if name.starts_with("data-") && nv.attr_name.as_str() == &name[5..] =>
1745 {
1746 return Some(nv.value.clone());
1747 }
1748 (name, AttributeType::AriaState(nv))
1750 if name == format!("aria-{}", nv.attr_name.as_str()) =>
1751 {
1752 return Some(nv.value.clone());
1753 }
1754 (name, AttributeType::AriaProperty(nv))
1755 if name == format!("aria-{}", nv.attr_name.as_str()) =>
1756 {
1757 return Some(nv.value.clone());
1758 }
1759 (name, AttributeType::Custom(nv)) if nv.attr_name.as_str() == name => {
1761 return Some(nv.value.clone());
1762 }
1763 _ => continue,
1764 }
1765 }
1766
1767 if attr_name == "id" {
1769 for id_or_class in node_data.ids_and_classes.as_ref() {
1770 if let IdOrClass::Id(id) = id_or_class {
1771 return Some(id.clone());
1772 }
1773 }
1774 }
1775
1776 if attr_name == "class" {
1777 let classes: Vec<&str> = node_data
1778 .ids_and_classes
1779 .as_ref()
1780 .iter()
1781 .filter_map(|ioc| {
1782 if let IdOrClass::Class(class) = ioc {
1783 Some(class.as_str())
1784 } else {
1785 None
1786 }
1787 })
1788 .collect();
1789 if !classes.is_empty() {
1790 return Some(classes.join(" ").into());
1791 }
1792 }
1793
1794 None
1795 }
1796
1797 pub fn get_node_classes(&self, node_id: DomNodeId) -> StringVec {
1799 let layout_window = match self.get_layout_window().get_layout_result(&node_id.dom) {
1800 Some(lr) => lr,
1801 None => return StringVec::from_const_slice(&[]),
1802 };
1803 let node_id_internal = match node_id.node.into_crate_internal() {
1804 Some(n) => n,
1805 None => return StringVec::from_const_slice(&[]),
1806 };
1807 let node_data_cont = layout_window.styled_dom.node_data.as_container();
1808 let node_data = match node_data_cont.get(node_id_internal) {
1809 Some(n) => n,
1810 None => return StringVec::from_const_slice(&[]),
1811 };
1812
1813 let classes: Vec<AzString> = node_data
1814 .ids_and_classes
1815 .as_ref()
1816 .iter()
1817 .filter_map(|ioc| {
1818 if let IdOrClass::Class(class) = ioc {
1819 Some(class.clone())
1820 } else {
1821 None
1822 }
1823 })
1824 .collect();
1825
1826 StringVec::from(classes)
1827 }
1828
1829 pub fn get_node_id(&self, node_id: DomNodeId) -> Option<AzString> {
1831 let layout_window = self.get_layout_window();
1832 let layout_result = layout_window.get_layout_result(&node_id.dom)?;
1833 let node_id_internal = node_id.node.into_crate_internal()?;
1834 let node_data_cont = layout_result.styled_dom.node_data.as_container();
1835 let node_data = node_data_cont.get(node_id_internal)?;
1836
1837 for id_or_class in node_data.ids_and_classes.as_ref() {
1838 if let IdOrClass::Id(id) = id_or_class {
1839 return Some(id.clone());
1840 }
1841 }
1842 None
1843 }
1844
1845 pub fn get_selection(&self, dom_id: &DomId) -> Option<&SelectionState> {
1849 self.get_layout_window()
1850 .selection_manager
1851 .get_selection(dom_id)
1852 }
1853
1854 pub fn has_selection(&self, dom_id: &DomId) -> bool {
1856 self.get_layout_window()
1857 .selection_manager
1858 .has_selection(dom_id)
1859 }
1860
1861 pub fn get_primary_cursor(&self, dom_id: &DomId) -> Option<TextCursor> {
1863 self.get_layout_window()
1864 .selection_manager
1865 .get_primary_cursor(dom_id)
1866 }
1867
1868 pub fn get_selection_ranges(&self, dom_id: &DomId) -> SelectionRangeVec {
1870 self.get_layout_window()
1871 .selection_manager
1872 .get_ranges(dom_id)
1873 .into()
1874 }
1875
1876 pub fn get_text_cache(&self) -> &TextLayoutCache {
1889 &self.get_layout_window().text_cache
1890 }
1891
1892 pub fn get_current_window_state(&self) -> &FullWindowState {
1896 unsafe { (*self.ref_data).current_window_state }
1898 }
1899
1900 pub fn get_current_window_flags(&self) -> WindowFlags {
1902 self.get_current_window_state().flags.clone()
1903 }
1904
1905 pub fn get_current_keyboard_state(&self) -> KeyboardState {
1907 self.get_current_window_state().keyboard_state.clone()
1908 }
1909
1910 pub fn get_current_mouse_state(&self) -> MouseState {
1912 self.get_current_window_state().mouse_state.clone()
1913 }
1914
1915 pub fn get_previous_window_state(&self) -> &Option<FullWindowState> {
1917 unsafe { (*self.ref_data).previous_window_state }
1918 }
1919
1920 pub fn get_previous_window_flags(&self) -> Option<WindowFlags> {
1922 Some(self.get_previous_window_state().as_ref()?.flags.clone())
1923 }
1924
1925 pub fn get_previous_keyboard_state(&self) -> Option<KeyboardState> {
1927 Some(
1928 self.get_previous_window_state()
1929 .as_ref()?
1930 .keyboard_state
1931 .clone(),
1932 )
1933 }
1934
1935 pub fn get_previous_mouse_state(&self) -> Option<MouseState> {
1937 Some(
1938 self.get_previous_window_state()
1939 .as_ref()?
1940 .mouse_state
1941 .clone(),
1942 )
1943 }
1944
1945 pub fn get_cursor_relative_to_node(&self) -> OptionLogicalPosition {
1948 self.cursor_relative_to_item
1949 }
1950
1951 pub fn get_cursor_relative_to_viewport(&self) -> OptionLogicalPosition {
1952 self.cursor_in_viewport
1953 }
1954
1955 pub fn get_current_window_handle(&self) -> RawWindowHandle {
1956 unsafe { (*self.ref_data).current_window_handle.clone() }
1957 }
1958
1959 pub fn get_system_style(&self) -> Arc<SystemStyle> {
1962 unsafe { (*self.ref_data).system_style.clone() }
1963 }
1964
1965 #[cfg(feature = "icu")]
1980 pub fn get_icu_localizer(&self) -> &IcuLocalizerHandle {
1981 unsafe { &(*self.ref_data).icu_localizer }
1982 }
1983
1984 #[cfg(feature = "icu")]
1997 pub fn format_integer(&self, locale: &str, value: i64) -> AzString {
1998 self.get_icu_localizer().format_integer(locale, value)
1999 }
2000
2001 #[cfg(feature = "icu")]
2014 pub fn format_decimal(&self, locale: &str, integer_part: i64, decimal_places: i16) -> AzString {
2015 self.get_icu_localizer().format_decimal(locale, integer_part, decimal_places)
2016 }
2017
2018 #[cfg(feature = "icu")]
2032 pub fn get_plural_category(&self, locale: &str, value: i64) -> PluralCategory {
2033 self.get_icu_localizer().get_plural_category(locale, value)
2034 }
2035
2036 #[cfg(feature = "icu")]
2049 pub fn pluralize(
2050 &self,
2051 locale: &str,
2052 value: i64,
2053 zero: &str,
2054 one: &str,
2055 two: &str,
2056 few: &str,
2057 many: &str,
2058 other: &str,
2059 ) -> AzString {
2060 self.get_icu_localizer().pluralize(locale, value, zero, one, two, few, many, other)
2061 }
2062
2063 #[cfg(feature = "icu")]
2076 pub fn format_list(&self, locale: &str, items: &[AzString], list_type: ListType) -> AzString {
2077 self.get_icu_localizer().format_list(locale, items, list_type)
2078 }
2079
2080 #[cfg(feature = "icu")]
2094 pub fn format_date(&self, locale: &str, date: IcuDate, length: FormatLength) -> IcuResult {
2095 self.get_icu_localizer().format_date(locale, date, length)
2096 }
2097
2098 #[cfg(feature = "icu")]
2112 pub fn format_time(&self, locale: &str, time: IcuTime, include_seconds: bool) -> IcuResult {
2113 self.get_icu_localizer().format_time(locale, time, include_seconds)
2114 }
2115
2116 #[cfg(feature = "icu")]
2123 pub fn format_datetime(&self, locale: &str, datetime: IcuDateTime, length: FormatLength) -> IcuResult {
2124 self.get_icu_localizer().format_datetime(locale, datetime, length)
2125 }
2126
2127 #[cfg(feature = "icu")]
2143 pub fn compare_strings(&self, locale: &str, a: &str, b: &str) -> i32 {
2144 self.get_icu_localizer().compare_strings(locale, a, b)
2145 }
2146
2147 #[cfg(feature = "icu")]
2162 pub fn sort_strings(&self, locale: &str, strings: &[AzString]) -> IcuStringVec {
2163 self.get_icu_localizer().sort_strings(locale, strings)
2164 }
2165
2166 #[cfg(feature = "icu")]
2176 pub fn strings_equal(&self, locale: &str, a: &str, b: &str) -> bool {
2177 self.get_icu_localizer().strings_equal(locale, a, b)
2178 }
2179
2180 pub fn get_cursor_position(&self) -> Option<LogicalPosition> {
2182 self.cursor_in_viewport.into_option()
2183 }
2184
2185 pub fn get_hit_node_layout_rect(&self) -> Option<LogicalRect> {
2187 self.get_layout_window()
2188 .get_node_layout_rect(self.hit_dom_node)
2189 }
2190
2191 pub fn get_computed_css_property(
2211 &self,
2212 node_id: DomNodeId,
2213 property_type: CssPropertyType,
2214 ) -> Option<CssProperty> {
2215 let layout_window = self.get_layout_window();
2216
2217 let layout_result = layout_window.layout_results.get(&node_id.dom)?;
2219
2220 let styled_dom = &layout_result.styled_dom;
2222
2223 let internal_node_id = node_id.node.into_crate_internal()?;
2225
2226 let node_data_container = styled_dom.node_data.as_container();
2228 let node_data = node_data_container.get(internal_node_id)?;
2229
2230 let styled_nodes_container = styled_dom.styled_nodes.as_container();
2232 let styled_node = styled_nodes_container.get(internal_node_id)?;
2233 let node_state = &styled_node.styled_node_state;
2234
2235 let css_property_cache = &styled_dom.css_property_cache.ptr;
2237 css_property_cache
2238 .get_property(node_data, &internal_node_id, node_state, &property_type)
2239 .cloned()
2240 }
2241
2242 pub fn get_computed_width(&self, node_id: DomNodeId) -> Option<CssProperty> {
2246 self.get_computed_css_property(node_id, CssPropertyType::Width)
2247 }
2248
2249 pub fn get_computed_height(&self, node_id: DomNodeId) -> Option<CssProperty> {
2253 self.get_computed_css_property(node_id, CssPropertyType::Height)
2254 }
2255
2256 pub fn get_system_time_fn(&self) -> GetSystemTimeCallback {
2259 unsafe { (*self.ref_data).system_callbacks.get_system_time_fn }
2260 }
2261
2262 pub fn get_current_time(&self) -> task::Instant {
2263 let cb = self.get_system_time_fn();
2264 (cb.cb)()
2265 }
2266
2267 pub fn get_renderer_resources(&self) -> &RendererResources {
2272 unsafe { (*self.ref_data).renderer_resources }
2273 }
2274
2275 #[cfg(feature = "cpurender")]
2304 pub fn take_screenshot(&self, dom_id: DomId) -> Result<alloc::vec::Vec<u8>, AzString> {
2305 use crate::cpurender::{render, RenderOptions};
2306
2307 let layout_window = self.get_layout_window();
2308 let renderer_resources = self.get_renderer_resources();
2309
2310 let layout_result = layout_window
2312 .layout_results
2313 .get(&dom_id)
2314 .ok_or_else(|| AzString::from("DOM not found in layout results"))?;
2315
2316 let viewport = &layout_result.viewport;
2318 let width = viewport.size.width;
2319 let height = viewport.size.height;
2320
2321 if width <= 0.0 || height <= 0.0 {
2322 return Err(AzString::from("Invalid viewport dimensions"));
2323 }
2324
2325 let display_list = &layout_result.display_list;
2327
2328 let dpi_factor = self
2330 .get_current_window_state()
2331 .size
2332 .get_hidpi_factor()
2333 .inner
2334 .get();
2335
2336 let opts = RenderOptions {
2338 width,
2339 height,
2340 dpi_factor,
2341 };
2342
2343 let pixmap =
2344 render(display_list, renderer_resources, opts).map_err(|e| AzString::from(e))?;
2345
2346 let png_data = pixmap
2348 .encode_png()
2349 .map_err(|e| AzString::from(alloc::format!("PNG encoding failed: {}", e)))?;
2350
2351 Ok(png_data)
2352 }
2353
2354 #[cfg(all(feature = "std", feature = "cpurender"))]
2366 pub fn take_screenshot_to_file(&self, dom_id: DomId, path: &str) -> Result<(), AzString> {
2367 let png_data = self.take_screenshot(dom_id)?;
2368 std::fs::write(path, png_data)
2369 .map_err(|e| AzString::from(alloc::format!("Failed to write file: {}", e)))?;
2370 Ok(())
2371 }
2372
2373 #[cfg(feature = "std")]
2382 pub fn take_native_screenshot(&self, _path: &str) -> Result<(), AzString> {
2383 Err(AzString::from(
2384 "Native screenshot requires the NativeScreenshotExt trait from azul-dll crate. \
2385 Import it with: use azul::desktop::NativeScreenshotExt;",
2386 ))
2387 }
2388
2389 #[cfg(feature = "std")]
2398 pub fn take_native_screenshot_bytes(&self) -> Result<alloc::vec::Vec<u8>, AzString> {
2399 let temp_path = std::env::temp_dir().join("azul_screenshot_temp.png");
2401 let temp_path_str = temp_path.to_string_lossy().to_string();
2402
2403 self.take_native_screenshot(&temp_path_str)?;
2404
2405 let bytes = std::fs::read(&temp_path)
2406 .map_err(|e| AzString::from(alloc::format!("Failed to read screenshot: {}", e)))?;
2407
2408 let _ = std::fs::remove_file(&temp_path);
2409
2410 Ok(bytes)
2411 }
2412
2413 #[cfg(feature = "std")]
2423 pub fn take_native_screenshot_base64(&self) -> Result<AzString, AzString> {
2424 let png_bytes = self.take_native_screenshot_bytes()?;
2425 let base64_str = base64_encode(&png_bytes);
2426 Ok(AzString::from(alloc::format!(
2427 "data:image/png;base64,{}",
2428 base64_str
2429 )))
2430 }
2431
2432 #[cfg(feature = "cpurender")]
2441 pub fn take_screenshot_base64(&self, dom_id: DomId) -> Result<AzString, AzString> {
2442 let png_bytes = self.take_screenshot(dom_id)?;
2443 let base64_str = base64_encode(&png_bytes);
2444 Ok(AzString::from(alloc::format!(
2445 "data:image/png;base64,{}",
2446 base64_str
2447 )))
2448 }
2449
2450 pub fn get_scroll_manager(&self) -> &ScrollManager {
2457 unsafe { &(*self.ref_data).layout_window.scroll_manager }
2458 }
2459
2460 pub fn get_gesture_drag_manager(&self) -> &GestureAndDragManager {
2468 unsafe { &(*self.ref_data).layout_window.gesture_drag_manager }
2469 }
2470
2471 pub fn get_focus_manager(&self) -> &FocusManager {
2476 &self.get_layout_window().focus_manager
2477 }
2478
2479 pub fn get_undo_redo_manager(&self) -> &UndoRedoManager {
2484 &self.get_layout_window().undo_redo_manager
2485 }
2486
2487 pub fn get_hover_manager(&self) -> &HoverManager {
2492 &self.get_layout_window().hover_manager
2493 }
2494
2495 pub fn get_text_input_manager(&self) -> &TextInputManager {
2499 &self.get_layout_window().text_input_manager
2500 }
2501
2502 pub fn get_selection_manager(&self) -> &SelectionManager {
2506 &self.get_layout_window().selection_manager
2507 }
2508
2509 pub fn is_node_focused(&self, node_id: DomNodeId) -> bool {
2511 self.get_focus_manager().has_focus(&node_id)
2512 }
2513
2514 pub fn is_dom_focused(&self, dom_id: DomId) -> bool {
2516 self.get_focused_node()
2517 .map(|n| n.dom == dom_id)
2518 .unwrap_or(false)
2519 }
2520
2521 pub fn get_pen_state(&self) -> Option<&PenState> {
2525 self.get_gesture_drag_manager().get_pen_state()
2526 }
2527
2528 pub fn get_pen_pressure(&self) -> Option<f32> {
2531 self.get_pen_state().map(|pen| pen.pressure)
2532 }
2533
2534 pub fn get_pen_tilt(&self) -> Option<PenTilt> {
2537 self.get_pen_state().map(|pen| pen.tilt)
2538 }
2539
2540 pub fn is_pen_in_contact(&self) -> bool {
2542 self.get_pen_state()
2543 .map(|pen| pen.in_contact)
2544 .unwrap_or(false)
2545 }
2546
2547 pub fn is_pen_eraser(&self) -> bool {
2549 self.get_pen_state()
2550 .map(|pen| pen.is_eraser)
2551 .unwrap_or(false)
2552 }
2553
2554 pub fn is_pen_barrel_button_pressed(&self) -> bool {
2556 self.get_pen_state()
2557 .map(|pen| pen.barrel_button_pressed)
2558 .unwrap_or(false)
2559 }
2560
2561 pub fn get_last_input_sample(&self) -> Option<&InputSample> {
2563 let manager = self.get_gesture_drag_manager();
2564 manager
2565 .get_current_session()
2566 .and_then(|session| session.last_sample())
2567 }
2568
2569 pub fn get_current_event_id(&self) -> Option<u64> {
2571 self.get_last_input_sample().map(|sample| sample.event_id)
2572 }
2573
2574 pub fn set_focus_to_node(&mut self, dom_id: DomId, node_id: NodeId) {
2578 self.set_focus(FocusTarget::Id(DomNodeId {
2579 dom: dom_id,
2580 node: NodeHierarchyItemId::from_crate_internal(Some(node_id)),
2581 }));
2582 }
2583
2584 pub fn set_focus_to_path(&mut self, dom_id: DomId, css_path: CssPath) {
2586 self.set_focus(FocusTarget::Path(FocusTargetPath {
2587 dom: dom_id,
2588 css_path,
2589 }));
2590 }
2591
2592 pub fn focus_next(&mut self) {
2594 self.set_focus(FocusTarget::Next);
2595 }
2596
2597 pub fn focus_previous(&mut self) {
2599 self.set_focus(FocusTarget::Previous);
2600 }
2601
2602 pub fn focus_first(&mut self) {
2604 self.set_focus(FocusTarget::First);
2605 }
2606
2607 pub fn focus_last(&mut self) {
2609 self.set_focus(FocusTarget::Last);
2610 }
2611
2612 pub fn clear_focus(&mut self) {
2614 self.set_focus(FocusTarget::NoFocus);
2615 }
2616
2617 pub fn is_dragging(&self) -> bool {
2623 self.get_gesture_drag_manager().is_dragging()
2624 }
2625
2626 pub fn get_focused_node(&self) -> Option<DomNodeId> {
2630 self.get_layout_window()
2631 .focus_manager
2632 .get_focused_node()
2633 .copied()
2634 }
2635
2636 pub fn has_focus(&self, node_id: DomNodeId) -> bool {
2638 self.get_layout_window().focus_manager.has_focus(&node_id)
2639 }
2640
2641 pub fn get_hovered_file(&self) -> Option<&azul_css::AzString> {
2645 self.get_layout_window()
2646 .file_drop_manager
2647 .get_hovered_file()
2648 }
2649
2650 pub fn get_dropped_file(&self) -> Option<&azul_css::AzString> {
2655 self.get_layout_window()
2656 .file_drop_manager
2657 .dropped_file
2658 .as_ref()
2659 }
2660
2661 pub fn is_drag_active(&self) -> bool {
2665 self.get_layout_window().drag_drop_manager.is_dragging()
2666 }
2667
2668 pub fn is_node_drag_active(&self) -> bool {
2670 self.get_layout_window()
2671 .drag_drop_manager
2672 .is_dragging_node()
2673 }
2674
2675 pub fn is_file_drag_active(&self) -> bool {
2677 self.get_layout_window()
2678 .drag_drop_manager
2679 .is_dragging_file()
2680 }
2681
2682 pub fn get_drag_state(&self) -> Option<crate::managers::drag_drop::DragState> {
2687 self.get_layout_window().drag_drop_manager.get_drag_state()
2688 }
2689
2690 pub fn get_drag_context(&self) -> Option<&azul_core::drag::DragContext> {
2695 self.get_layout_window().drag_drop_manager.get_drag_context()
2696 }
2697
2698 pub fn get_current_hit_test(&self) -> Option<&FullHitTest> {
2702 self.get_hover_manager().get_current(&InputPointId::Mouse)
2703 }
2704
2705 pub fn get_hit_test_frame(&self, frames_ago: usize) -> Option<&FullHitTest> {
2707 self.get_hover_manager()
2708 .get_frame(&InputPointId::Mouse, frames_ago)
2709 }
2710
2711 pub fn get_hit_test_history(&self) -> Option<&VecDeque<FullHitTest>> {
2715 self.get_hover_manager().get_history(&InputPointId::Mouse)
2716 }
2717
2718 pub fn has_sufficient_history_for_gestures(&self) -> bool {
2720 self.get_hover_manager()
2721 .has_sufficient_history_for_gestures(&InputPointId::Mouse)
2722 }
2723
2724 pub fn get_file_drop_manager(&self) -> &FileDropManager {
2728 &self.get_layout_window().file_drop_manager
2729 }
2730
2731 pub fn get_all_selections(&self) -> &BTreeMap<DomId, SelectionState> {
2733 self.get_selection_manager().get_all_selections()
2734 }
2735
2736 pub fn get_drag_drop_manager(&self) -> &DragDropManager {
2740 &self.get_layout_window().drag_drop_manager
2741 }
2742
2743 pub fn get_dragged_node(&self) -> Option<DomNodeId> {
2745 self.get_drag_drop_manager()
2746 .get_drag_context()
2747 .and_then(|ctx| {
2748 ctx.as_node_drag().map(|node_drag| {
2749 DomNodeId {
2750 dom: node_drag.dom_id,
2751 node: azul_core::styled_dom::NodeHierarchyItemId::from_crate_internal(Some(node_drag.node_id)),
2752 }
2753 })
2754 })
2755 }
2756
2757 pub fn get_dragged_file(&self) -> Option<&AzString> {
2759 self.get_drag_drop_manager()
2760 .get_drag_context()
2761 .and_then(|ctx| {
2762 ctx.as_file_drop().and_then(|file_drop| {
2763 file_drop.files.as_ref().first()
2764 })
2765 })
2766 }
2767
2768 pub fn get_scroll_offset(&self) -> Option<LogicalPosition> {
2775 self.get_scroll_offset_for_node(
2776 self.hit_dom_node.dom,
2777 self.hit_dom_node.node.into_crate_internal().unwrap(),
2778 )
2779 }
2780
2781 pub fn get_scroll_offset_for_node(
2783 &self,
2784 dom_id: DomId,
2785 node_id: NodeId,
2786 ) -> Option<LogicalPosition> {
2787 self.get_scroll_manager()
2788 .get_current_offset(dom_id, node_id)
2789 }
2790
2791 pub fn get_scroll_delta(&self, dom_id: DomId, node_id: NodeId) -> Option<LogicalPosition> {
2793 self.get_scroll_manager().get_scroll_delta(dom_id, node_id)
2794 }
2795
2796 pub fn had_scroll_activity(&self, dom_id: DomId, node_id: NodeId) -> bool {
2798 self.get_scroll_manager()
2799 .had_scroll_activity_for_node(dom_id, node_id)
2800 }
2801
2802 pub fn get_scroll_state(&self, dom_id: DomId, node_id: NodeId) -> Option<&AnimatedScrollState> {
2804 self.get_scroll_manager().get_scroll_state(dom_id, node_id)
2805 }
2806
2807 pub fn get_gpu_state_manager(&self) -> &GpuStateManager {
2811 &self.get_layout_window().gpu_state_manager
2812 }
2813
2814 pub fn get_iframe_manager(&self) -> &IFrameManager {
2818 &self.get_layout_window().iframe_manager
2819 }
2820
2821 pub fn inspect_copy_changeset(&self, target: DomNodeId) -> Option<ClipboardContent> {
2829 let layout_window = self.get_layout_window();
2830 let dom_id = &target.dom;
2831 layout_window.get_selected_content_for_clipboard(dom_id)
2832 }
2833
2834 pub fn inspect_cut_changeset(&self, target: DomNodeId) -> Option<ClipboardContent> {
2839 self.inspect_copy_changeset(target)
2841 }
2842
2843 pub fn inspect_paste_target_range(&self, target: DomNodeId) -> Option<SelectionRange> {
2848 let layout_window = self.get_layout_window();
2849 let dom_id = &target.dom;
2850 layout_window
2851 .selection_manager
2852 .get_ranges(dom_id)
2853 .first()
2854 .copied()
2855 }
2856
2857 pub fn inspect_select_all_changeset(&self, target: DomNodeId) -> Option<SelectAllResult> {
2861 use azul_core::selection::{CursorAffinity, GraphemeClusterId, TextCursor};
2862
2863 let layout_window = self.get_layout_window();
2864 let node_id = target.node.into_crate_internal()?;
2865
2866 let content = layout_window.get_text_before_textinput(target.dom, node_id);
2868 let text = layout_window.extract_text_from_inline_content(&content);
2869
2870 let start_cursor = TextCursor {
2872 cluster_id: GraphemeClusterId {
2873 source_run: 0,
2874 start_byte_in_run: 0,
2875 },
2876 affinity: CursorAffinity::Leading,
2877 };
2878
2879 let end_cursor = TextCursor {
2880 cluster_id: GraphemeClusterId {
2881 source_run: 0,
2882 start_byte_in_run: text.len() as u32,
2883 },
2884 affinity: CursorAffinity::Leading,
2885 };
2886
2887 let range = SelectionRange {
2888 start: start_cursor,
2889 end: end_cursor,
2890 };
2891
2892 Some(SelectAllResult {
2893 full_text: text.into(),
2894 selection_range: range,
2895 })
2896 }
2897
2898 pub fn inspect_delete_changeset(
2907 &self,
2908 target: DomNodeId,
2909 forward: bool,
2910 ) -> Option<DeleteResult> {
2911 let layout_window = self.get_layout_window();
2912 let dom_id = &target.dom;
2913 let node_id = target.node.into_crate_internal()?;
2914
2915 let content = layout_window.get_text_before_textinput(target.dom, node_id);
2917
2918 let selection =
2920 if let Some(range) = layout_window.selection_manager.get_ranges(dom_id).first() {
2921 Selection::Range(*range)
2922 } else if let Some(cursor) = layout_window.cursor_manager.get_cursor() {
2923 Selection::Cursor(*cursor)
2924 } else {
2925 return None; };
2927
2928 crate::text3::edit::inspect_delete(&content, &selection, forward).map(|(range, text)| {
2930 DeleteResult {
2931 range_to_delete: range,
2932 deleted_text: text.into(),
2933 }
2934 })
2935 }
2936
2937 pub fn inspect_undo_operation(&self, node_id: NodeId) -> Option<&UndoableOperation> {
2942 self.get_undo_redo_manager().peek_undo(node_id)
2943 }
2944
2945 pub fn inspect_redo_operation(&self, node_id: NodeId) -> Option<&UndoableOperation> {
2949 self.get_undo_redo_manager().peek_redo(node_id)
2950 }
2951
2952 pub fn can_undo(&self, node_id: NodeId) -> bool {
2956 self.get_undo_redo_manager()
2957 .get_stack(node_id)
2958 .map(|stack| stack.can_undo())
2959 .unwrap_or(false)
2960 }
2961
2962 pub fn can_redo(&self, node_id: NodeId) -> bool {
2966 self.get_undo_redo_manager()
2967 .get_stack(node_id)
2968 .map(|stack| stack.can_redo())
2969 .unwrap_or(false)
2970 }
2971
2972 pub fn get_undo_text(&self, node_id: NodeId) -> Option<AzString> {
2977 self.get_undo_redo_manager()
2978 .peek_undo(node_id)
2979 .map(|op| op.pre_state.text_content.clone())
2980 }
2981
2982 pub fn get_redo_text(&self, node_id: NodeId) -> Option<AzString> {
2987 self.get_undo_redo_manager()
2988 .peek_redo(node_id)
2989 .map(|op| op.pre_state.text_content.clone())
2990 }
2991
2992 pub fn get_clipboard_content(&self) -> Option<&ClipboardContent> {
3005 unsafe {
3006 (*self.ref_data)
3007 .layout_window
3008 .clipboard_manager
3009 .get_paste_content()
3010 }
3011 }
3012
3013 pub fn set_clipboard_content(&mut self, content: ClipboardContent) {
3021 self.push_change(CallbackChange::SetCopyContent {
3024 target: self.hit_dom_node,
3025 content,
3026 });
3027 }
3028
3029 pub fn set_copy_content(&mut self, target: DomNodeId, content: ClipboardContent) {
3035 self.push_change(CallbackChange::SetCopyContent { target, content });
3036 }
3037
3038 pub fn set_cut_content(&mut self, target: DomNodeId, content: ClipboardContent) {
3043 self.push_change(CallbackChange::SetCutContent { target, content });
3044 }
3045
3046 pub fn set_select_all_range(&mut self, target: DomNodeId, range: SelectionRange) {
3051 self.push_change(CallbackChange::SetSelectAllRange { target, range });
3052 }
3053
3054 pub fn request_hit_test_update(&mut self, position: LogicalPosition) {
3062 self.push_change(CallbackChange::RequestHitTestUpdate { position });
3063 }
3064
3065 pub fn process_text_selection_click(&mut self, position: LogicalPosition, time_ms: u64) {
3073 self.push_change(CallbackChange::ProcessTextSelectionClick { position, time_ms });
3074 }
3075
3076 pub fn get_node_text_content(&self, target: DomNodeId) -> Option<String> {
3080 let layout_window = self.get_layout_window();
3081 let node_id = target.node.into_crate_internal()?;
3082 let content = layout_window.get_text_before_textinput(target.dom, node_id);
3083 Some(layout_window.extract_text_from_inline_content(&content))
3084 }
3085
3086 pub fn get_node_cursor_position(&self, target: DomNodeId) -> Option<TextCursor> {
3090 let layout_window = self.get_layout_window();
3091
3092 if !layout_window.focus_manager.has_focus(&target) {
3094 return None;
3095 }
3096
3097 layout_window.cursor_manager.get_cursor().copied()
3098 }
3099
3100 pub fn get_node_selection_ranges(&self, target: DomNodeId) -> SelectionRangeVec {
3104 let layout_window = self.get_layout_window();
3105 layout_window
3106 .selection_manager
3107 .get_ranges(&target.dom)
3108 .into()
3109 }
3110
3111 pub fn node_has_selection(&self, target: DomNodeId) -> bool {
3116 self.get_node_selection_ranges(target).as_ref().is_empty() == false
3117 }
3118
3119 pub fn get_node_text_length(&self, target: DomNodeId) -> Option<usize> {
3123 self.get_node_text_content(target).map(|text| text.len())
3124 }
3125
3126 pub fn inspect_move_cursor_left(&self, target: DomNodeId) -> Option<TextCursor> {
3136 let layout_window = self.get_layout_window();
3137 let cursor = layout_window.cursor_manager.get_cursor()?;
3138
3139 let layout = self.get_inline_layout_for_node(&target)?;
3142
3143 let new_cursor = layout.move_cursor_left(*cursor, &mut None);
3145
3146 if new_cursor != *cursor {
3148 Some(new_cursor)
3149 } else {
3150 None
3151 }
3152 }
3153
3154 pub fn inspect_move_cursor_right(&self, target: DomNodeId) -> Option<TextCursor> {
3159 let layout_window = self.get_layout_window();
3160 let cursor = layout_window.cursor_manager.get_cursor()?;
3161
3162 let layout = self.get_inline_layout_for_node(&target)?;
3165
3166 let new_cursor = layout.move_cursor_right(*cursor, &mut None);
3168
3169 if new_cursor != *cursor {
3171 Some(new_cursor)
3172 } else {
3173 None
3174 }
3175 }
3176
3177 pub fn inspect_move_cursor_up(&self, target: DomNodeId) -> Option<TextCursor> {
3182 let layout_window = self.get_layout_window();
3183 let cursor = layout_window.cursor_manager.get_cursor()?;
3184
3185 let layout = self.get_inline_layout_for_node(&target)?;
3188
3189 let new_cursor = layout.move_cursor_up(*cursor, &mut None, &mut None);
3192
3193 if new_cursor != *cursor {
3195 Some(new_cursor)
3196 } else {
3197 None
3198 }
3199 }
3200
3201 pub fn inspect_move_cursor_down(&self, target: DomNodeId) -> Option<TextCursor> {
3206 let layout_window = self.get_layout_window();
3207 let cursor = layout_window.cursor_manager.get_cursor()?;
3208
3209 let layout = self.get_inline_layout_for_node(&target)?;
3212
3213 let new_cursor = layout.move_cursor_down(*cursor, &mut None, &mut None);
3216
3217 if new_cursor != *cursor {
3219 Some(new_cursor)
3220 } else {
3221 None
3222 }
3223 }
3224
3225 pub fn inspect_move_cursor_to_line_start(&self, target: DomNodeId) -> Option<TextCursor> {
3229 let layout_window = self.get_layout_window();
3230 let cursor = layout_window.cursor_manager.get_cursor()?;
3231
3232 let layout = self.get_inline_layout_for_node(&target)?;
3235
3236 let new_cursor = layout.move_cursor_to_line_start(*cursor, &mut None);
3238
3239 Some(new_cursor)
3241 }
3242
3243 pub fn inspect_move_cursor_to_line_end(&self, target: DomNodeId) -> Option<TextCursor> {
3247 let layout_window = self.get_layout_window();
3248 let cursor = layout_window.cursor_manager.get_cursor()?;
3249
3250 let layout = self.get_inline_layout_for_node(&target)?;
3253
3254 let new_cursor = layout.move_cursor_to_line_end(*cursor, &mut None);
3256
3257 Some(new_cursor)
3259 }
3260
3261 pub fn inspect_move_cursor_to_document_start(&self, target: DomNodeId) -> Option<TextCursor> {
3265 use azul_core::selection::{CursorAffinity, GraphemeClusterId};
3266
3267 Some(TextCursor {
3268 cluster_id: GraphemeClusterId {
3269 source_run: 0,
3270 start_byte_in_run: 0,
3271 },
3272 affinity: CursorAffinity::Leading,
3273 })
3274 }
3275
3276 pub fn inspect_move_cursor_to_document_end(&self, target: DomNodeId) -> Option<TextCursor> {
3280 use azul_core::selection::{CursorAffinity, GraphemeClusterId};
3281
3282 let text_len = self.get_node_text_length(target)?;
3283
3284 Some(TextCursor {
3285 cluster_id: GraphemeClusterId {
3286 source_run: 0,
3287 start_byte_in_run: text_len as u32,
3288 },
3289 affinity: CursorAffinity::Leading,
3290 })
3291 }
3292
3293 pub fn inspect_backspace(&self, target: DomNodeId) -> Option<DeleteResult> {
3298 self.inspect_delete_changeset(target, false)
3299 }
3300
3301 pub fn inspect_delete(&self, target: DomNodeId) -> Option<DeleteResult> {
3306 self.inspect_delete_changeset(target, true)
3307 }
3308
3309 pub fn move_cursor_left(&mut self, target: DomNodeId, extend_selection: bool) {
3318 self.push_change(CallbackChange::MoveCursorLeft {
3319 dom_id: target.dom,
3320 node_id: target.node.into_crate_internal().unwrap_or(NodeId::ZERO),
3321 extend_selection,
3322 });
3323 }
3324
3325 pub fn move_cursor_right(&mut self, target: DomNodeId, extend_selection: bool) {
3327 self.push_change(CallbackChange::MoveCursorRight {
3328 dom_id: target.dom,
3329 node_id: target.node.into_crate_internal().unwrap_or(NodeId::ZERO),
3330 extend_selection,
3331 });
3332 }
3333
3334 pub fn move_cursor_up(&mut self, target: DomNodeId, extend_selection: bool) {
3336 self.push_change(CallbackChange::MoveCursorUp {
3337 dom_id: target.dom,
3338 node_id: target.node.into_crate_internal().unwrap_or(NodeId::ZERO),
3339 extend_selection,
3340 });
3341 }
3342
3343 pub fn move_cursor_down(&mut self, target: DomNodeId, extend_selection: bool) {
3345 self.push_change(CallbackChange::MoveCursorDown {
3346 dom_id: target.dom,
3347 node_id: target.node.into_crate_internal().unwrap_or(NodeId::ZERO),
3348 extend_selection,
3349 });
3350 }
3351
3352 pub fn move_cursor_to_line_start(&mut self, target: DomNodeId, extend_selection: bool) {
3354 self.push_change(CallbackChange::MoveCursorToLineStart {
3355 dom_id: target.dom,
3356 node_id: target.node.into_crate_internal().unwrap_or(NodeId::ZERO),
3357 extend_selection,
3358 });
3359 }
3360
3361 pub fn move_cursor_to_line_end(&mut self, target: DomNodeId, extend_selection: bool) {
3363 self.push_change(CallbackChange::MoveCursorToLineEnd {
3364 dom_id: target.dom,
3365 node_id: target.node.into_crate_internal().unwrap_or(NodeId::ZERO),
3366 extend_selection,
3367 });
3368 }
3369
3370 pub fn move_cursor_to_document_start(&mut self, target: DomNodeId, extend_selection: bool) {
3372 self.push_change(CallbackChange::MoveCursorToDocumentStart {
3373 dom_id: target.dom,
3374 node_id: target.node.into_crate_internal().unwrap_or(NodeId::ZERO),
3375 extend_selection,
3376 });
3377 }
3378
3379 pub fn move_cursor_to_document_end(&mut self, target: DomNodeId, extend_selection: bool) {
3381 self.push_change(CallbackChange::MoveCursorToDocumentEnd {
3382 dom_id: target.dom,
3383 node_id: target.node.into_crate_internal().unwrap_or(NodeId::ZERO),
3384 extend_selection,
3385 });
3386 }
3387
3388 pub fn delete_backward(&mut self, target: DomNodeId) {
3393 self.push_change(CallbackChange::DeleteBackward {
3394 dom_id: target.dom,
3395 node_id: target.node.into_crate_internal().unwrap_or(NodeId::ZERO),
3396 });
3397 }
3398
3399 pub fn delete_forward(&mut self, target: DomNodeId) {
3404 self.push_change(CallbackChange::DeleteForward {
3405 dom_id: target.dom,
3406 node_id: target.node.into_crate_internal().unwrap_or(NodeId::ZERO),
3407 });
3408 }
3409}
3410
3411#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
3413#[repr(C)]
3414pub struct ExternalSystemCallbacks {
3415 pub create_thread_fn: CreateThreadCallback,
3416 pub get_system_time_fn: GetSystemTimeCallback,
3417}
3418
3419impl ExternalSystemCallbacks {
3420 #[cfg(not(feature = "std"))]
3421 pub fn rust_internal() -> Self {
3422 use crate::thread::create_thread_libstd;
3423
3424 Self {
3425 create_thread_fn: CreateThreadCallback {
3426 cb: create_thread_libstd,
3427 },
3428 get_system_time_fn: GetSystemTimeCallback {
3429 cb: azul_core::task::get_system_time_libstd,
3430 },
3431 }
3432 }
3433
3434 #[cfg(feature = "std")]
3435 pub fn rust_internal() -> Self {
3436 use crate::thread::create_thread_libstd;
3437
3438 Self {
3439 create_thread_fn: CreateThreadCallback {
3440 cb: create_thread_libstd,
3441 },
3442 get_system_time_fn: GetSystemTimeCallback {
3443 cb: azul_core::task::get_system_time_libstd,
3444 },
3445 }
3446 }
3447}
3448
3449#[derive(Debug, Clone, PartialEq, Eq)]
3451pub enum FocusUpdateRequest {
3452 FocusNode(DomNodeId),
3454 ClearFocus,
3456 NoChange,
3458}
3459
3460impl FocusUpdateRequest {
3461 pub fn is_change(&self) -> bool {
3463 !matches!(self, FocusUpdateRequest::NoChange)
3464 }
3465
3466 pub fn to_focused_node(&self) -> Option<Option<DomNodeId>> {
3468 match self {
3469 FocusUpdateRequest::FocusNode(node) => Some(Some(*node)),
3470 FocusUpdateRequest::ClearFocus => Some(None),
3471 FocusUpdateRequest::NoChange => None,
3472 }
3473 }
3474
3475 pub fn from_optional(opt: Option<Option<DomNodeId>>) -> Self {
3477 match opt {
3478 Some(Some(node)) => FocusUpdateRequest::FocusNode(node),
3479 Some(None) => FocusUpdateRequest::ClearFocus,
3480 None => FocusUpdateRequest::NoChange,
3481 }
3482 }
3483}
3484
3485#[derive(Debug)]
3487pub struct CallCallbacksResult {
3488 pub should_scroll_render: bool,
3490 pub callbacks_update_screen: Update,
3492 pub modified_window_state: Option<FullWindowState>,
3494 pub words_changed: Option<BTreeMap<DomId, BTreeMap<NodeId, AzString>>>,
3496 pub images_changed: Option<BTreeMap<DomId, BTreeMap<NodeId, (ImageRef, UpdateImageType)>>>,
3498 pub image_masks_changed: Option<BTreeMap<DomId, BTreeMap<NodeId, ImageMask>>>,
3500 pub image_callbacks_changed: Option<BTreeMap<DomId, FastBTreeSet<NodeId>>>,
3502 pub css_properties_changed: Option<BTreeMap<DomId, BTreeMap<NodeId, CssPropertyVec>>>,
3504 pub nodes_scrolled_in_callbacks:
3506 Option<BTreeMap<DomId, BTreeMap<NodeHierarchyItemId, LogicalPosition>>>,
3507 pub update_focused_node: FocusUpdateRequest,
3509 pub timers: Option<FastHashMap<TimerId, Timer>>,
3511 pub threads: Option<FastHashMap<ThreadId, Thread>>,
3513 pub timers_removed: Option<FastBTreeSet<TimerId>>,
3515 pub threads_removed: Option<FastBTreeSet<ThreadId>>,
3517 pub windows_created: Vec<WindowCreateOptions>,
3519 pub menus_to_open: Vec<(Menu, Option<LogicalPosition>)>,
3521 pub tooltips_to_show: Vec<(AzString, LogicalPosition)>,
3523 pub hide_tooltip: bool,
3525 pub cursor_changed: bool,
3527 pub stop_propagation: bool,
3529 pub prevent_default: bool,
3531 pub hit_test_update_requested: Option<LogicalPosition>,
3534 pub queued_window_states: Vec<FullWindowState>,
3537 pub text_input_triggered: Vec<(azul_core::dom::DomNodeId, Vec<azul_core::events::EventFilter>)>,
3541}
3542
3543impl Default for CallCallbacksResult {
3544 fn default() -> Self {
3545 Self {
3546 should_scroll_render: false,
3547 callbacks_update_screen: Update::DoNothing,
3548 modified_window_state: None,
3549 words_changed: None,
3550 images_changed: None,
3551 image_masks_changed: None,
3552 image_callbacks_changed: None,
3553 css_properties_changed: None,
3554 nodes_scrolled_in_callbacks: None,
3555 update_focused_node: FocusUpdateRequest::NoChange,
3556 timers: None,
3557 threads: None,
3558 timers_removed: None,
3559 threads_removed: None,
3560 windows_created: Vec::new(),
3561 menus_to_open: Vec::new(),
3562 tooltips_to_show: Vec::new(),
3563 hide_tooltip: false,
3564 cursor_changed: false,
3565 stop_propagation: false,
3566 prevent_default: false,
3567 hit_test_update_requested: None,
3568 queued_window_states: Vec::new(),
3569 text_input_triggered: Vec::new(),
3570 }
3571 }
3572}
3573
3574impl CallCallbacksResult {
3575 pub fn cursor_changed(&self) -> bool {
3576 self.cursor_changed
3577 }
3578
3579 pub fn focus_changed(&self) -> bool {
3580 self.update_focused_node.is_change()
3581 }
3582}
3583
3584impl azul_core::events::CallbackResultRef for CallCallbacksResult {
3585 fn stop_propagation(&self) -> bool {
3586 self.stop_propagation
3587 }
3588
3589 fn prevent_default(&self) -> bool {
3590 self.prevent_default
3591 }
3592
3593 fn should_regenerate_dom(&self) -> bool {
3594 use azul_core::callbacks::Update;
3595 matches!(
3596 self.callbacks_update_screen,
3597 Update::RefreshDom | Update::RefreshDomAllWindows
3598 )
3599 }
3600}
3601
3602#[derive(Debug, Clone, PartialEq, PartialOrd, Hash, Eq, Ord)]
3605#[repr(C)]
3606pub struct MenuCallback {
3607 pub callback: Callback,
3608 pub refany: RefAny,
3609}
3610
3611#[derive(Debug, Clone, PartialEq, PartialOrd, Hash, Eq, Ord)]
3613#[repr(C, u8)]
3614pub enum OptionMenuCallback {
3615 None,
3616 Some(MenuCallback),
3617}
3618
3619impl OptionMenuCallback {
3620 pub fn into_option(self) -> Option<MenuCallback> {
3621 match self {
3622 OptionMenuCallback::None => None,
3623 OptionMenuCallback::Some(c) => Some(c),
3624 }
3625 }
3626
3627 pub fn is_some(&self) -> bool {
3628 matches!(self, OptionMenuCallback::Some(_))
3629 }
3630
3631 pub fn is_none(&self) -> bool {
3632 matches!(self, OptionMenuCallback::None)
3633 }
3634}
3635
3636impl From<Option<MenuCallback>> for OptionMenuCallback {
3637 fn from(o: Option<MenuCallback>) -> Self {
3638 match o {
3639 None => OptionMenuCallback::None,
3640 Some(c) => OptionMenuCallback::Some(c),
3641 }
3642 }
3643}
3644
3645impl From<OptionMenuCallback> for Option<MenuCallback> {
3646 fn from(o: OptionMenuCallback) -> Self {
3647 o.into_option()
3648 }
3649}
3650
3651pub type RenderImageCallbackType = extern "C" fn(RefAny, RenderImageCallbackInfo) -> ImageRef;
3659
3660#[repr(C)]
3667pub struct RenderImageCallback {
3668 pub cb: RenderImageCallbackType,
3669 pub ctx: OptionRefAny,
3672}
3673
3674impl_callback!(RenderImageCallback, RenderImageCallbackType);
3675
3676impl RenderImageCallback {
3677 pub fn create(cb: RenderImageCallbackType) -> Self {
3679 Self {
3680 cb,
3681 ctx: OptionRefAny::None,
3682 }
3683 }
3684
3685 pub fn from_core(core_callback: &azul_core::callbacks::CoreRenderImageCallback) -> Self {
3693 Self {
3694 cb: unsafe { core::mem::transmute(core_callback.cb) },
3695 ctx: core_callback.ctx.clone(),
3696 }
3697 }
3698
3699 pub fn to_core(self) -> azul_core::callbacks::CoreRenderImageCallback {
3703 azul_core::callbacks::CoreRenderImageCallback {
3704 cb: self.cb as usize,
3705 ctx: self.ctx,
3706 }
3707 }
3708}
3709
3710impl From<RenderImageCallback> for azul_core::callbacks::CoreRenderImageCallback {
3712 fn from(callback: RenderImageCallback) -> Self {
3713 callback.to_core()
3714 }
3715}
3716
3717#[derive(Debug)]
3719#[repr(C)]
3720pub struct RenderImageCallbackInfo {
3721 callback_node_id: DomNodeId,
3723 bounds: HidpiAdjustedBounds,
3725 gl_context: *const OptionGlContextPtr,
3727 image_cache: *const ImageCache,
3729 system_fonts: *const FcFontCache,
3731 callable_ptr: *const OptionRefAny,
3733 _abi_mut: *mut core::ffi::c_void,
3735}
3736
3737impl Clone for RenderImageCallbackInfo {
3738 fn clone(&self) -> Self {
3739 Self {
3740 callback_node_id: self.callback_node_id,
3741 bounds: self.bounds,
3742 gl_context: self.gl_context,
3743 image_cache: self.image_cache,
3744 system_fonts: self.system_fonts,
3745 callable_ptr: self.callable_ptr,
3746 _abi_mut: self._abi_mut,
3747 }
3748 }
3749}
3750
3751impl RenderImageCallbackInfo {
3752 pub fn new<'a>(
3753 callback_node_id: DomNodeId,
3754 bounds: HidpiAdjustedBounds,
3755 gl_context: &'a OptionGlContextPtr,
3756 image_cache: &'a ImageCache,
3757 system_fonts: &'a FcFontCache,
3758 ) -> Self {
3759 Self {
3760 callback_node_id,
3761 bounds,
3762 gl_context: gl_context as *const OptionGlContextPtr,
3763 image_cache: image_cache as *const ImageCache,
3764 system_fonts: system_fonts as *const FcFontCache,
3765 callable_ptr: core::ptr::null(),
3766 _abi_mut: core::ptr::null_mut(),
3767 }
3768 }
3769
3770 pub fn get_ctx(&self) -> OptionRefAny {
3772 if self.callable_ptr.is_null() {
3773 OptionRefAny::None
3774 } else {
3775 unsafe { (*self.callable_ptr).clone() }
3776 }
3777 }
3778
3779 pub unsafe fn set_callable_ptr(&mut self, ptr: *const OptionRefAny) {
3781 self.callable_ptr = ptr;
3782 }
3783
3784 pub fn get_callback_node_id(&self) -> DomNodeId {
3785 self.callback_node_id
3786 }
3787
3788 pub fn get_bounds(&self) -> HidpiAdjustedBounds {
3789 self.bounds
3790 }
3791
3792 fn internal_get_gl_context<'a>(&'a self) -> &'a OptionGlContextPtr {
3793 unsafe { &*self.gl_context }
3794 }
3795
3796 fn internal_get_image_cache<'a>(&'a self) -> &'a ImageCache {
3797 unsafe { &*self.image_cache }
3798 }
3799
3800 fn internal_get_system_fonts<'a>(&'a self) -> &'a FcFontCache {
3801 unsafe { &*self.system_fonts }
3802 }
3803
3804 pub fn get_gl_context(&self) -> OptionGlContextPtr {
3805 self.internal_get_gl_context().clone()
3806 }
3807}
3808
3809#[derive(Debug, Clone)]
3815#[repr(C, u8)]
3816pub enum ResultU8VecString {
3817 Ok(azul_css::U8Vec),
3818 Err(AzString),
3819}
3820
3821impl From<Result<alloc::vec::Vec<u8>, AzString>> for ResultU8VecString {
3822 fn from(result: Result<alloc::vec::Vec<u8>, AzString>) -> Self {
3823 match result {
3824 Ok(v) => ResultU8VecString::Ok(v.into()),
3825 Err(e) => ResultU8VecString::Err(e),
3826 }
3827 }
3828}
3829
3830#[derive(Debug, Clone)]
3832#[repr(C, u8)]
3833pub enum ResultVoidString {
3834 Ok,
3835 Err(AzString),
3836}
3837
3838impl From<Result<(), AzString>> for ResultVoidString {
3839 fn from(result: Result<(), AzString>) -> Self {
3840 match result {
3841 Ok(()) => ResultVoidString::Ok,
3842 Err(e) => ResultVoidString::Err(e),
3843 }
3844 }
3845}
3846
3847#[derive(Debug, Clone)]
3849#[repr(C, u8)]
3850pub enum ResultStringString {
3851 Ok(AzString),
3852 Err(AzString),
3853}
3854
3855impl From<Result<AzString, AzString>> for ResultStringString {
3856 fn from(result: Result<AzString, AzString>) -> Self {
3857 match result {
3858 Ok(s) => ResultStringString::Ok(s),
3859 Err(e) => ResultStringString::Err(e),
3860 }
3861 }
3862}
3863
3864const BASE64_ALPHABET: &[u8; 64] =
3869 b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
3870
3871fn base64_encode(input: &[u8]) -> alloc::string::String {
3873 let mut output = alloc::string::String::with_capacity((input.len() + 2) / 3 * 4);
3874
3875 for chunk in input.chunks(3) {
3876 let b0 = chunk[0] as usize;
3877 let b1 = chunk.get(1).copied().unwrap_or(0) as usize;
3878 let b2 = chunk.get(2).copied().unwrap_or(0) as usize;
3879
3880 let n = (b0 << 16) | (b1 << 8) | b2;
3881
3882 output.push(BASE64_ALPHABET[(n >> 18) & 0x3F] as char);
3883 output.push(BASE64_ALPHABET[(n >> 12) & 0x3F] as char);
3884
3885 if chunk.len() > 1 {
3886 output.push(BASE64_ALPHABET[(n >> 6) & 0x3F] as char);
3887 } else {
3888 output.push('=');
3889 }
3890
3891 if chunk.len() > 2 {
3892 output.push(BASE64_ALPHABET[n & 0x3F] as char);
3893 } else {
3894 output.push('=');
3895 }
3896 }
3897
3898 output
3899}