1#[cfg(not(feature = "std"))]
2use alloc::string::{String, ToString};
3use alloc::{
4 boxed::Box,
5 collections::{btree_map::BTreeMap, btree_set::BTreeSet},
6 vec::Vec,
7};
8use core::{
9 cmp::Ordering,
10 ffi::c_void,
11 hash::{Hash, Hasher},
12 ops,
13 sync::atomic::{AtomicI64, AtomicUsize, Ordering as AtomicOrdering},
14};
15
16use azul_css::{
17 AzString, ColorU, CssPath, CssProperty, FloatValue, LayoutPoint, LayoutRect, LayoutSize,
18 OptionAzString, OptionF32, OptionI32, U8Vec,
19};
20use rust_fontconfig::FcFontCache;
21
22use crate::{
23 app_resources::{
24 DpiScaleFactor, Epoch, GlTextureCache, IdNamespace, ImageCache, ImageMask, ImageRef,
25 RendererResources, ResourceUpdate,
26 },
27 callbacks::{
28 Callback, CallbackType, DocumentId, DomNodeId, HitTestItem, LayoutCallback,
29 LayoutCallbackType, OptionCallback, PipelineId, RefAny, ScrollPosition, Update,
30 UpdateImageType,
31 },
32 display_list::RenderCallbacks,
33 dom::NodeHierarchy,
34 gl::OptionGlContextPtr,
35 id_tree::NodeId,
36 styled_dom::{DomId, NodeHierarchyItemId},
37 task::{ExternalSystemCallbacks, Instant, Thread, ThreadId, Timer, TimerId},
38 ui_solver::{
39 ExternalScrollId, HitTest, LayoutResult, OverflowingScrollNode, QuickResizeResult,
40 },
41 window_state::RelayoutFn,
42 FastBTreeSet, FastHashMap,
43};
44
45pub const DEFAULT_TITLE: &str = "Azul App";
46
47static LAST_WINDOW_ID: AtomicI64 = AtomicI64::new(0);
48
49#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
52#[repr(transparent)]
53pub struct WindowId {
54 pub id: i64,
55}
56
57impl WindowId {
58 pub fn new() -> Self {
59 WindowId {
60 id: LAST_WINDOW_ID.fetch_add(1, AtomicOrdering::SeqCst),
61 }
62 }
63}
64
65static LAST_ICON_KEY: AtomicUsize = AtomicUsize::new(0);
66
67#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
71#[repr(C)]
72pub struct IconKey {
73 id: usize,
74}
75
76impl IconKey {
77 pub fn new() -> Self {
78 Self {
79 id: LAST_ICON_KEY.fetch_add(1, AtomicOrdering::SeqCst),
80 }
81 }
82}
83
84#[repr(C)]
85#[derive(PartialEq, Copy, Clone, Debug, PartialOrd, Ord, Eq, Hash)]
86pub struct RendererOptions {
87 pub vsync: Vsync,
88 pub srgb: Srgb,
89 pub hw_accel: HwAcceleration,
90}
91
92impl_option!(
93 RendererOptions,
94 OptionRendererOptions,
95 [PartialEq, Copy, Clone, Debug, PartialOrd, Ord, Eq, Hash]
96);
97
98impl Default for RendererOptions {
99 fn default() -> Self {
100 Self {
101 vsync: Vsync::Enabled,
102 srgb: Srgb::Disabled,
103 hw_accel: HwAcceleration::Enabled,
104 }
105 }
106}
107
108impl RendererOptions {
109 pub const fn new(vsync: Vsync, srgb: Srgb, hw_accel: HwAcceleration) -> Self {
110 Self {
111 vsync,
112 srgb,
113 hw_accel,
114 }
115 }
116}
117
118#[repr(C)]
119#[derive(PartialEq, Copy, Clone, Debug, PartialOrd, Ord, Eq, Hash)]
120pub enum Vsync {
121 Enabled,
122 Disabled,
123 DontCare,
124}
125impl Vsync {
126 pub const fn is_enabled(&self) -> bool {
127 match self {
128 Vsync::Enabled => true,
129 _ => false,
130 }
131 }
132}
133
134#[repr(C)]
135#[derive(PartialEq, Copy, Clone, Debug, PartialOrd, Ord, Eq, Hash)]
136pub enum Srgb {
137 Enabled,
138 Disabled,
139 DontCare,
140}
141impl Srgb {
142 pub const fn is_enabled(&self) -> bool {
143 match self {
144 Srgb::Enabled => true,
145 _ => false,
146 }
147 }
148}
149
150#[repr(C)]
151#[derive(PartialEq, Copy, Clone, Debug, PartialOrd, Ord, Eq, Hash)]
152pub enum HwAcceleration {
153 Enabled,
154 Disabled,
155 DontCare,
156}
157impl HwAcceleration {
158 pub const fn is_enabled(&self) -> bool {
159 match self {
160 HwAcceleration::Enabled => true,
161 _ => false,
162 }
163 }
164}
165
166#[derive(Debug, Copy, Clone, PartialEq, Eq)]
167pub enum ProcessEventResult {
168 DoNothing = 0,
169 ShouldReRenderCurrentWindow = 1,
170 ShouldUpdateDisplayListCurrentWindow = 2,
171 UpdateHitTesterAndProcessAgain = 3,
174 ShouldRegenerateDomCurrentWindow = 4,
176 ShouldRegenerateDomAllWindows = 5,
177}
178
179impl ProcessEventResult {
180 pub fn order(&self) -> usize {
181 use self::ProcessEventResult::*;
182 match self {
183 DoNothing => 0,
184 ShouldReRenderCurrentWindow => 1,
185 ShouldUpdateDisplayListCurrentWindow => 2,
186 UpdateHitTesterAndProcessAgain => 3,
187 ShouldRegenerateDomCurrentWindow => 4,
188 ShouldRegenerateDomAllWindows => 5,
189 }
190 }
191}
192
193impl PartialOrd for ProcessEventResult {
194 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
195 self.order().partial_cmp(&other.order())
196 }
197}
198
199impl Ord for ProcessEventResult {
200 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
201 self.order().cmp(&other.order())
202 }
203}
204
205impl ProcessEventResult {
206 pub fn max_self(self, other: Self) -> Self {
207 self.max(other)
208 }
209}
210
211#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
212#[repr(C, u8)]
213pub enum RawWindowHandle {
214 IOS(IOSHandle),
215 MacOS(MacOSHandle),
216 Xlib(XlibHandle),
217 Xcb(XcbHandle),
218 Wayland(WaylandHandle),
219 Windows(WindowsHandle),
220 Web(WebHandle),
221 Android(AndroidHandle),
222 Unsupported,
223}
224
225unsafe impl Send for RawWindowHandle {}
226
227#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
228#[repr(C)]
229pub struct IOSHandle {
230 pub ui_window: *mut c_void,
231 pub ui_view: *mut c_void,
232 pub ui_view_controller: *mut c_void,
233}
234
235#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
236#[repr(C)]
237pub struct MacOSHandle {
238 pub ns_window: *mut c_void,
239 pub ns_view: *mut c_void,
240}
241
242#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
243#[repr(C)]
244pub struct XlibHandle {
245 pub window: u64,
247 pub display: *mut c_void,
248}
249
250#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
251#[repr(C)]
252pub struct XcbHandle {
253 pub window: u32,
255 pub connection: *mut c_void,
257}
258
259#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
260#[repr(C)]
261pub struct WaylandHandle {
262 pub surface: *mut c_void,
264 pub display: *mut c_void,
266}
267
268#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
269#[repr(C)]
270pub struct WindowsHandle {
271 pub hwnd: *mut c_void,
273 pub hinstance: *mut c_void,
275}
276
277#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
278#[repr(C)]
279pub struct WebHandle {
280 pub id: u32,
286}
287
288#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
289#[repr(C)]
290pub struct AndroidHandle {
291 pub a_native_window: *mut c_void,
293}
294
295#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
296#[repr(C)]
297pub enum MouseCursorType {
298 Default,
299 Crosshair,
300 Hand,
301 Arrow,
302 Move,
303 Text,
304 Wait,
305 Help,
306 Progress,
307 NotAllowed,
308 ContextMenu,
309 Cell,
310 VerticalText,
311 Alias,
312 Copy,
313 NoDrop,
314 Grab,
315 Grabbing,
316 AllScroll,
317 ZoomIn,
318 ZoomOut,
319 EResize,
320 NResize,
321 NeResize,
322 NwResize,
323 SResize,
324 SeResize,
325 SwResize,
326 WResize,
327 EwResize,
328 NsResize,
329 NeswResize,
330 NwseResize,
331 ColResize,
332 RowResize,
333}
334
335impl Default for MouseCursorType {
336 fn default() -> Self {
337 MouseCursorType::Default
338 }
339}
340
341pub type ScanCode = u32;
343
344#[derive(Default, Debug, Clone, PartialEq)]
346#[repr(C)]
347pub struct KeyboardState {
348 pub current_char: OptionChar,
350 pub current_virtual_keycode: OptionVirtualKeyCode,
356 pub pressed_virtual_keycodes: VirtualKeyCodeVec,
366 pub pressed_scancodes: ScanCodeVec,
372}
373
374impl KeyboardState {
375 pub fn shift_down(&self) -> bool {
376 self.is_key_down(VirtualKeyCode::LShift) || self.is_key_down(VirtualKeyCode::RShift)
377 }
378 pub fn ctrl_down(&self) -> bool {
379 self.is_key_down(VirtualKeyCode::LControl) || self.is_key_down(VirtualKeyCode::RControl)
380 }
381 pub fn alt_down(&self) -> bool {
382 self.is_key_down(VirtualKeyCode::LAlt) || self.is_key_down(VirtualKeyCode::RAlt)
383 }
384 pub fn super_down(&self) -> bool {
385 self.is_key_down(VirtualKeyCode::LWin) || self.is_key_down(VirtualKeyCode::RWin)
386 }
387 pub fn is_key_down(&self, key: VirtualKeyCode) -> bool {
388 self.pressed_virtual_keycodes.iter().any(|k| *k == key)
389 }
390}
391
392impl_option!(
393 KeyboardState,
394 OptionKeyboardState,
395 copy = false,
396 [Debug, Clone, PartialEq]
397);
398
399impl_option!(
401 u32,
402 OptionChar,
403 [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
404);
405impl_option!(
406 VirtualKeyCode,
407 OptionVirtualKeyCode,
408 [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
409);
410
411impl_vec!(
412 VirtualKeyCode,
413 VirtualKeyCodeVec,
414 VirtualKeyCodeVecDestructor
415);
416impl_vec_debug!(VirtualKeyCode, VirtualKeyCodeVec);
417impl_vec_partialord!(VirtualKeyCode, VirtualKeyCodeVec);
418impl_vec_ord!(VirtualKeyCode, VirtualKeyCodeVec);
419impl_vec_clone!(
420 VirtualKeyCode,
421 VirtualKeyCodeVec,
422 VirtualKeyCodeVecDestructor
423);
424impl_vec_partialeq!(VirtualKeyCode, VirtualKeyCodeVec);
425impl_vec_eq!(VirtualKeyCode, VirtualKeyCodeVec);
426impl_vec_hash!(VirtualKeyCode, VirtualKeyCodeVec);
427
428impl_vec_as_hashmap!(VirtualKeyCode, VirtualKeyCodeVec);
429
430impl_vec!(ScanCode, ScanCodeVec, ScanCodeVecDestructor);
431impl_vec_debug!(ScanCode, ScanCodeVec);
432impl_vec_partialord!(ScanCode, ScanCodeVec);
433impl_vec_ord!(ScanCode, ScanCodeVec);
434impl_vec_clone!(ScanCode, ScanCodeVec, ScanCodeVecDestructor);
435impl_vec_partialeq!(ScanCode, ScanCodeVec);
436impl_vec_eq!(ScanCode, ScanCodeVec);
437impl_vec_hash!(ScanCode, ScanCodeVec);
438
439impl_vec_as_hashmap!(ScanCode, ScanCodeVec);
440
441#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
443#[repr(C)]
444pub struct MouseState {
445 pub mouse_cursor_type: OptionMouseCursorType,
447 pub cursor_position: CursorPosition,
450 pub is_cursor_locked: bool,
453 pub left_down: bool,
455 pub right_down: bool,
457 pub middle_down: bool,
459 pub scroll_x: OptionF32,
462 pub scroll_y: OptionF32,
465}
466
467impl MouseState {
468 pub fn matches(&self, context: &ContextMenuMouseButton) -> bool {
469 use self::ContextMenuMouseButton::*;
470 match context {
471 Left => self.left_down,
472 Right => self.right_down,
473 Middle => self.middle_down,
474 }
475 }
476}
477
478impl_option!(
479 MouseState,
480 OptionMouseState,
481 [Debug, Copy, Clone, PartialEq, PartialOrd]
482);
483
484impl_option!(
485 MouseCursorType,
486 OptionMouseCursorType,
487 [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
488);
489
490impl Default for MouseState {
491 fn default() -> Self {
492 Self {
493 mouse_cursor_type: Some(MouseCursorType::Default).into(),
494 cursor_position: CursorPosition::default(),
495 is_cursor_locked: false,
496 left_down: false,
497 right_down: false,
498 middle_down: false,
499 scroll_x: None.into(),
500 scroll_y: None.into(),
501 }
502 }
503}
504
505impl MouseState {
506 pub fn mouse_down(&self) -> bool {
508 self.right_down || self.left_down || self.middle_down
509 }
510
511 pub fn get_scroll_x(&self) -> f32 {
512 self.scroll_x.as_option().copied().unwrap_or(0.0)
513 }
514
515 pub fn get_scroll_y(&self) -> f32 {
516 self.scroll_y.as_option().copied().unwrap_or(0.0)
517 }
518
519 pub fn get_scroll(&self) -> (f32, f32) {
520 (self.get_scroll_x(), self.get_scroll_y())
521 }
522
523 pub fn get_scroll_amount(&self) -> Option<(f32, f32)> {
524 const SCROLL_THRESHOLD: f32 = 0.5; if self.scroll_x.is_none() && self.scroll_y.is_none() {
527 return None;
528 }
529
530 let scroll_x = self.get_scroll_x();
531 let scroll_y = self.get_scroll_y();
532
533 if libm::fabsf(scroll_x) < SCROLL_THRESHOLD && libm::fabsf(scroll_y) < SCROLL_THRESHOLD {
534 return None;
535 }
536
537 Some((scroll_x, scroll_y))
538 }
539
540 pub fn reset_scroll_to_zero(&mut self) {
542 self.scroll_x = OptionF32::None;
543 self.scroll_y = OptionF32::None;
544 }
545}
546
547#[derive(Debug)]
549pub struct ScrollResult {}
550
551#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
552#[repr(C, u8)]
553pub enum CursorPosition {
554 OutOfWindow(LogicalPosition),
555 Uninitialized,
556 InWindow(LogicalPosition),
557}
558
559impl Default for CursorPosition {
560 fn default() -> CursorPosition {
561 CursorPosition::Uninitialized
562 }
563}
564
565impl CursorPosition {
566 pub fn get_position(&self) -> Option<LogicalPosition> {
567 match self {
568 CursorPosition::InWindow(logical_pos) => Some(*logical_pos),
569 CursorPosition::OutOfWindow(_) | CursorPosition::Uninitialized => None,
570 }
571 }
572
573 pub fn is_inside_window(&self) -> bool {
574 self.get_position().is_some()
575 }
576}
577
578#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
581#[repr(C)]
582pub struct DebugState {
583 pub profiler_dbg: bool,
584 pub render_target_dbg: bool,
585 pub texture_cache_dbg: bool,
586 pub gpu_time_queries: bool,
587 pub gpu_sample_queries: bool,
588 pub disable_batching: bool,
589 pub epochs: bool,
590 pub echo_driver_messages: bool,
591 pub show_overdraw: bool,
592 pub gpu_cache_dbg: bool,
593 pub texture_cache_dbg_clear_evicted: bool,
594 pub picture_caching_dbg: bool,
595 pub primitive_dbg: bool,
596 pub zoom_dbg: bool,
597 pub small_screen: bool,
598 pub disable_opaque_pass: bool,
599 pub disable_alpha_pass: bool,
600 pub disable_clip_masks: bool,
601 pub disable_text_prims: bool,
602 pub disable_gradient_prims: bool,
603 pub obscure_images: bool,
604 pub glyph_flashing: bool,
605 pub smart_profiler: bool,
606 pub invalidation_dbg: bool,
607 pub tile_cache_logging_dbg: bool,
608 pub profiler_capture: bool,
609 pub force_picture_invalidation: bool,
610}
611
612#[derive(Debug, Default)]
613pub struct ScrollStates(pub FastHashMap<ExternalScrollId, ScrollState>);
614
615impl ScrollStates {
616 #[must_use]
621 pub fn should_scroll_render(
622 &mut self,
623 (scroll_x, scroll_y): &(f32, f32),
624 hit_test: &FullHitTest,
625 ) -> bool {
626 let mut should_scroll_render = false;
627
628 for hit_test in hit_test.hovered_nodes.values() {
629 for scroll_hit_test_item in hit_test.scroll_hit_test_nodes.values() {
630 self.scroll_node(&scroll_hit_test_item.scroll_node, *scroll_x, *scroll_y);
631 should_scroll_render = true;
632 break; }
634 }
635
636 should_scroll_render
637 }
638
639 pub fn new() -> ScrollStates {
640 ScrollStates::default()
641 }
642
643 pub fn get_scroll_position(&self, scroll_id: &ExternalScrollId) -> Option<LogicalPosition> {
644 self.0.get(&scroll_id).map(|entry| entry.get())
645 }
646
647 pub fn set_scroll_position(
650 &mut self,
651 node: &OverflowingScrollNode,
652 scroll_position: LogicalPosition,
653 ) {
654 self.0
655 .entry(node.parent_external_scroll_id)
656 .or_insert_with(|| ScrollState::default())
657 .set(scroll_position.x, scroll_position.y, &node.child_rect);
658 }
659
660 pub fn scroll_node(
663 &mut self,
664 node: &OverflowingScrollNode,
665 scroll_by_x: f32,
666 scroll_by_y: f32,
667 ) {
668 self.0
669 .entry(node.parent_external_scroll_id)
670 .or_insert_with(|| ScrollState::default())
671 .add(scroll_by_x, scroll_by_y, &node.child_rect);
672 }
673}
674
675#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
676pub struct ScrollState {
677 pub scroll_position: LogicalPosition,
679}
680
681impl ScrollState {
682 pub fn get(&self) -> LogicalPosition {
684 self.scroll_position
685 }
686
687 pub fn add(&mut self, x: f32, y: f32, child_rect: &LogicalRect) {
689 self.scroll_position.x = (self.scroll_position.x + x)
690 .max(0.0)
691 .min(child_rect.size.width);
692 self.scroll_position.y = (self.scroll_position.y + y)
693 .max(0.0)
694 .min(child_rect.size.height);
695 }
696
697 pub fn set(&mut self, x: f32, y: f32, child_rect: &LogicalRect) {
699 self.scroll_position.x = x.max(0.0).min(child_rect.size.width);
700 self.scroll_position.y = y.max(0.0).min(child_rect.size.height);
701 }
702}
703
704impl Default for ScrollState {
705 fn default() -> Self {
706 ScrollState {
707 scroll_position: LogicalPosition::zero(),
708 }
709 }
710}
711
712pub fn update_full_window_state(
715 full_window_state: &mut FullWindowState,
716 window_state: &WindowState,
717) {
718 full_window_state.title = window_state.title.clone();
719 full_window_state.size = window_state.size.into();
720 full_window_state.position = window_state.position.into();
721 full_window_state.flags = window_state.flags;
722 full_window_state.debug_state = window_state.debug_state;
723 full_window_state.keyboard_state = window_state.keyboard_state.clone();
724 full_window_state.mouse_state = window_state.mouse_state;
725 full_window_state.ime_position = window_state.ime_position.into();
726 full_window_state.platform_specific_options = window_state.platform_specific_options.clone();
727}
728
729#[derive(Debug)]
730pub struct WindowInternal {
731 pub renderer_resources: RendererResources,
733 pub renderer_type: Option<RendererType>,
735 pub previous_window_state: Option<FullWindowState>,
737 pub current_window_state: FullWindowState,
740 pub document_id: DocumentId,
743 pub id_namespace: IdNamespace,
745 pub epoch: Epoch,
748 pub layout_results: Vec<LayoutResult>,
750 pub gl_texture_cache: GlTextureCache,
752 pub scroll_states: ScrollStates,
754 pub timers: BTreeMap<TimerId, Timer>,
756 pub threads: BTreeMap<ThreadId, Thread>,
758}
759
760impl WindowInternal {
761 pub fn get_dpi_scale_factor(&self) -> DpiScaleFactor {
762 DpiScaleFactor {
763 inner: FloatValue::new(self.current_window_state.size.get_hidpi_factor()),
764 }
765 }
766}
767
768#[derive(Debug, Clone, PartialEq)]
769pub struct FullHitTest {
770 pub hovered_nodes: BTreeMap<DomId, HitTest>,
771 pub focused_node: Option<(DomId, NodeId)>,
772}
773
774impl FullHitTest {
775 pub fn empty(focused_node: Option<DomNodeId>) -> Self {
776 Self {
777 hovered_nodes: BTreeMap::new(),
778 focused_node: focused_node.and_then(|f| Some((f.dom, f.node.into_crate_internal()?))),
779 }
780 }
781}
782
783#[derive(Debug, Clone, Default, PartialEq)]
784pub struct CursorTypeHitTest {
785 pub cursor_node: Option<(DomId, NodeId)>,
789 pub cursor_icon: MouseCursorType,
792}
793
794impl CursorTypeHitTest {
795 pub fn new(hit_test: &FullHitTest, layout_results: &[LayoutResult]) -> Self {
796 use azul_css::StyleCursor;
797
798 let mut cursor_node = None;
799 let mut cursor_icon = MouseCursorType::Default;
800
801 for (dom_id, hit_nodes) in hit_test.hovered_nodes.iter() {
802 for (node_id, _) in hit_nodes.regular_hit_test_nodes.iter() {
803 let styled_dom = &layout_results[dom_id.inner].styled_dom;
805 let node_data_container = styled_dom.node_data.as_container();
806 if let Some(cursor_prop) = styled_dom.get_css_property_cache().get_cursor(
807 &node_data_container[*node_id],
808 node_id,
809 &styled_dom.styled_nodes.as_container()[*node_id].state,
810 ) {
811 cursor_node = Some((*dom_id, *node_id));
812 cursor_icon = match cursor_prop.get_property().copied().unwrap_or_default() {
813 StyleCursor::Alias => MouseCursorType::Alias,
814 StyleCursor::AllScroll => MouseCursorType::AllScroll,
815 StyleCursor::Cell => MouseCursorType::Cell,
816 StyleCursor::ColResize => MouseCursorType::ColResize,
817 StyleCursor::ContextMenu => MouseCursorType::ContextMenu,
818 StyleCursor::Copy => MouseCursorType::Copy,
819 StyleCursor::Crosshair => MouseCursorType::Crosshair,
820 StyleCursor::Default => MouseCursorType::Default,
821 StyleCursor::EResize => MouseCursorType::EResize,
822 StyleCursor::EwResize => MouseCursorType::EwResize,
823 StyleCursor::Grab => MouseCursorType::Grab,
824 StyleCursor::Grabbing => MouseCursorType::Grabbing,
825 StyleCursor::Help => MouseCursorType::Help,
826 StyleCursor::Move => MouseCursorType::Move,
827 StyleCursor::NResize => MouseCursorType::NResize,
828 StyleCursor::NsResize => MouseCursorType::NsResize,
829 StyleCursor::NeswResize => MouseCursorType::NeswResize,
830 StyleCursor::NwseResize => MouseCursorType::NwseResize,
831 StyleCursor::Pointer => MouseCursorType::Hand,
832 StyleCursor::Progress => MouseCursorType::Progress,
833 StyleCursor::RowResize => MouseCursorType::RowResize,
834 StyleCursor::SResize => MouseCursorType::SResize,
835 StyleCursor::SeResize => MouseCursorType::SeResize,
836 StyleCursor::Text => MouseCursorType::Text,
837 StyleCursor::Unset => MouseCursorType::Default,
838 StyleCursor::VerticalText => MouseCursorType::VerticalText,
839 StyleCursor::WResize => MouseCursorType::WResize,
840 StyleCursor::Wait => MouseCursorType::Wait,
841 StyleCursor::ZoomIn => MouseCursorType::ZoomIn,
842 StyleCursor::ZoomOut => MouseCursorType::ZoomOut,
843 }
844 }
845 }
846 }
847
848 Self {
849 cursor_node,
850 cursor_icon,
851 }
852 }
853}
854pub struct WindowInternalInit {
855 pub window_create_options: WindowCreateOptions,
856 pub document_id: DocumentId,
857 pub id_namespace: IdNamespace,
858}
859
860impl WindowInternal {
861 #[cfg(feature = "std")]
864 pub fn new<F>(
865 mut init: WindowInternalInit,
866 data: &mut RefAny,
867 image_cache: &ImageCache,
868 gl_context: &OptionGlContextPtr,
869 all_resource_updates: &mut Vec<ResourceUpdate>,
870 callbacks: &RenderCallbacks,
871 fc_cache_real: &mut FcFontCache,
872 relayout_fn: RelayoutFn,
873 hit_test_func: F,
874 ) -> Self
875 where
876 F: Fn(&FullWindowState, &ScrollStates, &[LayoutResult]) -> FullHitTest,
877 {
878 use crate::{
879 callbacks::LayoutCallbackInfo,
880 display_list::SolvedLayout,
881 window_state::{NodesToCheck, StyleAndLayoutChanges},
882 };
883
884 let mut inital_renderer_resources = RendererResources::default();
885
886 let epoch = Epoch::new();
887
888 let styled_dom = {
889 let layout_callback = &mut init.window_create_options.state.layout_callback;
890 let mut layout_info = LayoutCallbackInfo::new(
891 init.window_create_options.state.size,
892 init.window_create_options.state.theme,
893 image_cache,
894 gl_context,
895 &fc_cache_real,
896 );
897
898 match layout_callback {
899 LayoutCallback::Raw(r) => (r.cb)(data, &mut layout_info),
900 LayoutCallback::Marshaled(m) => {
901 let marshal_data = &mut m.marshal_data;
902 (m.cb.cb)(marshal_data, data, &mut layout_info)
903 }
904 }
905 };
906
907 let mut current_window_state = FullWindowState::from_window_state(
908 &init.window_create_options.state,
909 None,
910 None,
911 None,
912 FullHitTest::empty(None),
913 );
914
915 let SolvedLayout { mut layout_results } = SolvedLayout::new(
916 styled_dom,
917 epoch,
918 &init.document_id,
919 ¤t_window_state,
920 all_resource_updates,
921 init.id_namespace,
922 image_cache,
923 &fc_cache_real,
924 &callbacks,
925 &mut inital_renderer_resources,
926 DpiScaleFactor {
927 inner: FloatValue::new(init.window_create_options.state.size.get_hidpi_factor()),
928 },
929 );
930
931 let scroll_states = ScrollStates::default();
932
933 let ht = hit_test_func(¤t_window_state, &scroll_states, &layout_results);
936 current_window_state.last_hit_test = ht.clone();
937
938 let nodes_to_check = NodesToCheck::simulated_mouse_move(
939 &ht,
940 None, current_window_state.mouse_state.mouse_down(),
942 );
943
944 let _ = StyleAndLayoutChanges::new(
945 &nodes_to_check,
946 &mut layout_results,
947 &image_cache,
948 &mut inital_renderer_resources,
949 current_window_state.size.get_layout_size(),
950 &init.document_id,
951 Some(&BTreeMap::new()),
952 Some(&BTreeMap::new()),
953 &None,
954 relayout_fn,
955 );
956
957 let gl_texture_cache = GlTextureCache::new(
958 &mut layout_results,
959 gl_context,
960 init.id_namespace,
961 &init.document_id,
962 epoch,
963 current_window_state.size.get_hidpi_factor(),
964 image_cache,
965 &fc_cache_real,
966 callbacks,
967 all_resource_updates,
968 &mut inital_renderer_resources,
969 );
970
971 WindowInternal {
972 renderer_resources: inital_renderer_resources,
973 renderer_type: gl_context.as_ref().map(|r| r.renderer_type),
974 id_namespace: init.id_namespace,
975 previous_window_state: None,
976 current_window_state,
977 document_id: init.document_id,
978 epoch, layout_results,
980 gl_texture_cache,
981 timers: BTreeMap::new(),
982 threads: BTreeMap::new(),
983 scroll_states,
984 }
985 }
986
987 pub fn regenerate_styled_dom<F>(
989 &mut self,
990 data: &mut RefAny,
991 image_cache: &ImageCache,
992 gl_context: &OptionGlContextPtr,
993 all_resource_updates: &mut Vec<ResourceUpdate>,
994 current_window_dpi: DpiScaleFactor,
995 callbacks: &RenderCallbacks,
996 fc_cache_real: &mut FcFontCache,
997 relayout_fn: RelayoutFn,
998 mut hit_test_func: F,
999 ) where
1000 F: FnMut(&FullWindowState, &ScrollStates, &[LayoutResult]) -> FullHitTest,
1001 {
1002 use crate::{
1003 callbacks::LayoutCallbackInfo,
1004 display_list::SolvedLayout,
1005 gl::gl_textures_remove_epochs_from_pipeline,
1006 styled_dom::DefaultCallbacksCfg,
1007 window_state::{NodesToCheck, StyleAndLayoutChanges},
1008 };
1009
1010 let id_namespace = self.id_namespace;
1011
1012 let mut styled_dom = {
1013 let layout_callback = &mut self.current_window_state.layout_callback;
1014 let mut layout_info = LayoutCallbackInfo::new(
1015 self.current_window_state.size,
1016 self.current_window_state.theme,
1017 image_cache,
1018 gl_context,
1019 &fc_cache_real,
1020 );
1021
1022 match layout_callback {
1023 LayoutCallback::Raw(r) => (r.cb)(data, &mut layout_info),
1024 LayoutCallback::Marshaled(m) => {
1025 let marshal_data = &mut m.marshal_data;
1026 (m.cb.cb)(marshal_data, data, &mut layout_info)
1027 }
1028 }
1029 };
1030
1031 styled_dom.insert_default_system_callbacks(DefaultCallbacksCfg {
1032 smooth_scroll: self.current_window_state.flags.smooth_scroll_enabled,
1033 enable_autotab: self.current_window_state.flags.autotab_enabled,
1034 });
1035
1036 let SolvedLayout { mut layout_results } = SolvedLayout::new(
1037 styled_dom,
1038 self.epoch,
1039 &self.document_id,
1040 &self.current_window_state,
1041 all_resource_updates,
1042 id_namespace,
1043 image_cache,
1044 &fc_cache_real,
1045 callbacks,
1046 &mut self.renderer_resources,
1047 current_window_dpi,
1048 );
1049
1050 let ht = hit_test_func(
1052 &self.current_window_state,
1053 &self.scroll_states,
1054 &layout_results,
1055 );
1056 self.current_window_state.last_hit_test = ht.clone();
1057
1058 let nodes_to_check = NodesToCheck::simulated_mouse_move(
1060 &ht,
1061 self.current_window_state.focused_node,
1062 self.current_window_state.mouse_state.mouse_down(),
1063 );
1064
1065 let sl = StyleAndLayoutChanges::new(
1066 &nodes_to_check,
1067 &mut layout_results,
1068 &image_cache,
1069 &mut self.renderer_resources,
1070 self.current_window_state.size.get_layout_size(),
1071 &self.document_id,
1072 Some(&BTreeMap::new()),
1073 Some(&BTreeMap::new()),
1074 &None,
1075 relayout_fn,
1076 );
1077
1078 let gl_texture_cache = GlTextureCache::new(
1080 &mut layout_results,
1081 gl_context,
1082 self.id_namespace,
1083 &self.document_id,
1084 self.epoch,
1085 self.current_window_state.size.get_hidpi_factor(),
1086 image_cache,
1087 &fc_cache_real,
1088 callbacks,
1089 all_resource_updates,
1090 &mut self.renderer_resources,
1091 );
1092
1093 gl_textures_remove_epochs_from_pipeline(&self.document_id, self.epoch);
1095
1096 self.renderer_resources.do_gc(
1098 all_resource_updates,
1099 image_cache,
1100 &layout_results,
1101 &gl_texture_cache,
1102 );
1103
1104 self.epoch.increment();
1106 self.layout_results = layout_results;
1107 self.gl_texture_cache = gl_texture_cache;
1108 }
1109
1110 pub fn get_current_scroll_states(
1112 &self,
1113 ) -> BTreeMap<DomId, BTreeMap<NodeHierarchyItemId, ScrollPosition>> {
1114 self.layout_results
1115 .iter()
1116 .enumerate()
1117 .filter_map(|(dom_id, layout_result)| {
1118 let scroll_positions = layout_result
1119 .scrollable_nodes
1120 .overflowing_nodes
1121 .iter()
1122 .filter_map(|(node_id, overflowing_node)| {
1123 let scroll_position = ScrollPosition {
1124 parent_rect: overflowing_node.parent_rect,
1125 children_rect: overflowing_node.child_rect,
1126 };
1127 Some((*node_id, scroll_position))
1128 })
1129 .collect::<BTreeMap<_, _>>();
1130
1131 if scroll_positions.is_empty() {
1132 None
1133 } else {
1134 Some((DomId { inner: dom_id }, scroll_positions))
1135 }
1136 })
1137 .collect()
1138 }
1139
1140 pub fn get_content_size(&self) -> LogicalSize {
1143 let layout_result = match self.layout_results.get(0) {
1144 Some(s) => s,
1145 None => return LogicalSize::zero(),
1146 };
1147 let root_width =
1148 layout_result.width_calculated_rects.as_ref()[NodeId::ZERO].overflow_width();
1149 let root_height =
1150 layout_result.height_calculated_rects.as_ref()[NodeId::ZERO].overflow_height();
1151 LogicalSize::new(root_width, root_height)
1152 }
1153
1154 pub fn do_quick_resize(
1157 &mut self,
1158 image_cache: &ImageCache,
1159 callbacks: &RenderCallbacks,
1160 relayout_fn: RelayoutFn,
1161 fc_cache: &FcFontCache,
1162 gl_context: &OptionGlContextPtr,
1163 window_size: &WindowSize,
1164 window_theme: WindowTheme,
1165 ) -> QuickResizeResult {
1166 LayoutResult::do_quick_resize(
1167 self.id_namespace,
1168 self.document_id,
1169 self.epoch,
1170 DomId::ROOT_ID,
1171 image_cache,
1172 gl_context,
1173 &mut self.layout_results,
1174 &mut self.gl_texture_cache,
1175 &mut self.renderer_resources,
1176 callbacks,
1177 relayout_fn,
1178 fc_cache,
1179 window_size,
1180 window_theme,
1181 )
1182 }
1183
1184 pub fn may_have_changed_monitor(&self) -> bool {
1188 let previous = match self.previous_window_state.as_ref() {
1189 None => return true,
1190 Some(s) => s,
1191 };
1192 let current = &self.current_window_state;
1193
1194 previous.size.dimensions != current.size.dimensions && previous.position != current.position
1195 }
1196
1197 pub fn get_layout_size(&self) -> LayoutSize {
1198 LayoutSize::new(
1199 libm::roundf(self.current_window_state.size.dimensions.width) as isize,
1200 libm::roundf(self.current_window_state.size.dimensions.height) as isize,
1201 )
1202 }
1203
1204 pub fn get_menu_bar<'a>(&'a self) -> Option<&'a Box<Menu>> {
1206 let lr = self.layout_results.get(0)?;
1207 let ndc = lr.styled_dom.node_data.as_container();
1208 let nd = ndc.get_extended_lifetime(NodeId::ZERO)?;
1209 let mb = nd.get_menu_bar();
1210 mb
1211 }
1212
1213 pub fn get_context_menu<'a>(&'a self) -> Option<(&'a Box<Menu>, HitTestItem, DomNodeId)> {
1216 let mut context_menu = None;
1217 let hit_test = &self.current_window_state.last_hit_test;
1218
1219 for (dom_id, hit_test) in hit_test.hovered_nodes.iter() {
1220 let layout_result = self.layout_results.get(dom_id.inner)?;
1221 for (node_id, hit) in hit_test.regular_hit_test_nodes.iter() {
1222 let ndc = layout_result.styled_dom.node_data.as_container();
1223 if let Some(cm) = ndc
1224 .get_extended_lifetime(*node_id)
1225 .and_then(|node| node.get_context_menu())
1226 {
1227 if self
1228 .current_window_state
1229 .mouse_state
1230 .matches(&cm.context_mouse_btn)
1231 {
1232 let domnode = DomNodeId {
1233 dom: *dom_id,
1234 node: NodeHierarchyItemId::from_crate_internal(Some(*node_id)),
1235 };
1236 context_menu = Some((cm, hit.clone(), domnode));
1237 }
1238 }
1239 }
1240 }
1241 context_menu
1242 }
1243
1244 pub fn run_single_timer(
1249 &mut self,
1250 timer_id: usize,
1251 frame_start: Instant,
1252 current_window_handle: &RawWindowHandle,
1253 gl_context: &OptionGlContextPtr,
1254 image_cache: &mut ImageCache,
1255 system_fonts: &mut FcFontCache,
1256 system_callbacks: &ExternalSystemCallbacks,
1257 ) -> CallCallbacksResult {
1258 use crate::{callbacks::CallbackInfo, task::TerminateTimer};
1259
1260 let mut ret = CallCallbacksResult {
1261 should_scroll_render: false,
1262 callbacks_update_screen: Update::DoNothing,
1263 modified_window_state: None,
1264 css_properties_changed: None,
1265 words_changed: None,
1266 images_changed: None,
1267 image_masks_changed: None,
1268 nodes_scrolled_in_callbacks: None,
1269 update_focused_node: None,
1270 timers: None,
1271 threads: None,
1272 timers_removed: None,
1273 threads_removed: None,
1274 windows_created: Vec::new(),
1275 cursor_changed: false,
1276 };
1277
1278 let mut ret_modified_window_state: WindowState = self.current_window_state.clone().into();
1279 let ret_window_state = ret_modified_window_state.clone();
1280 let mut ret_timers = FastHashMap::new();
1281 let mut ret_timers_removed = FastBTreeSet::new();
1282 let mut ret_threads = FastHashMap::new();
1283 let mut ret_threads_removed = FastBTreeSet::new();
1284 let mut ret_words_changed = BTreeMap::new();
1285 let mut ret_images_changed = BTreeMap::new();
1286 let mut ret_image_masks_changed = BTreeMap::new();
1287 let mut ret_css_properties_changed = BTreeMap::new();
1288 let mut ret_nodes_scrolled_in_callbacks = BTreeMap::new();
1289
1290 let mut should_terminate = TerminateTimer::Continue;
1291 let mut new_focus_target = None;
1292
1293 let current_scroll_states = self.get_current_scroll_states();
1294
1295 if let Some(timer) = self.timers.get_mut(&TimerId { id: timer_id }) {
1296 let mut stop_propagation = false;
1297
1298 let hit_dom_node = match timer.node_id.into_option() {
1300 Some(s) => s,
1301 None => DomNodeId {
1302 dom: DomId::ROOT_ID,
1303 node: NodeHierarchyItemId::from_crate_internal(None),
1304 },
1305 };
1306 let cursor_relative_to_item = OptionLogicalPosition::None;
1307 let cursor_in_viewport = OptionLogicalPosition::None;
1308
1309 let callback_info = CallbackInfo::new(
1310 &self.layout_results,
1311 &self.renderer_resources,
1312 &self.previous_window_state,
1313 &self.current_window_state,
1314 &mut ret_modified_window_state,
1315 gl_context,
1316 image_cache,
1317 system_fonts,
1318 &mut ret_timers,
1319 &mut ret_threads,
1320 &mut ret_timers_removed,
1321 &mut ret_threads_removed,
1322 current_window_handle,
1323 &mut ret.windows_created,
1324 system_callbacks,
1325 &mut stop_propagation,
1326 &mut new_focus_target,
1327 &mut ret_words_changed,
1328 &mut ret_images_changed,
1329 &mut ret_image_masks_changed,
1330 &mut ret_css_properties_changed,
1331 ¤t_scroll_states,
1332 &mut ret_nodes_scrolled_in_callbacks,
1333 hit_dom_node,
1334 cursor_relative_to_item,
1335 cursor_in_viewport,
1336 );
1337
1338 let tcr = timer.invoke(
1339 callback_info,
1340 frame_start.clone(),
1341 system_callbacks.get_system_time_fn,
1342 );
1343
1344 ret.callbacks_update_screen = tcr.should_update;
1345 should_terminate = tcr.should_terminate;
1346
1347 if !ret_timers.is_empty() {
1348 ret.timers = Some(ret_timers);
1349 }
1350 if !ret_threads.is_empty() {
1351 ret.threads = Some(ret_threads);
1352 }
1353 if ret_modified_window_state != ret_window_state {
1354 ret.modified_window_state = Some(ret_modified_window_state);
1355 }
1356 if !ret_threads_removed.is_empty() {
1357 ret.threads_removed = Some(ret_threads_removed);
1358 }
1359 if !ret_timers_removed.is_empty() {
1360 ret.timers_removed = Some(ret_timers_removed);
1361 }
1362 if !ret_words_changed.is_empty() {
1363 ret.words_changed = Some(ret_words_changed);
1364 }
1365 if !ret_images_changed.is_empty() {
1366 ret.images_changed = Some(ret_images_changed);
1367 }
1368 if !ret_image_masks_changed.is_empty() {
1369 ret.image_masks_changed = Some(ret_image_masks_changed);
1370 }
1371 if !ret_css_properties_changed.is_empty() {
1372 ret.css_properties_changed = Some(ret_css_properties_changed);
1373 }
1374 if !ret_nodes_scrolled_in_callbacks.is_empty() {
1375 ret.nodes_scrolled_in_callbacks = Some(ret_nodes_scrolled_in_callbacks);
1376 }
1377 }
1378
1379 if let Some(ft) = new_focus_target {
1380 if let Ok(new_focus_node) =
1381 ft.resolve(&self.layout_results, self.current_window_state.focused_node)
1382 {
1383 ret.update_focused_node = Some(new_focus_node);
1384 }
1385 }
1386
1387 if should_terminate == TerminateTimer::Terminate {
1388 ret.timers_removed
1389 .get_or_insert_with(|| BTreeSet::new())
1390 .insert(TimerId { id: timer_id });
1391 }
1392
1393 return ret;
1394 }
1395
1396 #[cfg(feature = "std")]
1397 pub fn run_all_threads(
1398 &mut self,
1399 data: &mut RefAny,
1400 current_window_handle: &RawWindowHandle,
1401 gl_context: &OptionGlContextPtr,
1402 image_cache: &mut ImageCache,
1403 system_fonts: &mut FcFontCache,
1404 system_callbacks: &ExternalSystemCallbacks,
1405 ) -> CallCallbacksResult {
1406 use crate::{
1407 callbacks::CallbackInfo,
1408 task::{
1409 OptionThreadReceiveMsg, OptionThreadSendMsg, ThreadReceiveMsg, ThreadReceiver,
1410 ThreadSendMsg, ThreadWriteBackMsg,
1411 },
1412 };
1413
1414 let mut ret = CallCallbacksResult {
1415 should_scroll_render: false,
1416 callbacks_update_screen: Update::DoNothing,
1417 modified_window_state: None,
1418 css_properties_changed: None,
1419 words_changed: None,
1420 images_changed: None,
1421 image_masks_changed: None,
1422 nodes_scrolled_in_callbacks: None,
1423 update_focused_node: None,
1424 timers: None,
1425 threads: None,
1426 timers_removed: None,
1427 threads_removed: None,
1428 windows_created: Vec::new(),
1429 cursor_changed: false,
1430 };
1431
1432 let mut ret_modified_window_state: WindowState = self.current_window_state.clone().into();
1433 let ret_window_state = ret_modified_window_state.clone();
1434 let mut ret_timers = FastHashMap::new();
1435 let mut ret_timers_removed = FastBTreeSet::new();
1436 let mut ret_threads = FastHashMap::new();
1437 let mut ret_threads_removed = FastBTreeSet::new();
1438 let mut ret_words_changed = BTreeMap::new();
1439 let mut ret_images_changed = BTreeMap::new();
1440 let mut ret_image_masks_changed = BTreeMap::new();
1441 let mut ret_css_properties_changed = BTreeMap::new();
1442 let mut ret_nodes_scrolled_in_callbacks = BTreeMap::new();
1443 let mut new_focus_target = None;
1444 let mut stop_propagation = false;
1445 let current_scroll_states = self.get_current_scroll_states();
1446
1447 for (thread_id, thread) in self.threads.iter_mut() {
1448 let hit_dom_node = DomNodeId {
1449 dom: DomId::ROOT_ID,
1450 node: NodeHierarchyItemId::from_crate_internal(None),
1451 };
1452 let cursor_relative_to_item = OptionLogicalPosition::None;
1453 let cursor_in_viewport = OptionLogicalPosition::None;
1454
1455 let thread = &mut *match thread.ptr.lock().ok() {
1456 Some(s) => s,
1457 None => {
1458 ret.threads_removed
1459 .get_or_insert_with(|| BTreeSet::default())
1460 .insert(*thread_id);
1461 continue;
1462 }
1463 };
1464
1465 let _ = thread.sender_send(ThreadSendMsg::Tick);
1466 let update = thread.receiver_try_recv();
1467 let msg = match update {
1468 OptionThreadReceiveMsg::None => continue,
1469 OptionThreadReceiveMsg::Some(s) => s,
1470 };
1471
1472 let ThreadWriteBackMsg { mut data, callback } = match msg {
1473 ThreadReceiveMsg::Update(update_screen) => {
1474 ret.callbacks_update_screen.max_self(update_screen);
1475 continue;
1476 }
1477 ThreadReceiveMsg::WriteBack(t) => t,
1478 };
1479
1480 let mut callback_info = CallbackInfo::new(
1481 &self.layout_results,
1482 &self.renderer_resources,
1483 &self.previous_window_state,
1484 &self.current_window_state,
1485 &mut ret_modified_window_state,
1486 gl_context,
1487 image_cache,
1488 system_fonts,
1489 &mut ret_timers,
1490 &mut ret_threads,
1491 &mut ret_timers_removed,
1492 &mut ret_threads_removed,
1493 current_window_handle,
1494 &mut ret.windows_created,
1495 system_callbacks,
1496 &mut stop_propagation,
1497 &mut new_focus_target,
1498 &mut ret_words_changed,
1499 &mut ret_images_changed,
1500 &mut ret_image_masks_changed,
1501 &mut ret_css_properties_changed,
1502 ¤t_scroll_states,
1503 &mut ret_nodes_scrolled_in_callbacks,
1504 hit_dom_node,
1505 cursor_relative_to_item,
1506 cursor_in_viewport,
1507 );
1508
1509 let callback_update =
1510 (callback.cb)(&mut thread.writeback_data, &mut data, &mut callback_info);
1511 ret.callbacks_update_screen.max_self(callback_update);
1512
1513 if thread.is_finished() {
1514 ret.threads_removed
1515 .get_or_insert_with(|| BTreeSet::default())
1516 .insert(*thread_id);
1517 }
1518 }
1519
1520 if !ret_timers.is_empty() {
1521 ret.timers = Some(ret_timers);
1522 }
1523 if !ret_threads.is_empty() {
1524 ret.threads = Some(ret_threads);
1525 }
1526 if ret_modified_window_state != ret_window_state {
1527 ret.modified_window_state = Some(ret_modified_window_state);
1528 }
1529 if !ret_threads_removed.is_empty() {
1530 ret.threads_removed = Some(ret_threads_removed);
1531 }
1532 if !ret_timers_removed.is_empty() {
1533 ret.timers_removed = Some(ret_timers_removed);
1534 }
1535 if !ret_words_changed.is_empty() {
1536 ret.words_changed = Some(ret_words_changed);
1537 }
1538 if !ret_images_changed.is_empty() {
1539 ret.images_changed = Some(ret_images_changed);
1540 }
1541 if !ret_image_masks_changed.is_empty() {
1542 ret.image_masks_changed = Some(ret_image_masks_changed);
1543 }
1544 if !ret_css_properties_changed.is_empty() {
1545 ret.css_properties_changed = Some(ret_css_properties_changed);
1546 }
1547 if !ret_nodes_scrolled_in_callbacks.is_empty() {
1548 ret.nodes_scrolled_in_callbacks = Some(ret_nodes_scrolled_in_callbacks);
1549 }
1550
1551 if let Some(ft) = new_focus_target {
1552 if let Ok(new_focus_node) =
1553 ft.resolve(&self.layout_results, self.current_window_state.focused_node)
1554 {
1555 ret.update_focused_node = Some(new_focus_node);
1556 }
1557 }
1558
1559 return ret;
1560 }
1561
1562 pub fn invoke_single_callback(
1565 &mut self,
1566 callback: &mut Callback,
1567 data: &mut RefAny,
1568 current_window_handle: &RawWindowHandle,
1569 gl_context: &OptionGlContextPtr,
1570 image_cache: &mut ImageCache,
1571 system_fonts: &mut FcFontCache,
1572 system_callbacks: &ExternalSystemCallbacks,
1573 ) -> CallCallbacksResult {
1574 let hit_dom_node = DomNodeId {
1575 dom: DomId::ROOT_ID,
1576 node: NodeHierarchyItemId::from_crate_internal(None),
1577 };
1578
1579 use crate::callbacks::CallbackInfo;
1580
1581 let mut ret = CallCallbacksResult {
1582 should_scroll_render: false,
1583 callbacks_update_screen: Update::DoNothing,
1584 modified_window_state: None,
1585 css_properties_changed: None,
1586 words_changed: None,
1587 images_changed: None,
1588 image_masks_changed: None,
1589 nodes_scrolled_in_callbacks: None,
1590 update_focused_node: None,
1591 timers: None,
1592 threads: None,
1593 timers_removed: None,
1594 threads_removed: None,
1595 windows_created: Vec::new(),
1596 cursor_changed: false,
1597 };
1598
1599 let mut ret_modified_window_state: WindowState = self.current_window_state.clone().into();
1600 let ret_window_state = ret_modified_window_state.clone();
1601 let mut ret_timers = FastHashMap::new();
1602 let mut ret_timers_removed = FastBTreeSet::new();
1603 let mut ret_threads = FastHashMap::new();
1604 let mut ret_threads_removed = FastBTreeSet::new();
1605 let mut ret_words_changed = BTreeMap::new();
1606 let mut ret_images_changed = BTreeMap::new();
1607 let mut ret_image_masks_changed = BTreeMap::new();
1608 let mut ret_css_properties_changed = BTreeMap::new();
1609 let mut ret_nodes_scrolled_in_callbacks = BTreeMap::new();
1610 let mut new_focus_target = None;
1611 let mut stop_propagation = false;
1612 let current_scroll_states = self.get_current_scroll_states();
1613
1614 let cursor_relative_to_item = OptionLogicalPosition::None;
1615 let cursor_in_viewport = OptionLogicalPosition::None;
1616
1617 let mut callback_info = CallbackInfo::new(
1618 &self.layout_results,
1619 &self.renderer_resources,
1620 &self.previous_window_state,
1621 &self.current_window_state,
1622 &mut ret_modified_window_state,
1623 gl_context,
1624 image_cache,
1625 system_fonts,
1626 &mut ret_timers,
1627 &mut ret_threads,
1628 &mut ret_timers_removed,
1629 &mut ret_threads_removed,
1630 current_window_handle,
1631 &mut ret.windows_created,
1632 system_callbacks,
1633 &mut stop_propagation,
1634 &mut new_focus_target,
1635 &mut ret_words_changed,
1636 &mut ret_images_changed,
1637 &mut ret_image_masks_changed,
1638 &mut ret_css_properties_changed,
1639 ¤t_scroll_states,
1640 &mut ret_nodes_scrolled_in_callbacks,
1641 hit_dom_node,
1642 cursor_relative_to_item,
1643 cursor_in_viewport,
1644 );
1645
1646 ret.callbacks_update_screen = (callback.cb)(data, &mut callback_info);
1647
1648 if !ret_timers.is_empty() {
1649 ret.timers = Some(ret_timers);
1650 }
1651 if !ret_threads.is_empty() {
1652 ret.threads = Some(ret_threads);
1653 }
1654 if ret_modified_window_state != ret_window_state {
1655 ret.modified_window_state = Some(ret_modified_window_state);
1656 }
1657 if !ret_threads_removed.is_empty() {
1658 ret.threads_removed = Some(ret_threads_removed);
1659 }
1660 if !ret_timers_removed.is_empty() {
1661 ret.timers_removed = Some(ret_timers_removed);
1662 }
1663 if !ret_words_changed.is_empty() {
1664 ret.words_changed = Some(ret_words_changed);
1665 }
1666 if !ret_images_changed.is_empty() {
1667 ret.images_changed = Some(ret_images_changed);
1668 }
1669 if !ret_image_masks_changed.is_empty() {
1670 ret.image_masks_changed = Some(ret_image_masks_changed);
1671 }
1672 if !ret_css_properties_changed.is_empty() {
1673 ret.css_properties_changed = Some(ret_css_properties_changed);
1674 }
1675 if !ret_nodes_scrolled_in_callbacks.is_empty() {
1676 ret.nodes_scrolled_in_callbacks = Some(ret_nodes_scrolled_in_callbacks);
1677 }
1678
1679 if let Some(ft) = new_focus_target {
1680 if let Ok(new_focus_node) =
1681 ft.resolve(&self.layout_results, self.current_window_state.focused_node)
1682 {
1683 ret.update_focused_node = Some(new_focus_node);
1684 }
1685 }
1686
1687 return ret;
1688 }
1689
1690 pub fn invoke_menu_callback(
1691 &mut self,
1692 menu_callback: &mut MenuCallback,
1693 hit_dom_node: DomNodeId,
1694 current_window_handle: &RawWindowHandle,
1695 gl_context: &OptionGlContextPtr,
1696 image_cache: &mut ImageCache,
1697 system_fonts: &mut FcFontCache,
1698 system_callbacks: &ExternalSystemCallbacks,
1699 ) -> CallCallbacksResult {
1700 use crate::callbacks::CallbackInfo;
1701
1702 let mut ret = CallCallbacksResult {
1703 should_scroll_render: false,
1704 callbacks_update_screen: Update::DoNothing,
1705 modified_window_state: None,
1706 css_properties_changed: None,
1707 words_changed: None,
1708 images_changed: None,
1709 image_masks_changed: None,
1710 nodes_scrolled_in_callbacks: None,
1711 update_focused_node: None,
1712 timers: None,
1713 threads: None,
1714 timers_removed: None,
1715 threads_removed: None,
1716 windows_created: Vec::new(),
1717 cursor_changed: false,
1718 };
1719
1720 let mut ret_modified_window_state: WindowState = self.current_window_state.clone().into();
1721 let ret_window_state = ret_modified_window_state.clone();
1722 let mut ret_timers = FastHashMap::new();
1723 let mut ret_timers_removed = FastBTreeSet::new();
1724 let mut ret_threads = FastHashMap::new();
1725 let mut ret_threads_removed = FastBTreeSet::new();
1726 let mut ret_words_changed = BTreeMap::new();
1727 let mut ret_images_changed = BTreeMap::new();
1728 let mut ret_image_masks_changed = BTreeMap::new();
1729 let mut ret_css_properties_changed = BTreeMap::new();
1730 let mut ret_nodes_scrolled_in_callbacks = BTreeMap::new();
1731 let mut new_focus_target = None;
1732 let mut stop_propagation = false;
1733 let current_scroll_states = self.get_current_scroll_states();
1734
1735 let cursor_relative_to_item = OptionLogicalPosition::None;
1736 let cursor_in_viewport = OptionLogicalPosition::None;
1737
1738 let mut callback_info = CallbackInfo::new(
1739 &self.layout_results,
1740 &self.renderer_resources,
1741 &self.previous_window_state,
1742 &self.current_window_state,
1743 &mut ret_modified_window_state,
1744 gl_context,
1745 image_cache,
1746 system_fonts,
1747 &mut ret_timers,
1748 &mut ret_threads,
1749 &mut ret_timers_removed,
1750 &mut ret_threads_removed,
1751 current_window_handle,
1752 &mut ret.windows_created,
1753 system_callbacks,
1754 &mut stop_propagation,
1755 &mut new_focus_target,
1756 &mut ret_words_changed,
1757 &mut ret_images_changed,
1758 &mut ret_image_masks_changed,
1759 &mut ret_css_properties_changed,
1760 ¤t_scroll_states,
1761 &mut ret_nodes_scrolled_in_callbacks,
1762 hit_dom_node,
1763 cursor_relative_to_item,
1764 cursor_in_viewport,
1765 );
1766
1767 ret.callbacks_update_screen =
1768 (menu_callback.callback.cb)(&mut menu_callback.data, &mut callback_info);
1769
1770 if !ret_timers.is_empty() {
1771 ret.timers = Some(ret_timers);
1772 }
1773 if !ret_threads.is_empty() {
1774 ret.threads = Some(ret_threads);
1775 }
1776 if ret_modified_window_state != ret_window_state {
1777 ret.modified_window_state = Some(ret_modified_window_state);
1778 }
1779 if !ret_threads_removed.is_empty() {
1780 ret.threads_removed = Some(ret_threads_removed);
1781 }
1782 if !ret_timers_removed.is_empty() {
1783 ret.timers_removed = Some(ret_timers_removed);
1784 }
1785 if !ret_words_changed.is_empty() {
1786 ret.words_changed = Some(ret_words_changed);
1787 }
1788 if !ret_images_changed.is_empty() {
1789 ret.images_changed = Some(ret_images_changed);
1790 }
1791 if !ret_image_masks_changed.is_empty() {
1792 ret.image_masks_changed = Some(ret_image_masks_changed);
1793 }
1794 if !ret_css_properties_changed.is_empty() {
1795 ret.css_properties_changed = Some(ret_css_properties_changed);
1796 }
1797 if !ret_nodes_scrolled_in_callbacks.is_empty() {
1798 ret.nodes_scrolled_in_callbacks = Some(ret_nodes_scrolled_in_callbacks);
1799 }
1800
1801 if let Some(ft) = new_focus_target {
1802 if let Ok(new_focus_node) =
1803 ft.resolve(&self.layout_results, self.current_window_state.focused_node)
1804 {
1805 ret.update_focused_node = Some(new_focus_node);
1806 }
1807 }
1808
1809 return ret;
1810 }
1811}
1812
1813#[derive(Debug, Default, Copy, Clone, PartialEq, PartialOrd, Hash, Ord, Eq)]
1814#[repr(C)]
1815pub struct TouchState {
1816 pub unimplemented: u8,
1818}
1819
1820#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Hash, Ord, Eq)]
1822#[repr(C)]
1823pub enum WindowTheme {
1824 DarkMode,
1825 LightMode,
1826}
1827
1828impl Default for WindowTheme {
1829 fn default() -> WindowTheme {
1830 WindowTheme::LightMode }
1832}
1833
1834impl_option!(
1835 WindowTheme,
1836 OptionWindowTheme,
1837 [Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq, Hash]
1838);
1839
1840#[derive(Debug, PartialEq, PartialOrd, Clone)]
1841#[repr(C)]
1842pub struct Monitor {
1843 pub id: usize,
1844 pub name: OptionAzString,
1845 pub size: LayoutSize,
1846 pub position: LayoutPoint,
1847 pub scale_factor: f64,
1848 pub video_modes: VideoModeVec,
1849 pub is_primary_monitor: bool,
1850}
1851
1852impl_vec!(Monitor, MonitorVec, MonitorVecDestructor);
1853impl_vec_debug!(Monitor, MonitorVec);
1854impl_vec_clone!(Monitor, MonitorVec, MonitorVecDestructor);
1855impl_vec_partialeq!(Monitor, MonitorVec);
1856impl_vec_partialord!(Monitor, MonitorVec);
1857
1858impl core::hash::Hash for Monitor {
1859 fn hash<H>(&self, state: &mut H)
1860 where
1861 H: core::hash::Hasher,
1862 {
1863 self.id.hash(state)
1864 }
1865}
1866
1867impl Default for Monitor {
1868 fn default() -> Self {
1869 Monitor {
1870 id: 0,
1871 name: OptionAzString::None,
1872 size: LayoutSize::zero(),
1873 position: LayoutPoint::zero(),
1874 scale_factor: 1.0,
1875 video_modes: Vec::new().into(),
1876 is_primary_monitor: false,
1877 }
1878 }
1879}
1880#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1881#[repr(C)]
1882pub struct VideoMode {
1883 pub size: LayoutSize,
1884 pub bit_depth: u16,
1885 pub refresh_rate: u16,
1886}
1887
1888impl_vec!(VideoMode, VideoModeVec, VideoModeVecDestructor);
1889impl_vec_clone!(VideoMode, VideoModeVec, VideoModeVecDestructor);
1890impl_vec_debug!(VideoMode, VideoModeVec);
1891impl_vec_partialeq!(VideoMode, VideoModeVec);
1892impl_vec_partialord!(VideoMode, VideoModeVec);
1893
1894#[derive(Debug, Clone, PartialEq)]
1895#[repr(C)]
1896pub struct WindowState {
1897 pub title: AzString,
1898 pub theme: WindowTheme,
1903 pub size: WindowSize,
1905 pub position: WindowPosition,
1907 pub flags: WindowFlags,
1909 pub debug_state: DebugState,
1912 pub keyboard_state: KeyboardState,
1915 pub mouse_state: MouseState,
1917 pub touch_state: TouchState,
1919 pub ime_position: ImePosition,
1922 pub monitor: Monitor,
1924 pub platform_specific_options: PlatformSpecificOptions,
1927 pub renderer_options: RendererOptions,
1929 pub background_color: ColorU,
1931 pub layout_callback: LayoutCallback,
1941 pub close_callback: OptionCallback,
1943}
1944
1945impl_option!(
1946 WindowState,
1947 OptionWindowState,
1948 copy = false,
1949 [Debug, Clone, PartialEq]
1950);
1951
1952#[derive(Debug, Copy, Clone, PartialEq)]
1953#[repr(C, u8)]
1954pub enum WindowPosition {
1955 Uninitialized,
1956 Initialized(PhysicalPositionI32),
1957}
1958
1959impl Default for WindowPosition {
1960 fn default() -> WindowPosition {
1961 WindowPosition::Uninitialized
1962 }
1963}
1964
1965#[derive(Debug, Copy, Clone, PartialEq)]
1966#[repr(C, u8)]
1967pub enum ImePosition {
1968 Uninitialized,
1969 Initialized(LogicalPosition),
1970}
1971
1972impl Default for ImePosition {
1973 fn default() -> ImePosition {
1974 ImePosition::Uninitialized
1975 }
1976}
1977
1978#[derive(Debug, Clone, PartialEq)]
1979pub struct FullWindowState {
1980 pub theme: WindowTheme,
1985 pub title: AzString,
1987 pub size: WindowSize,
1989 pub position: WindowPosition,
1991 pub flags: WindowFlags,
1993 pub debug_state: DebugState,
1996 pub keyboard_state: KeyboardState,
1999 pub mouse_state: MouseState,
2001 pub touch_state: TouchState,
2003 pub ime_position: ImePosition,
2006 pub platform_specific_options: PlatformSpecificOptions,
2009 pub renderer_options: RendererOptions,
2011 pub background_color: ColorU,
2013 pub layout_callback: LayoutCallback,
2023 pub close_callback: OptionCallback,
2026 pub monitor: Monitor,
2029 pub hovered_file: Option<AzString>, pub dropped_file: Option<AzString>, pub focused_node: Option<DomNodeId>,
2037 pub last_hit_test: FullHitTest,
2041}
2042
2043impl Default for FullWindowState {
2044 fn default() -> Self {
2045 Self {
2046 theme: WindowTheme::default(),
2047 title: AzString::from_const_str(DEFAULT_TITLE),
2048 size: WindowSize::default(),
2049 position: WindowPosition::Uninitialized,
2050 flags: WindowFlags::default(),
2051 debug_state: DebugState::default(),
2052 keyboard_state: KeyboardState::default(),
2053 mouse_state: MouseState::default(),
2054 touch_state: TouchState::default(),
2055 ime_position: ImePosition::Uninitialized,
2056 platform_specific_options: PlatformSpecificOptions::default(),
2057 background_color: ColorU::WHITE,
2058 layout_callback: LayoutCallback::default(),
2059 close_callback: OptionCallback::None,
2060 renderer_options: RendererOptions::default(),
2061 monitor: Monitor::default(),
2062 hovered_file: None,
2064 dropped_file: None,
2065 focused_node: None,
2066 last_hit_test: FullHitTest::empty(None),
2067 }
2068 }
2069}
2070
2071impl FullWindowState {
2072 pub fn get_mouse_state(&self) -> &MouseState {
2073 &self.mouse_state
2074 }
2075
2076 pub fn get_keyboard_state(&self) -> &KeyboardState {
2077 &self.keyboard_state
2078 }
2079
2080 pub fn get_hovered_file(&self) -> Option<&AzString> {
2081 self.hovered_file.as_ref()
2082 }
2083
2084 pub fn get_dropped_file(&self) -> Option<&AzString> {
2085 self.dropped_file.as_ref()
2086 }
2087
2088 pub fn get_scroll_amount(&self) -> Option<(f32, f32)> {
2089 self.mouse_state.get_scroll_amount()
2090 }
2091
2092 pub fn layout_callback_changed(&self, other: &Option<Self>) -> bool {
2093 match other {
2094 Some(s) => self.layout_callback != s.layout_callback,
2095 None => false,
2096 }
2097 }
2098
2099 pub fn from_window_state(
2105 window_state: &WindowState,
2106 dropped_file: Option<AzString>,
2107 hovered_file: Option<AzString>,
2108 focused_node: Option<DomNodeId>,
2109 last_hit_test: FullHitTest,
2110 ) -> Self {
2111 Self {
2112 monitor: window_state.monitor.clone(),
2113 theme: window_state.theme,
2114 title: window_state.title.clone(),
2115 size: window_state.size,
2116 position: window_state.position.into(),
2117 flags: window_state.flags,
2118 debug_state: window_state.debug_state,
2119 keyboard_state: window_state.keyboard_state.clone(),
2120 mouse_state: window_state.mouse_state,
2121 touch_state: window_state.touch_state,
2122 ime_position: window_state.ime_position.into(),
2123 platform_specific_options: window_state.platform_specific_options.clone(),
2124 background_color: window_state.background_color,
2125 layout_callback: window_state.layout_callback.clone(),
2126 close_callback: window_state.close_callback,
2127 renderer_options: window_state.renderer_options,
2128 dropped_file,
2129 hovered_file,
2130 focused_node,
2131 last_hit_test,
2132 }
2133 }
2134
2135 pub fn process_system_scroll(&mut self, scroll_states: &ScrollStates) -> Option<ScrollResult> {
2136 let (x, y) = self.mouse_state.get_scroll_amount()?;
2137 Some(ScrollResult {})
2139 }
2140}
2141
2142impl From<FullWindowState> for WindowState {
2143 fn from(full_window_state: FullWindowState) -> WindowState {
2144 WindowState {
2145 monitor: full_window_state.monitor.clone(),
2146 theme: full_window_state.theme,
2147 title: full_window_state.title.into(),
2148 size: full_window_state.size,
2149 position: full_window_state.position.into(),
2150 flags: full_window_state.flags,
2151 debug_state: full_window_state.debug_state,
2152 keyboard_state: full_window_state.keyboard_state,
2153 mouse_state: full_window_state.mouse_state,
2154 touch_state: full_window_state.touch_state,
2155 ime_position: full_window_state.ime_position.into(),
2156 platform_specific_options: full_window_state.platform_specific_options,
2157 background_color: full_window_state.background_color,
2158 layout_callback: full_window_state.layout_callback,
2159 close_callback: full_window_state.close_callback,
2160 renderer_options: full_window_state.renderer_options,
2161 }
2162 }
2163}
2164
2165#[derive(Debug)]
2166pub struct CallCallbacksResult {
2167 pub should_scroll_render: bool,
2170 pub callbacks_update_screen: Update,
2172 pub modified_window_state: Option<WindowState>,
2174 pub words_changed: Option<BTreeMap<DomId, BTreeMap<NodeId, AzString>>>,
2178 pub images_changed: Option<BTreeMap<DomId, BTreeMap<NodeId, (ImageRef, UpdateImageType)>>>,
2181 pub image_masks_changed: Option<BTreeMap<DomId, BTreeMap<NodeId, ImageMask>>>,
2184 pub css_properties_changed: Option<BTreeMap<DomId, BTreeMap<NodeId, Vec<CssProperty>>>>,
2187 pub nodes_scrolled_in_callbacks:
2189 Option<BTreeMap<DomId, BTreeMap<NodeHierarchyItemId, LogicalPosition>>>,
2190 pub update_focused_node: Option<Option<DomNodeId>>,
2192 pub timers: Option<FastHashMap<TimerId, Timer>>,
2194 pub threads: Option<FastHashMap<ThreadId, Thread>>,
2196 pub timers_removed: Option<FastBTreeSet<TimerId>>,
2198 pub threads_removed: Option<FastBTreeSet<ThreadId>>,
2200 pub windows_created: Vec<WindowCreateOptions>,
2202 pub cursor_changed: bool,
2204}
2205
2206impl CallCallbacksResult {
2207 pub fn cursor_changed(&self) -> bool {
2208 self.cursor_changed
2209 }
2210 pub fn focus_changed(&self) -> bool {
2211 self.update_focused_node.is_some()
2212 }
2213}
2214
2215#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
2216#[repr(C)]
2217pub struct WindowFlags {
2218 pub frame: WindowFrame,
2220 pub is_about_to_close: bool,
2222 pub has_decorations: bool,
2224 pub is_visible: bool,
2226 pub is_always_on_top: bool,
2228 pub is_resizable: bool,
2230 pub has_focus: bool,
2232 pub has_extended_window_frame: bool,
2235 pub has_blur_behind_window: bool,
2237 pub smooth_scroll_enabled: bool,
2239 pub autotab_enabled: bool,
2241}
2242
2243#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
2244#[repr(C)]
2245pub enum WindowFrame {
2246 Normal,
2247 Minimized,
2248 Maximized,
2249 Fullscreen,
2250}
2251
2252impl Default for WindowFlags {
2253 fn default() -> Self {
2254 Self {
2255 frame: WindowFrame::Normal,
2256 is_about_to_close: false,
2257 has_decorations: true,
2258 is_visible: true,
2259 is_always_on_top: false,
2260 is_resizable: true,
2261 has_focus: true,
2262 has_extended_window_frame: false,
2263 has_blur_behind_window: false,
2264 smooth_scroll_enabled: true,
2265 autotab_enabled: true,
2266 }
2267 }
2268}
2269
2270#[derive(Debug, Default, Clone, PartialEq, PartialOrd)]
2271#[repr(C)]
2272pub struct PlatformSpecificOptions {
2273 pub windows_options: WindowsWindowOptions,
2274 pub linux_options: LinuxWindowOptions,
2275 pub mac_options: MacWindowOptions,
2276 pub wasm_options: WasmWindowOptions,
2277}
2278
2279unsafe impl Sync for PlatformSpecificOptions {}
2280unsafe impl Send for PlatformSpecificOptions {}
2281
2282#[derive(Debug, Clone, PartialEq, PartialOrd)]
2283#[repr(C)]
2284pub struct WindowsWindowOptions {
2285 pub allow_drag_and_drop: bool,
2287 pub no_redirection_bitmap: bool,
2289 pub window_icon: OptionWindowIcon,
2291 pub taskbar_icon: OptionTaskBarIcon,
2295 pub parent_window: OptionHwndHandle,
2297}
2298
2299impl Default for WindowsWindowOptions {
2300 fn default() -> WindowsWindowOptions {
2301 WindowsWindowOptions {
2302 allow_drag_and_drop: true,
2303 no_redirection_bitmap: false,
2304 window_icon: OptionWindowIcon::None,
2305 taskbar_icon: OptionTaskBarIcon::None,
2306 parent_window: OptionHwndHandle::None,
2307 }
2308 }
2309}
2310
2311type HwndHandle = *mut c_void;
2313
2314impl_option!(
2315 HwndHandle,
2316 OptionHwndHandle,
2317 copy = false,
2318 [Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
2319);
2320
2321#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2324#[repr(C)]
2325pub enum XWindowType {
2326 Desktop,
2330 Dock,
2333 Toolbar,
2335 Menu,
2337 Utility,
2339 Splash,
2341 Dialog,
2343 DropdownMenu,
2346 PopupMenu,
2349 Tooltip,
2352 Notification,
2355 Combo,
2358 Dnd,
2361 Normal,
2363}
2364
2365impl Default for XWindowType {
2366 fn default() -> Self {
2367 XWindowType::Normal
2368 }
2369}
2370
2371#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq, Hash)]
2372#[repr(C)]
2373pub enum UserAttentionType {
2374 None,
2375 Critical,
2376 Informational,
2377}
2378
2379impl Default for UserAttentionType {
2380 fn default() -> UserAttentionType {
2381 UserAttentionType::None
2382 }
2383}
2384
2385#[derive(Debug, Default, Clone, PartialEq, PartialOrd)]
2386#[repr(C)]
2387pub struct LinuxWindowOptions {
2388 pub x11_visual: OptionX11Visual,
2390 pub x11_screen: OptionI32,
2392 pub x11_wm_classes: StringPairVec,
2395 pub x11_override_redirect: bool,
2398 pub x11_window_types: XWindowTypeVec,
2401 pub x11_gtk_theme_variant: OptionAzString,
2404 pub x11_resize_increments: OptionLogicalSize,
2407 pub x11_base_size: OptionLogicalSize,
2410 pub wayland_app_id: OptionAzString,
2417 pub wayland_theme: OptionWaylandTheme,
2418 pub request_user_attention: UserAttentionType,
2419 pub window_icon: OptionWindowIcon,
2420}
2421
2422type X11Visual = *const c_void;
2423impl_option!(
2424 X11Visual,
2425 OptionX11Visual,
2426 [Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]
2427);
2428
2429#[derive(Debug, Default, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
2430#[repr(C)]
2431pub struct AzStringPair {
2432 pub key: AzString,
2433 pub value: AzString,
2434}
2435
2436impl_vec!(AzStringPair, StringPairVec, StringPairVecDestructor);
2437impl_vec_mut!(AzStringPair, StringPairVec);
2438impl_vec_debug!(AzStringPair, StringPairVec);
2439impl_vec_partialord!(AzStringPair, StringPairVec);
2440impl_vec_ord!(AzStringPair, StringPairVec);
2441impl_vec_clone!(AzStringPair, StringPairVec, StringPairVecDestructor);
2442impl_vec_partialeq!(AzStringPair, StringPairVec);
2443impl_vec_eq!(AzStringPair, StringPairVec);
2444impl_vec_hash!(AzStringPair, StringPairVec);
2445
2446impl_option!(
2447 StringPairVec,
2448 OptionStringPairVec,
2449 copy = false,
2450 [Debug, Clone, PartialOrd, PartialEq, Ord, Eq, Hash]
2451);
2452
2453impl StringPairVec {
2454 pub fn get_key(&self, search_key: &str) -> Option<&AzString> {
2455 self.as_ref().iter().find_map(|v| {
2456 if v.key.as_str() == search_key {
2457 Some(&v.value)
2458 } else {
2459 None
2460 }
2461 })
2462 }
2463 pub fn get_key_mut(&mut self, search_key: &str) -> Option<&mut AzStringPair> {
2464 self.as_mut()
2465 .iter_mut()
2466 .find(|v| v.key.as_str() == search_key)
2467 }
2468 pub fn insert_kv<I: Into<AzString>>(&mut self, key: I, value: I) {
2469 let key = key.into();
2470 let value = value.into();
2471 match self.get_key_mut(key.as_str()) {
2472 None => {}
2473 Some(s) => {
2474 s.value = value;
2475 return;
2476 }
2477 }
2478 self.push(AzStringPair { key, value });
2479 }
2480}
2481
2482impl_vec!(XWindowType, XWindowTypeVec, XWindowTypeVecDestructor);
2483impl_vec_debug!(XWindowType, XWindowTypeVec);
2484impl_vec_partialord!(XWindowType, XWindowTypeVec);
2485impl_vec_ord!(XWindowType, XWindowTypeVec);
2486impl_vec_clone!(XWindowType, XWindowTypeVec, XWindowTypeVecDestructor);
2487impl_vec_partialeq!(XWindowType, XWindowTypeVec);
2488impl_vec_eq!(XWindowType, XWindowTypeVec);
2489impl_vec_hash!(XWindowType, XWindowTypeVec);
2490
2491impl_option!(
2492 WaylandTheme,
2493 OptionWaylandTheme,
2494 copy = false,
2495 [Debug, Clone, PartialEq, PartialOrd]
2496);
2497
2498#[derive(Debug, Default, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
2499#[repr(C)]
2500pub struct MacWindowOptions {
2501 pub reserved: u8,
2502}
2503
2504#[derive(Debug, Default, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
2505#[repr(C)]
2506pub struct WasmWindowOptions {
2507 pub _reserved: u8,
2509}
2510
2511impl WindowState {
2512 pub fn new(callback: LayoutCallbackType) -> Self {
2514 use crate::callbacks::LayoutCallbackInner;
2515 Self {
2516 layout_callback: LayoutCallback::Raw(LayoutCallbackInner { cb: callback }),
2517 ..Default::default()
2518 }
2519 }
2520
2521 pub fn get_mouse_state(&self) -> &MouseState {
2524 &self.mouse_state
2525 }
2526
2527 pub fn get_keyboard_state(&self) -> &KeyboardState {
2530 &self.keyboard_state
2531 }
2532
2533 pub fn get_physical_size(&self) -> (usize, usize) {
2535 (
2536 self.size.dimensions.width as usize,
2537 self.size.dimensions.height as usize,
2538 )
2539 }
2540
2541 pub fn get_hidpi_factor(&self) -> f32 {
2543 self.size.get_hidpi_factor()
2544 }
2545}
2546
2547#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
2548#[repr(C)]
2549pub enum FullScreenMode {
2550 SlowFullScreen,
2553 FastFullScreen,
2556 SlowWindowed,
2559 FastWindowed,
2562}
2563
2564#[derive(Debug, Clone, PartialEq, PartialOrd)]
2565#[repr(C)]
2566pub struct WaylandTheme {
2569 pub title_bar_active_background_color: [u8; 4],
2570 pub title_bar_active_separator_color: [u8; 4],
2571 pub title_bar_active_text_color: [u8; 4],
2572 pub title_bar_inactive_background_color: [u8; 4],
2573 pub title_bar_inactive_separator_color: [u8; 4],
2574 pub title_bar_inactive_text_color: [u8; 4],
2575 pub maximize_idle_foreground_inactive_color: [u8; 4],
2576 pub minimize_idle_foreground_inactive_color: [u8; 4],
2577 pub close_idle_foreground_inactive_color: [u8; 4],
2578 pub maximize_hovered_foreground_inactive_color: [u8; 4],
2579 pub minimize_hovered_foreground_inactive_color: [u8; 4],
2580 pub close_hovered_foreground_inactive_color: [u8; 4],
2581 pub maximize_disabled_foreground_inactive_color: [u8; 4],
2582 pub minimize_disabled_foreground_inactive_color: [u8; 4],
2583 pub close_disabled_foreground_inactive_color: [u8; 4],
2584 pub maximize_idle_background_inactive_color: [u8; 4],
2585 pub minimize_idle_background_inactive_color: [u8; 4],
2586 pub close_idle_background_inactive_color: [u8; 4],
2587 pub maximize_hovered_background_inactive_color: [u8; 4],
2588 pub minimize_hovered_background_inactive_color: [u8; 4],
2589 pub close_hovered_background_inactive_color: [u8; 4],
2590 pub maximize_disabled_background_inactive_color: [u8; 4],
2591 pub minimize_disabled_background_inactive_color: [u8; 4],
2592 pub close_disabled_background_inactive_color: [u8; 4],
2593 pub maximize_idle_foreground_active_color: [u8; 4],
2594 pub minimize_idle_foreground_active_color: [u8; 4],
2595 pub close_idle_foreground_active_color: [u8; 4],
2596 pub maximize_hovered_foreground_active_color: [u8; 4],
2597 pub minimize_hovered_foreground_active_color: [u8; 4],
2598 pub close_hovered_foreground_active_color: [u8; 4],
2599 pub maximize_disabled_foreground_active_color: [u8; 4],
2600 pub minimize_disabled_foreground_active_color: [u8; 4],
2601 pub close_disabled_foreground_active_color: [u8; 4],
2602 pub maximize_idle_background_active_color: [u8; 4],
2603 pub minimize_idle_background_active_color: [u8; 4],
2604 pub close_idle_background_active_color: [u8; 4],
2605 pub maximize_hovered_background_active_color: [u8; 4],
2606 pub minimize_hovered_background_active_color: [u8; 4],
2607 pub close_hovered_background_active_color: [u8; 4],
2608 pub maximize_disabled_background_active_color: [u8; 4],
2609 pub minimize_disabled_background_active_color: [u8; 4],
2610 pub close_disabled_background_active_color: [u8; 4],
2611 pub title_bar_font: AzString,
2612 pub title_bar_font_size: f32,
2613}
2614
2615#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
2616#[repr(C)]
2617pub struct WindowSize {
2618 pub dimensions: LogicalSize,
2621 pub dpi: u32,
2623 pub min_dimensions: OptionLogicalSize,
2625 pub max_dimensions: OptionLogicalSize,
2627}
2628
2629impl WindowSize {
2630 pub fn get_layout_size(&self) -> LayoutSize {
2631 LayoutSize::new(
2632 libm::roundf(self.dimensions.width) as isize,
2633 libm::roundf(self.dimensions.height) as isize,
2634 )
2635 }
2636
2637 pub fn get_logical_size(&self) -> LogicalSize {
2639 self.dimensions
2640 }
2641
2642 pub fn get_physical_size(&self) -> PhysicalSize<u32> {
2643 self.dimensions.to_physical(self.get_hidpi_factor())
2644 }
2645
2646 pub fn get_hidpi_factor(&self) -> f32 {
2647 self.dpi as f32 / 96.0
2648 }
2649}
2650
2651impl Default for WindowSize {
2652 fn default() -> Self {
2653 Self {
2654 #[cfg(not(feature = "glow"))]
2655 dimensions: LogicalSize::new(640.0, 480.0),
2656 dpi: 96,
2657 min_dimensions: None.into(),
2658 max_dimensions: None.into(),
2659 }
2660 }
2661}
2662
2663impl Default for WindowState {
2664 fn default() -> Self {
2665 FullWindowState::default().into()
2666 }
2667}
2668
2669#[derive(Debug, Clone)]
2670#[repr(C)]
2671pub struct WindowCreateOptions {
2672 pub state: WindowState,
2674 pub size_to_content: bool,
2678 pub renderer: OptionRendererOptions,
2680 pub theme: OptionWindowTheme,
2682 pub create_callback: OptionCallback,
2684 pub hot_reload: bool,
2687}
2688
2689impl Default for WindowCreateOptions {
2690 fn default() -> Self {
2691 Self {
2692 state: WindowState::default(),
2693 size_to_content: false,
2694 renderer: OptionRendererOptions::None,
2695 theme: OptionWindowTheme::None,
2696 create_callback: OptionCallback::None,
2697 hot_reload: false,
2698 }
2699 }
2700}
2701
2702impl WindowCreateOptions {
2703 pub fn new(callback: LayoutCallbackType) -> Self {
2704 Self {
2705 state: WindowState::new(callback),
2706 ..WindowCreateOptions::default()
2707 }
2708 }
2709 pub fn renderer_types(&self) -> Vec<RendererType> {
2710 match self.renderer.into_option() {
2711 Some(s) => match s.hw_accel {
2712 HwAcceleration::DontCare => vec![RendererType::Hardware, RendererType::Software],
2713 HwAcceleration::Enabled => vec![RendererType::Hardware],
2714 HwAcceleration::Disabled => vec![RendererType::Software],
2715 },
2716 None => vec![RendererType::Hardware, RendererType::Software],
2717 }
2718 }
2719}
2720
2721#[repr(C)]
2722#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq, Hash)]
2723pub enum RendererType {
2724 Hardware,
2726 Software,
2728}
2729
2730impl_option!(
2731 RendererType,
2732 OptionRendererType,
2733 [Debug, Copy, Clone, PartialEq, PartialOrd, Ord, Eq, Hash]
2734);
2735
2736#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq, Hash)]
2737pub enum UpdateFocusWarning {
2738 FocusInvalidDomId(DomId),
2739 FocusInvalidNodeId(NodeHierarchyItemId),
2740 CouldNotFindFocusNode(CssPath),
2741}
2742
2743impl ::core::fmt::Display for UpdateFocusWarning {
2744 fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
2745 use self::UpdateFocusWarning::*;
2746 match self {
2747 FocusInvalidDomId(dom_id) => write!(f, "Focusing on DOM with invalid ID: {:?}", dom_id),
2748 FocusInvalidNodeId(node_id) => {
2749 write!(f, "Focusing on node with invalid ID: {}", node_id)
2750 }
2751 CouldNotFindFocusNode(css_path) => {
2752 write!(f, "Could not find focus node for path: {}", css_path)
2753 }
2754 }
2755 }
2756}
2757
2758#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
2759#[repr(C)]
2760pub struct LogicalRect {
2761 pub origin: LogicalPosition,
2762 pub size: LogicalSize,
2763}
2764
2765impl core::fmt::Debug for LogicalRect {
2766 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
2767 write!(f, "{} @ {}", self.size, self.origin)
2768 }
2769}
2770
2771impl core::fmt::Display for LogicalRect {
2772 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
2773 write!(f, "{} @ {}", self.size, self.origin)
2774 }
2775}
2776
2777impl LogicalRect {
2778 pub const fn zero() -> Self {
2779 Self::new(LogicalPosition::zero(), LogicalSize::zero())
2780 }
2781 pub const fn new(origin: LogicalPosition, size: LogicalSize) -> Self {
2782 Self { origin, size }
2783 }
2784
2785 #[inline]
2786 pub fn scale_for_dpi(&mut self, scale_factor: f32) {
2787 self.origin.x *= scale_factor;
2788 self.origin.y *= scale_factor;
2789 self.size.width *= scale_factor;
2790 self.size.height *= scale_factor;
2791 }
2792
2793 #[inline(always)]
2794 pub fn max_x(&self) -> f32 {
2795 self.origin.x + self.size.width
2796 }
2797 #[inline(always)]
2798 pub fn min_x(&self) -> f32 {
2799 self.origin.x
2800 }
2801 #[inline(always)]
2802 pub fn max_y(&self) -> f32 {
2803 self.origin.y + self.size.height
2804 }
2805 #[inline(always)]
2806 pub fn min_y(&self) -> f32 {
2807 self.origin.y
2808 }
2809
2810 #[inline]
2812 pub fn union<I: Iterator<Item = Self>>(mut rects: I) -> Option<Self> {
2813 let first = rects.next()?;
2814
2815 let mut max_width = first.size.width;
2816 let mut max_height = first.size.height;
2817 let mut min_x = first.origin.x;
2818 let mut min_y = first.origin.y;
2819
2820 while let Some(Self {
2821 origin: LogicalPosition { x, y },
2822 size: LogicalSize { width, height },
2823 }) = rects.next()
2824 {
2825 let cur_lower_right_x = x + width;
2826 let cur_lower_right_y = y + height;
2827 max_width = max_width.max(cur_lower_right_x - min_x);
2828 max_height = max_height.max(cur_lower_right_y - min_y);
2829 min_x = min_x.min(x);
2830 min_y = min_y.min(y);
2831 }
2832
2833 Some(Self {
2834 origin: LogicalPosition { x: min_x, y: min_y },
2835 size: LogicalSize {
2836 width: max_width,
2837 height: max_height,
2838 },
2839 })
2840 }
2841
2842 #[inline]
2846 pub fn hit_test(&self, other: &LogicalPosition) -> Option<LogicalPosition> {
2847 let dx_left_edge = other.x - self.min_x();
2848 let dx_right_edge = self.max_x() - other.x;
2849 let dy_top_edge = other.y - self.min_y();
2850 let dy_bottom_edge = self.max_y() - other.y;
2851 if dx_left_edge > 0.0 && dx_right_edge > 0.0 && dy_top_edge > 0.0 && dy_bottom_edge > 0.0 {
2852 Some(LogicalPosition::new(dx_left_edge, dy_top_edge))
2853 } else {
2854 None
2855 }
2856 }
2857
2858 pub fn to_layout_rect(&self) -> LayoutRect {
2859 LayoutRect {
2860 origin: LayoutPoint::new(
2861 libm::roundf(self.origin.x) as isize,
2862 libm::roundf(self.origin.y) as isize,
2863 ),
2864 size: LayoutSize::new(
2865 libm::roundf(self.size.width) as isize,
2866 libm::roundf(self.size.height) as isize,
2867 ),
2868 }
2869 }
2870}
2871
2872impl_vec!(LogicalRect, LogicalRectVec, LogicalRectVecDestructor);
2873impl_vec_clone!(LogicalRect, LogicalRectVec, LogicalRectVecDestructor);
2874impl_vec_debug!(LogicalRect, LogicalRectVec);
2875impl_vec_partialeq!(LogicalRect, LogicalRectVec);
2876impl_vec_partialord!(LogicalRect, LogicalRectVec);
2877impl_vec_ord!(LogicalRect, LogicalRectVec);
2878impl_vec_hash!(LogicalRect, LogicalRectVec);
2879impl_vec_eq!(LogicalRect, LogicalRectVec);
2880
2881use core::ops::{AddAssign, SubAssign};
2882
2883#[derive(Default, Copy, Clone, PartialEq, PartialOrd)]
2884#[repr(C)]
2885pub struct LogicalPosition {
2886 pub x: f32,
2887 pub y: f32,
2888}
2889
2890impl LogicalPosition {
2891 pub fn scale_for_dpi(&mut self, scale_factor: f32) {
2892 self.x *= scale_factor;
2893 self.y *= scale_factor;
2894 }
2895}
2896
2897impl SubAssign<LogicalPosition> for LogicalPosition {
2898 fn sub_assign(&mut self, other: LogicalPosition) {
2899 self.x -= other.x;
2900 self.y -= other.y;
2901 }
2902}
2903
2904impl AddAssign<LogicalPosition> for LogicalPosition {
2905 fn add_assign(&mut self, other: LogicalPosition) {
2906 self.x += other.x;
2907 self.y += other.y;
2908 }
2909}
2910
2911impl core::fmt::Debug for LogicalPosition {
2912 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
2913 write!(f, "({}, {})", self.x, self.y)
2914 }
2915}
2916
2917impl core::fmt::Display for LogicalPosition {
2918 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
2919 write!(f, "({}, {})", self.x, self.y)
2920 }
2921}
2922
2923impl ops::Add for LogicalPosition {
2924 type Output = Self;
2925
2926 #[inline]
2927 fn add(self, other: Self) -> Self {
2928 Self {
2929 x: self.x + other.x,
2930 y: self.y + other.y,
2931 }
2932 }
2933}
2934
2935impl ops::Sub for LogicalPosition {
2936 type Output = Self;
2937
2938 #[inline]
2939 fn sub(self, other: Self) -> Self {
2940 Self {
2941 x: self.x - other.x,
2942 y: self.y - other.y,
2943 }
2944 }
2945}
2946
2947const DECIMAL_MULTIPLIER: f32 = 1000.0;
2948
2949impl_option!(
2950 LogicalPosition,
2951 OptionLogicalPosition,
2952 [Debug, Copy, Clone, PartialEq, PartialOrd]
2953);
2954
2955impl Ord for LogicalPosition {
2956 fn cmp(&self, other: &LogicalPosition) -> Ordering {
2957 let self_x = (self.x * DECIMAL_MULTIPLIER) as usize;
2958 let self_y = (self.y * DECIMAL_MULTIPLIER) as usize;
2959 let other_x = (other.x * DECIMAL_MULTIPLIER) as usize;
2960 let other_y = (other.y * DECIMAL_MULTIPLIER) as usize;
2961 self_x.cmp(&other_x).then(self_y.cmp(&other_y))
2962 }
2963}
2964
2965impl Eq for LogicalPosition {}
2966
2967impl Hash for LogicalPosition {
2968 fn hash<H>(&self, state: &mut H)
2969 where
2970 H: Hasher,
2971 {
2972 let self_x = (self.x * DECIMAL_MULTIPLIER) as usize;
2973 let self_y = (self.y * DECIMAL_MULTIPLIER) as usize;
2974 self_x.hash(state);
2975 self_y.hash(state);
2976 }
2977}
2978
2979#[derive(Default, Copy, Clone, PartialEq, PartialOrd)]
2980#[repr(C)]
2981pub struct LogicalSize {
2982 pub width: f32,
2983 pub height: f32,
2984}
2985
2986impl LogicalSize {
2987 pub fn scale_for_dpi(&mut self, scale_factor: f32) -> Self {
2988 self.width *= scale_factor;
2989 self.height *= scale_factor;
2990 *self
2991 }
2992}
2993
2994impl core::fmt::Debug for LogicalSize {
2995 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
2996 write!(f, "{}x{}", self.width, self.height)
2997 }
2998}
2999
3000impl core::fmt::Display for LogicalSize {
3001 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
3002 write!(f, "{}x{}", self.width, self.height)
3003 }
3004}
3005
3006impl_option!(
3007 LogicalSize,
3008 OptionLogicalSize,
3009 [Debug, Copy, Clone, PartialEq, PartialOrd]
3010);
3011
3012impl Ord for LogicalSize {
3013 fn cmp(&self, other: &LogicalSize) -> Ordering {
3014 let self_width = (self.width * DECIMAL_MULTIPLIER) as usize;
3015 let self_height = (self.height * DECIMAL_MULTIPLIER) as usize;
3016 let other_width = (other.width * DECIMAL_MULTIPLIER) as usize;
3017 let other_height = (other.height * DECIMAL_MULTIPLIER) as usize;
3018 self_width
3019 .cmp(&other_width)
3020 .then(self_height.cmp(&other_height))
3021 }
3022}
3023
3024impl Eq for LogicalSize {}
3025
3026impl Hash for LogicalSize {
3027 fn hash<H>(&self, state: &mut H)
3028 where
3029 H: Hasher,
3030 {
3031 let self_width = (self.width * DECIMAL_MULTIPLIER) as usize;
3032 let self_height = (self.height * DECIMAL_MULTIPLIER) as usize;
3033 self_width.hash(state);
3034 self_height.hash(state);
3035 }
3036}
3037
3038#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
3039#[repr(C)]
3040pub struct PhysicalPosition<T> {
3041 pub x: T,
3042 pub y: T,
3043}
3044
3045impl<T: ::core::fmt::Display> ::core::fmt::Debug for PhysicalPosition<T> {
3046 fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
3047 write!(f, "({}, {})", self.x, self.y)
3048 }
3049}
3050
3051pub type PhysicalPositionI32 = PhysicalPosition<i32>;
3052impl_option!(
3053 PhysicalPositionI32,
3054 OptionPhysicalPositionI32,
3055 [Debug, Copy, Clone, PartialEq, PartialOrd]
3056);
3057
3058#[derive(Ord, Hash, Eq, Copy, Clone, PartialEq, PartialOrd)]
3059#[repr(C)]
3060pub struct PhysicalSize<T> {
3061 pub width: T,
3062 pub height: T,
3063}
3064
3065impl<T: ::core::fmt::Display> ::core::fmt::Debug for PhysicalSize<T> {
3066 fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
3067 write!(f, "{}x{}", self.width, self.height)
3068 }
3069}
3070
3071pub type PhysicalSizeU32 = PhysicalSize<u32>;
3072impl_option!(
3073 PhysicalSizeU32,
3074 OptionPhysicalSizeU32,
3075 [Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash]
3076);
3077pub type PhysicalSizeF32 = PhysicalSize<f32>;
3078impl_option!(
3079 PhysicalSizeF32,
3080 OptionPhysicalSizeF32,
3081 [Debug, Copy, Clone, PartialEq, PartialOrd]
3082);
3083
3084impl LogicalPosition {
3085 #[inline(always)]
3086 pub const fn new(x: f32, y: f32) -> Self {
3087 Self { x, y }
3088 }
3089 #[inline(always)]
3090 pub const fn zero() -> Self {
3091 Self::new(0.0, 0.0)
3092 }
3093 #[inline(always)]
3094 pub fn to_physical(self, hidpi_factor: f32) -> PhysicalPosition<u32> {
3095 PhysicalPosition {
3096 x: (self.x * hidpi_factor) as u32,
3097 y: (self.y * hidpi_factor) as u32,
3098 }
3099 }
3100}
3101
3102impl<T> PhysicalPosition<T> {
3103 #[inline(always)]
3104 pub const fn new(x: T, y: T) -> Self {
3105 Self { x, y }
3106 }
3107}
3108
3109impl PhysicalPosition<i32> {
3110 #[inline(always)]
3111 pub const fn zero() -> Self {
3112 Self::new(0, 0)
3113 }
3114 #[inline(always)]
3115 pub fn to_logical(self, hidpi_factor: f32) -> LogicalPosition {
3116 LogicalPosition {
3117 x: self.x as f32 / hidpi_factor,
3118 y: self.y as f32 / hidpi_factor,
3119 }
3120 }
3121}
3122
3123impl PhysicalPosition<f64> {
3124 #[inline(always)]
3125 pub const fn zero() -> Self {
3126 Self::new(0.0, 0.0)
3127 }
3128 #[inline(always)]
3129 pub fn to_logical(self, hidpi_factor: f32) -> LogicalPosition {
3130 LogicalPosition {
3131 x: self.x as f32 / hidpi_factor,
3132 y: self.y as f32 / hidpi_factor,
3133 }
3134 }
3135}
3136
3137impl LogicalSize {
3138 #[inline(always)]
3139 pub const fn new(width: f32, height: f32) -> Self {
3140 Self { width, height }
3141 }
3142 #[inline(always)]
3143 pub const fn zero() -> Self {
3144 Self::new(0.0, 0.0)
3145 }
3146 #[inline(always)]
3147 pub fn to_physical(self, hidpi_factor: f32) -> PhysicalSize<u32> {
3148 PhysicalSize {
3149 width: (self.width * hidpi_factor) as u32,
3150 height: (self.height * hidpi_factor) as u32,
3151 }
3152 }
3153}
3154
3155impl<T> PhysicalSize<T> {
3156 #[inline(always)]
3157 pub const fn new(width: T, height: T) -> Self {
3158 Self { width, height }
3159 }
3160}
3161
3162impl PhysicalSize<u32> {
3163 #[inline(always)]
3164 pub const fn zero() -> Self {
3165 Self::new(0, 0)
3166 }
3167 #[inline(always)]
3168 pub fn to_logical(self, hidpi_factor: f32) -> LogicalSize {
3169 LogicalSize {
3170 width: self.width as f32 / hidpi_factor,
3171 height: self.height as f32 / hidpi_factor,
3172 }
3173 }
3174}
3175
3176#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
3178#[repr(C, u8)]
3179pub enum AcceleratorKey {
3180 Ctrl,
3181 Alt,
3182 Shift,
3183 Key(VirtualKeyCode),
3184}
3185
3186impl AcceleratorKey {
3187 pub fn matches(&self, keyboard_state: &KeyboardState) -> bool {
3191 use self::AcceleratorKey::*;
3192 match self {
3193 Ctrl => keyboard_state.ctrl_down(),
3194 Alt => keyboard_state.alt_down(),
3195 Shift => keyboard_state.shift_down(),
3196 Key(k) => keyboard_state.is_key_down(*k),
3197 }
3198 }
3199}
3200
3201#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
3203#[repr(C)]
3204pub enum VirtualKeyCode {
3205 Key1,
3206 Key2,
3207 Key3,
3208 Key4,
3209 Key5,
3210 Key6,
3211 Key7,
3212 Key8,
3213 Key9,
3214 Key0,
3215 A,
3216 B,
3217 C,
3218 D,
3219 E,
3220 F,
3221 G,
3222 H,
3223 I,
3224 J,
3225 K,
3226 L,
3227 M,
3228 N,
3229 O,
3230 P,
3231 Q,
3232 R,
3233 S,
3234 T,
3235 U,
3236 V,
3237 W,
3238 X,
3239 Y,
3240 Z,
3241 Escape,
3242 F1,
3243 F2,
3244 F3,
3245 F4,
3246 F5,
3247 F6,
3248 F7,
3249 F8,
3250 F9,
3251 F10,
3252 F11,
3253 F12,
3254 F13,
3255 F14,
3256 F15,
3257 F16,
3258 F17,
3259 F18,
3260 F19,
3261 F20,
3262 F21,
3263 F22,
3264 F23,
3265 F24,
3266 Snapshot,
3267 Scroll,
3268 Pause,
3269 Insert,
3270 Home,
3271 Delete,
3272 End,
3273 PageDown,
3274 PageUp,
3275 Left,
3276 Up,
3277 Right,
3278 Down,
3279 Back,
3280 Return,
3281 Space,
3282 Compose,
3283 Caret,
3284 Numlock,
3285 Numpad0,
3286 Numpad1,
3287 Numpad2,
3288 Numpad3,
3289 Numpad4,
3290 Numpad5,
3291 Numpad6,
3292 Numpad7,
3293 Numpad8,
3294 Numpad9,
3295 NumpadAdd,
3296 NumpadDivide,
3297 NumpadDecimal,
3298 NumpadComma,
3299 NumpadEnter,
3300 NumpadEquals,
3301 NumpadMultiply,
3302 NumpadSubtract,
3303 AbntC1,
3304 AbntC2,
3305 Apostrophe,
3306 Apps,
3307 Asterisk,
3308 At,
3309 Ax,
3310 Backslash,
3311 Calculator,
3312 Capital,
3313 Colon,
3314 Comma,
3315 Convert,
3316 Equals,
3317 Grave,
3318 Kana,
3319 Kanji,
3320 LAlt,
3321 LBracket,
3322 LControl,
3323 LShift,
3324 LWin,
3325 Mail,
3326 MediaSelect,
3327 MediaStop,
3328 Minus,
3329 Mute,
3330 MyComputer,
3331 NavigateForward,
3332 NavigateBackward,
3333 NextTrack,
3334 NoConvert,
3335 OEM102,
3336 Period,
3337 PlayPause,
3338 Plus,
3339 Power,
3340 PrevTrack,
3341 RAlt,
3342 RBracket,
3343 RControl,
3344 RShift,
3345 RWin,
3346 Semicolon,
3347 Slash,
3348 Sleep,
3349 Stop,
3350 Sysrq,
3351 Tab,
3352 Underline,
3353 Unlabeled,
3354 VolumeDown,
3355 VolumeUp,
3356 Wake,
3357 WebBack,
3358 WebFavorites,
3359 WebForward,
3360 WebHome,
3361 WebRefresh,
3362 WebSearch,
3363 WebStop,
3364 Yen,
3365 Copy,
3366 Paste,
3367 Cut,
3368}
3369
3370impl VirtualKeyCode {
3371 pub fn get_lowercase(&self) -> Option<char> {
3372 use self::VirtualKeyCode::*;
3373 match self {
3374 A => Some('a'),
3375 B => Some('b'),
3376 C => Some('c'),
3377 D => Some('d'),
3378 E => Some('e'),
3379 F => Some('f'),
3380 G => Some('g'),
3381 H => Some('h'),
3382 I => Some('i'),
3383 J => Some('j'),
3384 K => Some('k'),
3385 L => Some('l'),
3386 M => Some('m'),
3387 N => Some('n'),
3388 O => Some('o'),
3389 P => Some('p'),
3390 Q => Some('q'),
3391 R => Some('r'),
3392 S => Some('s'),
3393 T => Some('t'),
3394 U => Some('u'),
3395 V => Some('v'),
3396 W => Some('w'),
3397 X => Some('x'),
3398 Y => Some('y'),
3399 Z => Some('z'),
3400 Key0 | Numpad0 => Some('0'),
3401 Key1 | Numpad1 => Some('1'),
3402 Key2 | Numpad2 => Some('2'),
3403 Key3 | Numpad3 => Some('3'),
3404 Key4 | Numpad4 => Some('4'),
3405 Key5 | Numpad5 => Some('5'),
3406 Key6 | Numpad6 => Some('6'),
3407 Key7 | Numpad7 => Some('7'),
3408 Key8 | Numpad8 => Some('8'),
3409 Key9 | Numpad9 => Some('9'),
3410 Minus => Some('-'),
3411 Asterisk => Some('´'),
3412 At => Some('@'),
3413 Period => Some('.'),
3414 Semicolon => Some(';'),
3415 Slash => Some('/'),
3416 Caret => Some('^'),
3417 _ => None,
3418 }
3419 }
3420}
3421
3422#[derive(Debug, Clone)]
3424#[repr(C)]
3425pub struct SmallWindowIconBytes {
3426 pub key: IconKey,
3427 pub rgba_bytes: U8Vec,
3428}
3429
3430#[derive(Debug, Clone)]
3432#[repr(C)]
3433pub struct LargeWindowIconBytes {
3434 pub key: IconKey,
3435 pub rgba_bytes: U8Vec,
3436}
3437
3438#[derive(Debug, Clone)]
3440#[repr(C, u8)]
3441pub enum WindowIcon {
3442 Small(SmallWindowIconBytes),
3443 Large(LargeWindowIconBytes),
3445}
3446
3447impl_option!(
3448 WindowIcon,
3449 OptionWindowIcon,
3450 copy = false,
3451 [Debug, Clone, PartialOrd, PartialEq, Eq, Hash, Ord]
3452);
3453
3454impl WindowIcon {
3455 pub fn get_key(&self) -> IconKey {
3456 match &self {
3457 WindowIcon::Small(SmallWindowIconBytes { key, .. }) => *key,
3458 WindowIcon::Large(LargeWindowIconBytes { key, .. }) => *key,
3459 }
3460 }
3461}
3462impl PartialEq for WindowIcon {
3465 fn eq(&self, rhs: &Self) -> bool {
3466 self.get_key() == rhs.get_key()
3467 }
3468}
3469
3470impl PartialOrd for WindowIcon {
3471 fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
3472 Some((self.get_key()).cmp(&rhs.get_key()))
3473 }
3474}
3475
3476impl Eq for WindowIcon {}
3477
3478impl Ord for WindowIcon {
3479 fn cmp(&self, rhs: &Self) -> Ordering {
3480 (self.get_key()).cmp(&rhs.get_key())
3481 }
3482}
3483
3484impl Hash for WindowIcon {
3485 fn hash<H>(&self, state: &mut H)
3486 where
3487 H: Hasher,
3488 {
3489 self.get_key().hash(state);
3490 }
3491}
3492
3493#[derive(Debug, Clone)]
3495#[repr(C)]
3496pub struct TaskBarIcon {
3497 pub key: IconKey,
3498 pub rgba_bytes: U8Vec,
3499}
3500
3501impl_option!(
3502 TaskBarIcon,
3503 OptionTaskBarIcon,
3504 copy = false,
3505 [Debug, Clone, PartialOrd, PartialEq, Eq, Hash, Ord]
3506);
3507
3508impl PartialEq for TaskBarIcon {
3509 fn eq(&self, rhs: &Self) -> bool {
3510 self.key == rhs.key
3511 }
3512}
3513
3514impl PartialOrd for TaskBarIcon {
3515 fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
3516 Some((self.key).cmp(&rhs.key))
3517 }
3518}
3519
3520impl Eq for TaskBarIcon {}
3521
3522impl Ord for TaskBarIcon {
3523 fn cmp(&self, rhs: &Self) -> Ordering {
3524 (self.key).cmp(&rhs.key)
3525 }
3526}
3527
3528impl Hash for TaskBarIcon {
3529 fn hash<H>(&self, state: &mut H)
3530 where
3531 H: Hasher,
3532 {
3533 self.key.hash(state);
3534 }
3535}
3536
3537#[derive(Debug, Default, Clone, PartialEq, PartialOrd, Hash, Eq, Ord)]
3541#[repr(C)]
3542pub struct Menu {
3543 pub items: MenuItemVec,
3544 pub position: MenuPopupPosition,
3545 pub context_mouse_btn: ContextMenuMouseButton,
3546}
3547
3548impl_option!(
3549 Menu,
3550 OptionMenu,
3551 copy = false,
3552 [Debug, Clone, PartialEq, PartialOrd, Hash, Eq, Ord]
3553);
3554
3555impl Menu {
3556 pub fn new(items: MenuItemVec) -> Self {
3557 Self {
3558 items,
3559 position: MenuPopupPosition::AutoCursor,
3560 context_mouse_btn: ContextMenuMouseButton::Right,
3561 }
3562 }
3563}
3564
3565#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Hash, Eq, Ord)]
3566#[repr(C)]
3567pub enum ContextMenuMouseButton {
3568 Right,
3569 Middle,
3570 Left,
3571}
3572
3573impl Default for ContextMenuMouseButton {
3574 fn default() -> Self {
3575 ContextMenuMouseButton::Right
3576 }
3577}
3578
3579impl Menu {
3580 pub fn swap_with_default(&mut self) -> Self {
3581 let mut new = Self::default();
3582 core::mem::swap(&mut new, self);
3583 new
3584 }
3585}
3586
3587#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Hash, Eq, Ord)]
3591#[repr(C)]
3592pub enum MenuPopupPosition {
3593 BottomLeftOfCursor,
3595 BottomRightOfCursor,
3596 TopLeftOfCursor,
3597 TopRightOfCursor,
3598
3599 BottomOfHitRect,
3601 LeftOfHitRect,
3602 TopOfHitRect,
3603 RightOfHitRect,
3604
3605 AutoCursor,
3609 AutoHitRect,
3610}
3611
3612impl Default for MenuPopupPosition {
3613 fn default() -> Self {
3614 Self::AutoCursor
3615 }
3616}
3617
3618impl Menu {
3619 pub fn get_hash(&self) -> u64 {
3620 use highway::{HighwayHash, HighwayHasher, Key};
3621 let mut hasher = HighwayHasher::new(Key([0; 4]));
3622 self.hash(&mut hasher);
3623 hasher.finalize64()
3624 }
3625}
3626
3627#[derive(Debug, Clone, PartialEq, PartialOrd, Hash, Eq, Ord)]
3628#[repr(C, u8)]
3629pub enum MenuItem {
3630 String(StringMenuItem),
3632 Separator,
3634 BreakLine,
3636}
3637
3638impl_vec!(MenuItem, MenuItemVec, MenuItemVecDestructor);
3639impl_vec_clone!(MenuItem, MenuItemVec, MenuItemVecDestructor);
3640impl_vec_debug!(MenuItem, MenuItemVec);
3641impl_vec_partialeq!(MenuItem, MenuItemVec);
3642impl_vec_partialord!(MenuItem, MenuItemVec);
3643impl_vec_hash!(MenuItem, MenuItemVec);
3644impl_vec_eq!(MenuItem, MenuItemVec);
3645impl_vec_ord!(MenuItem, MenuItemVec);
3646
3647#[derive(Debug, Clone, PartialEq, PartialOrd, Hash, Eq, Ord)]
3648#[repr(C)]
3649pub struct StringMenuItem {
3650 pub label: AzString,
3652 pub accelerator: OptionVirtualKeyCodeCombo,
3655 pub callback: OptionMenuCallback,
3657 pub state: MenuItemState,
3659 pub icon: OptionMenuItemIcon,
3661 pub children: MenuItemVec,
3663}
3664
3665impl StringMenuItem {
3666 pub fn new(label: AzString) -> Self {
3667 StringMenuItem {
3668 label,
3669 accelerator: None.into(),
3670 callback: None.into(),
3671 state: MenuItemState::Normal,
3672 icon: None.into(),
3673 children: MenuItemVec::from_const_slice(&[]),
3674 }
3675 }
3676
3677 pub fn swap_with_default(&mut self) -> Self {
3678 let mut default = Self {
3679 label: AzString::from_const_str(""),
3680 accelerator: None.into(),
3681 callback: None.into(),
3682 state: MenuItemState::Normal,
3683 icon: None.into(),
3684 children: Vec::new().into(),
3685 };
3686 core::mem::swap(&mut default, self);
3687 default
3688 }
3689
3690 pub fn with_children(mut self, children: MenuItemVec) -> Self {
3691 self.children = children;
3692 self
3693 }
3694
3695 pub fn with_callback(mut self, data: RefAny, callback: CallbackType) -> Self {
3696 self.callback = Some(MenuCallback {
3697 data,
3698 callback: Callback { cb: callback },
3699 })
3700 .into();
3701 self
3702 }
3703}
3704
3705#[derive(Debug, Clone, PartialEq, PartialOrd, Hash, Eq, Ord)]
3706#[repr(C)]
3707pub struct VirtualKeyCodeCombo {
3708 pub keys: VirtualKeyCodeVec,
3709}
3710
3711impl_option!(
3712 VirtualKeyCodeCombo,
3713 OptionVirtualKeyCodeCombo,
3714 copy = false,
3715 [Debug, Clone, PartialEq, PartialOrd, Hash, Eq, Ord]
3716);
3717
3718#[derive(Debug, Clone, PartialEq, PartialOrd, Hash, Eq, Ord)]
3721#[repr(C)]
3722pub struct MenuCallback {
3723 pub callback: Callback,
3724 pub data: RefAny,
3725}
3726
3727impl_option!(
3728 MenuCallback,
3729 OptionMenuCallback,
3730 copy = false,
3731 [Debug, Clone, PartialEq, PartialOrd, Hash, Eq, Ord]
3732);
3733
3734#[derive(Debug, Clone, PartialEq, PartialOrd, Hash, Eq, Ord)]
3735#[repr(C, u8)]
3736pub enum MenuItemIcon {
3737 Checkbox(bool),
3739 Image(ImageRef),
3741}
3742
3743impl_option!(
3744 MenuItemIcon,
3745 OptionMenuItemIcon,
3746 copy = false,
3747 [Debug, Clone, PartialEq, PartialOrd, Hash, Eq, Ord]
3748);
3749
3750#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Hash, Eq, Ord)]
3752#[repr(C)]
3753pub enum MenuItemState {
3754 Normal,
3756 Greyed,
3758 Disabled,
3760}