1use std::{
2 fmt,
3 hash::{Hash, Hasher},
4 cmp::Ordering,
5 rc::Rc,
6 collections::{BTreeMap, HashSet},
7 sync::atomic::{AtomicUsize, Ordering as AtomicOrdering},
8 path::PathBuf,
9};
10#[cfg(target_os = "windows")]
11use std::ffi::c_void;
12#[cfg(not(test))]
13#[cfg(debug_assertions)]
14use std::time::Duration;
15use azul_css::{Css, LayoutPoint, LayoutRect, CssPath};
16#[cfg(debug_assertions)]
17#[cfg(not(test))]
18use azul_css::HotReloadHandler;
19use crate::{
20 FastHashMap,
21 app_resources::Epoch,
22 dom::{DomId, EventFilter},
23 id_tree::NodeId,
24 callbacks::{PipelineId, DocumentId, Callback, ScrollPosition, HitTestItem, UpdateScreen, Redraw},
25 ui_solver::{OverflowingScrollNode, ExternalScrollId, ScrolledNodes},
26 ui_state::UiState,
27 display_list::{SolvedLayoutCache, GlTextureCache, CachedDisplayList},
28};
29#[cfg(feature = "opengl")]
30use gleam::gl::Gl;
31
32pub const DEFAULT_TITLE: &str = "Azul App";
33pub const DEFAULT_WIDTH: f32 = 800.0;
34pub const DEFAULT_HEIGHT: f32 = 600.0;
35
36static LAST_WINDOW_ID: AtomicUsize = AtomicUsize::new(0);
37
38#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
41pub struct WindowId { id: usize }
42
43impl WindowId {
44 pub fn new() -> Self {
45 WindowId { id: LAST_WINDOW_ID.fetch_add(1, AtomicOrdering::SeqCst) }
46 }
47}
48
49static LAST_ICON_KEY: AtomicUsize = AtomicUsize::new(0);
50
51#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
55pub struct IconKey { id: usize }
56
57impl IconKey {
58 pub fn new() -> Self {
59 Self { id: LAST_ICON_KEY.fetch_add(1, AtomicOrdering::SeqCst) }
60 }
61}
62
63#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
64pub enum MouseCursorType {
65 Default,
66 Crosshair,
67 Hand,
68 Arrow,
69 Move,
70 Text,
71 Wait,
72 Help,
73 Progress,
74 NotAllowed,
75 ContextMenu,
76 Cell,
77 VerticalText,
78 Alias,
79 Copy,
80 NoDrop,
81 Grab,
82 Grabbing,
83 AllScroll,
84 ZoomIn,
85 ZoomOut,
86 EResize,
87 NResize,
88 NeResize,
89 NwResize,
90 SResize,
91 SeResize,
92 SwResize,
93 WResize,
94 EwResize,
95 NsResize,
96 NeswResize,
97 NwseResize,
98 ColResize,
99 RowResize,
100}
101
102impl Default for MouseCursorType {
103 fn default() -> Self {
104 MouseCursorType::Default
105 }
106}
107
108pub type ScanCode = u32;
110
111#[derive(Default, Debug, Clone, PartialEq)]
113pub struct KeyboardState {
114 pub shift_down: bool,
116 pub ctrl_down: bool,
118 pub alt_down: bool,
120 pub super_down: bool,
122 pub current_char: Option<char>,
124 pub current_virtual_keycode: Option<VirtualKeyCode>,
130 pub pressed_virtual_keycodes: HashSet<VirtualKeyCode>,
138 pub pressed_scancodes: HashSet<ScanCode>,
143}
144
145#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
147pub struct MouseState {
148 pub mouse_cursor_type: Option<MouseCursorType>,
150 pub cursor_position: CursorPosition,
152 pub is_cursor_locked: bool,
154 pub left_down: bool,
156 pub right_down: bool,
158 pub middle_down: bool,
160 pub scroll_x: Option<f32>,
162 pub scroll_y: Option<f32>,
164}
165
166impl Default for MouseState {
167 fn default() -> Self {
168 Self {
169 mouse_cursor_type: Some(MouseCursorType::Default),
170 cursor_position: CursorPosition::default(),
171 is_cursor_locked: false,
172 left_down: false,
173 right_down: false,
174 middle_down: false,
175 scroll_x: None,
176 scroll_y: None,
177 }
178 }
179}
180
181impl MouseState {
182
183 pub fn mouse_down(&self) -> bool {
185 self.right_down || self.left_down || self.middle_down
186 }
187
188 pub fn get_scroll_x(&self) -> f32 {
189 self.scroll_x.unwrap_or(0.0)
190 }
191
192 pub fn get_scroll_y(&self) -> f32 {
193 self.scroll_y.unwrap_or(0.0)
194 }
195
196 pub fn get_scroll(&self) -> (f32, f32) {
197 (self.get_scroll_x(), self.get_scroll_y())
198 }
199}
200
201#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
202pub enum CursorPosition {
203 OutOfWindow,
204 Uninitialized,
205 InWindow(LogicalPosition),
206}
207
208impl Default for CursorPosition {
209 fn default() -> CursorPosition {
210 CursorPosition::Uninitialized
211 }
212}
213
214impl CursorPosition {
215 pub fn get_position(&self) -> Option<LogicalPosition> {
216 match self {
217 CursorPosition::InWindow(logical_pos) => Some(*logical_pos),
218 CursorPosition::OutOfWindow | CursorPosition::Uninitialized => None,
219 }
220 }
221}
222
223#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
226pub struct DebugState {
227 pub profiler_dbg: bool,
229 pub render_target_dbg: bool,
231 pub texture_cache_dbg: bool,
233 pub gpu_time_queries: bool,
235 pub gpu_sample_queries: bool,
237 pub disable_batching: bool,
239 pub epochs: bool,
241 pub compact_profiler: bool,
243 pub echo_driver_messages: bool,
245 pub new_frame_indicator: bool,
247 pub new_scene_indicator: bool,
249 pub show_overdraw: bool,
251 pub gpu_cache_dbg: bool,
253}
254
255
256#[derive(Debug, Default)]
257pub struct ScrollStates(pub FastHashMap<ExternalScrollId, ScrollState>);
258
259impl ScrollStates {
260
261 pub fn new() -> ScrollStates {
262 ScrollStates::default()
263 }
264
265 #[must_use]
266 pub fn get_scroll_position(&self, scroll_id: &ExternalScrollId) -> Option<LayoutPoint> {
267 self.0.get(&scroll_id).map(|entry| entry.get())
268 }
269
270 pub fn set_scroll_position(&mut self, node: &OverflowingScrollNode, scroll_position: LayoutPoint) {
273 self.0.entry(node.parent_external_scroll_id)
274 .or_insert_with(|| ScrollState::default())
275 .set(scroll_position.x, scroll_position.y, &node.child_rect);
276 }
277
278 #[must_use]
280 pub fn get_scroll_position_and_mark_as_used(&mut self, scroll_id: &ExternalScrollId) -> Option<LayoutPoint> {
281 let entry = self.0.get_mut(&scroll_id)?;
282 Some(entry.get_and_mark_as_used())
283 }
284
285 pub fn scroll_node(&mut self, node: &OverflowingScrollNode, scroll_by_x: f32, scroll_by_y: f32) {
288 self.0.entry(node.parent_external_scroll_id)
289 .or_insert_with(|| ScrollState::default())
290 .add(scroll_by_x, scroll_by_y, &node.child_rect);
291 }
292
293 pub fn remove_unused_scroll_states(&mut self) {
295 self.0.retain(|_, state| state.used_this_frame);
296 }
297}
298
299#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
300pub struct ScrollState {
301 pub scroll_position: LayoutPoint,
303 pub used_this_frame: bool,
305}
306
307impl ScrollState {
308
309 pub fn get(&self) -> LayoutPoint {
311 self.scroll_position
312 }
313
314 pub fn add(&mut self, x: f32, y: f32, child_rect: &LayoutRect) {
316 self.scroll_position.x = (self.scroll_position.x + x).max(0.0).min(child_rect.size.width);
317 self.scroll_position.y = (self.scroll_position.y + y).max(0.0).min(child_rect.size.height);
318 }
319
320 pub fn set(&mut self, x: f32, y: f32, child_rect: &LayoutRect) {
322 self.scroll_position.x = x.max(0.0).min(child_rect.size.width);
323 self.scroll_position.y = y.max(0.0).min(child_rect.size.height);
324 }
325
326 pub fn get_and_mark_as_used(&mut self) -> LayoutPoint {
328 self.used_this_frame = true;
329 self.scroll_position
330 }
331}
332
333impl Default for ScrollState {
334 fn default() -> Self {
335 ScrollState {
336 scroll_position: LayoutPoint::zero(),
337 used_this_frame: true,
338 }
339 }
340}
341
342pub fn update_full_window_state(
345 full_window_state: &mut FullWindowState,
346 window_state: &WindowState
347) {
348 full_window_state.title = window_state.title.clone();
349 full_window_state.size = window_state.size;
350 full_window_state.position = window_state.position;
351 full_window_state.flags = window_state.flags;
352 full_window_state.debug_state = window_state.debug_state;
353 full_window_state.keyboard_state = window_state.keyboard_state.clone();
354 full_window_state.mouse_state = window_state.mouse_state;
355 full_window_state.ime_position = window_state.ime_position;
356 full_window_state.platform_specific_options = window_state.platform_specific_options.clone();
357}
358
359pub fn clear_scroll_state(window_state: &mut FullWindowState) {
361 window_state.mouse_state.scroll_x = None;
362 window_state.mouse_state.scroll_y = None;
363}
364
365pub struct WindowInternal {
366 pub document_id: DocumentId,
368 pub pipeline_id: PipelineId,
374 pub epoch: Epoch,
377 pub cached_display_list: CachedDisplayList,
379 pub layout_result: SolvedLayoutCache,
381 pub gl_texture_cache: GlTextureCache,
383 pub scrolled_nodes: BTreeMap<DomId, ScrolledNodes>,
385 pub scroll_states: ScrollStates,
387}
388
389impl WindowInternal {
390
391 pub fn get_current_scroll_states(&self, ui_states: &BTreeMap<DomId, UiState>)
393 -> BTreeMap<DomId, BTreeMap<NodeId, ScrollPosition>>
394 {
395 self.scrolled_nodes.iter().filter_map(|(dom_id, scrolled_nodes)| {
396
397 let layout_result = self.layout_result.solved_layouts.get(dom_id)?;
398 let ui_state = &ui_states.get(dom_id)?;
399
400 let scroll_positions = scrolled_nodes.overflowing_nodes.iter().filter_map(|(node_id, overflowing_node)| {
401 let scroll_location = self.scroll_states.get_scroll_position(&overflowing_node.parent_external_scroll_id)?;
402 let parent_node = ui_state.get_dom().arena.node_hierarchy[*node_id].parent.unwrap_or(NodeId::ZERO);
403 let scroll_position = ScrollPosition {
404 scroll_frame_rect: overflowing_node.child_rect,
405 parent_rect: layout_result.rects[parent_node].to_layouted_rectangle(),
406 scroll_location,
407 };
408 Some((*node_id, scroll_position))
409 }).collect();
410
411 Some((dom_id.clone(), scroll_positions))
412 }).collect()
413 }
414}
415
416#[derive(Debug, Clone, PartialEq)]
418pub struct WindowState {
419 pub title: String,
421 pub size: WindowSize,
423 pub position: Option<PhysicalPosition<u32>>,
425 pub flags: WindowFlags,
427 pub debug_state: DebugState,
430 pub keyboard_state: KeyboardState,
433 pub mouse_state: MouseState,
435 pub ime_position: Option<LogicalPosition>,
438 pub platform_specific_options: PlatformSpecificOptions,
441 pub css: Css,
443}
444
445#[derive(Debug, Clone, PartialEq)]
446pub struct FullWindowState {
447 pub title: String,
449 pub size: WindowSize,
451 pub position: Option<PhysicalPosition<u32>>,
453 pub flags: WindowFlags,
455 pub debug_state: DebugState,
458 pub keyboard_state: KeyboardState,
461 pub mouse_state: MouseState,
463 pub ime_position: Option<LogicalPosition>,
466 pub platform_specific_options: PlatformSpecificOptions,
469 pub css: Css,
471
472 pub previous_window_state: Option<Box<FullWindowState>>,
476 pub hovered_file: Option<PathBuf>,
478 pub dropped_file: Option<PathBuf>,
480 pub focused_node: Option<(DomId, NodeId)>,
484 pub hovered_nodes: BTreeMap<DomId, BTreeMap<NodeId, HitTestItem>>,
487}
488
489impl Default for FullWindowState {
490 fn default() -> Self {
491 Self {
492 title: DEFAULT_TITLE.into(),
493 size: WindowSize::default(),
494 position: None,
495 flags: WindowFlags::default(),
496 debug_state: DebugState::default(),
497 keyboard_state: KeyboardState::default(),
498 mouse_state: MouseState::default(),
499 ime_position: None,
500 platform_specific_options: PlatformSpecificOptions::default(),
501 css: Css::default(),
502
503 previous_window_state: None,
506 hovered_file: None,
507 dropped_file: None,
508 focused_node: None,
509 hovered_nodes: BTreeMap::default(),
510 }
511 }
512}
513
514impl FullWindowState {
515
516 pub fn get_mouse_state(&self) -> &MouseState {
517 &self.mouse_state
518 }
519
520 pub fn get_keyboard_state(&self) -> &KeyboardState {
521 &self.keyboard_state
522 }
523
524 pub fn get_hovered_file(&self) -> Option<&PathBuf> {
525 self.hovered_file.as_ref()
526 }
527
528 pub fn get_dropped_file(&self) -> Option<&PathBuf> {
529 self.dropped_file.as_ref()
530 }
531
532 pub fn get_previous_window_state(&self) -> Option<&Box<FullWindowState>> {
537 self.previous_window_state.as_ref()
538 }
539}
540
541impl From<WindowState> for FullWindowState {
542 fn from(window_state: WindowState) -> FullWindowState {
545 FullWindowState {
546 title: window_state.title,
547 size: window_state.size,
548 position: window_state.position,
549 flags: window_state.flags,
550 debug_state: window_state.debug_state,
551 keyboard_state: window_state.keyboard_state,
552 mouse_state: window_state.mouse_state,
553 ime_position: window_state.ime_position,
554 platform_specific_options: window_state.platform_specific_options,
555 css: window_state.css,
556 .. Default::default()
557 }
558 }
559}
560
561impl From<FullWindowState> for WindowState {
562 fn from(full_window_state: FullWindowState) -> WindowState {
563 WindowState {
564 title: full_window_state.title,
565 size: full_window_state.size,
566 position: full_window_state.position,
567 flags: full_window_state.flags,
568 debug_state: full_window_state.debug_state,
569 keyboard_state: full_window_state.keyboard_state,
570 mouse_state: full_window_state.mouse_state,
571 ime_position: full_window_state.ime_position,
572 platform_specific_options: full_window_state.platform_specific_options,
573 css: full_window_state.css,
574 }
575 }
576}
577
578#[derive(Debug, Clone, PartialEq)]
579pub struct CallCallbacksResult {
580 pub needs_restyle_hover_active: bool,
581 pub needs_relayout_hover_active: bool,
582 pub needs_restyle_focus_changed: bool,
583 pub should_scroll_render: bool,
585 pub callbacks_update_screen: UpdateScreen,
587 pub modified_window_state: WindowState,
589}
590
591impl CallCallbacksResult {
592
593 pub fn should_relayout(&self) -> bool {
594 self.needs_relayout_hover_active ||
595 self.callbacks_update_screen == Redraw
596 }
597
598 pub fn should_restyle(&self) -> bool {
599 self.should_relayout() ||
600 self.needs_restyle_focus_changed ||
601 self.needs_restyle_hover_active
602 }
603
604 pub fn should_rerender(&self) -> bool {
605 self.should_restyle() ||
606 self.should_scroll_render
607 }
608}
609
610#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
611pub struct WindowFlags {
612 pub is_maximized: bool,
614 pub is_fullscreen: bool,
616 pub has_decorations: bool,
618 pub is_visible: bool,
620 pub is_always_on_top: bool,
622 pub is_resizable: bool,
624}
625
626impl Default for WindowFlags {
627 fn default() -> Self {
628 Self {
629 is_maximized: false,
630 is_fullscreen: false,
631 has_decorations: true,
632 is_visible: true,
633 is_always_on_top: false,
634 is_resizable: true,
635 }
636 }
637}
638
639#[cfg(target_arch = "wasm32")]
640pub type PlatformSpecificOptions = WasmWindowOptions;
641#[cfg(target_os = "windows")]
642pub type PlatformSpecificOptions = WindowsWindowOptions;
643#[cfg(target_os = "linux")]
644pub type PlatformSpecificOptions = LinuxWindowOptions;
645#[cfg(target_os = "macos")]
646pub type PlatformSpecificOptions = MacWindowOptions;
647
648#[cfg(target_arch = "wasm32")]
649#[derive(Debug, Default, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
650pub struct WasmWindowOptions {
651 }
653
654#[cfg(target_os = "windows")]
655#[derive(Debug, Default, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
656pub struct WindowsWindowOptions {
657 pub no_redirection_bitmap: bool,
659 pub window_icon: Option<WindowIcon>,
661 pub taskbar_icon: Option<TaskBarIcon>,
665 pub parent_window: Option<*mut c_void>,
667}
668
669#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
672pub enum XWindowType {
673 Desktop,
677 Dock,
679 Toolbar,
681 Menu,
683 Utility,
685 Splash,
687 Dialog,
689 DropdownMenu,
692 PopupMenu,
695 Tooltip,
698 Notification,
701 Combo,
704 Dnd,
707 Normal,
709}
710
711impl Default for XWindowType {
712 fn default() -> Self {
713 XWindowType::Normal
714 }
715}
716
717#[cfg(target_os = "linux")]
718#[derive(Debug, Default, Clone, PartialEq, PartialOrd)]
719pub struct LinuxWindowOptions {
720 pub x11_visual: Option<*const ()>,
722 pub x11_screen: Option<i32>,
724 pub x11_wm_classes: Vec<(String, String)>,
727 pub x11_override_redirect: bool,
730 pub x11_window_types: Vec<XWindowType>,
733 pub x11_gtk_theme_variant: Option<String>,
736 pub x11_resize_increments: Option<LogicalSize>,
739 pub x11_base_size: Option<LogicalSize>,
742 pub wayland_app_id: Option<String>,
749 pub request_user_attention: bool,
750 pub wayland_theme: Option<WaylandTheme>,
751 pub window_icon: Option<WindowIcon>,
752}
753
754#[cfg(target_os = "macos")]
755#[derive(Debug, Default, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
756pub struct MacWindowOptions {
757 pub request_user_attention: bool,
758}
759
760impl WindowState {
761
762 pub fn new(css: Css) -> Self { Self { css, .. Default::default() } }
764
765 pub fn with_css(self, css: Css) -> Self { Self { css, .. self } }
767
768 pub fn get_mouse_state(&self) -> &MouseState {
771 &self.mouse_state
772 }
773
774 pub fn get_keyboard_state(&self) -> &KeyboardState {
777 &self.keyboard_state
778 }
779
780 pub fn get_physical_size(&self) -> (usize, usize) {
782 (self.size.dimensions.width as usize, self.size.dimensions.height as usize)
783 }
784
785 pub fn get_hidpi_factor(&self) -> f32 {
787 self.size.hidpi_factor
788 }
789}
790
791#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
792pub enum FullScreenMode {
793 SlowFullScreen,
796 FastFullScreen,
798 SlowWindowed,
801 FastWindowed,
803}
804
805#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
806pub struct WaylandTheme {
807 pub primary_active: [u8; 4],
809 pub primary_inactive: [u8; 4],
811 pub secondary_active: [u8; 4],
813 pub secondary_inactive: [u8; 4],
815 pub close_button_hovered: [u8; 4],
817 pub close_button: [u8; 4],
819 pub maximize_button_hovered: [u8; 4],
821 pub maximize_button: [u8; 4],
823 pub minimize_button_hovered: [u8; 4],
825 pub minimize_button: [u8; 4],
827}
828
829#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
830pub struct WindowSize {
831 pub dimensions: LogicalSize,
834 pub hidpi_factor: f32,
836 pub winit_hidpi_factor: f32,
838 pub min_dimensions: Option<LogicalSize>,
840 pub max_dimensions: Option<LogicalSize>,
842}
843
844impl WindowSize {
845
846 pub fn get_logical_size(&self) -> LogicalSize {
848 self.dimensions
849 }
850
851 pub fn get_physical_size(&self) -> PhysicalSize<u32> {
852 self.dimensions.to_physical(self.hidpi_factor)
853 }
854
855 pub fn get_reverse_logical_size(&self) -> LogicalSize {
857 LogicalSize::new(
858 self.dimensions.width * self.hidpi_factor / self.winit_hidpi_factor,
859 self.dimensions.height * self.hidpi_factor / self.winit_hidpi_factor,
860 )
861 }
862}
863
864impl Default for WindowSize {
865 fn default() -> Self {
866 Self {
867 dimensions: LogicalSize::new(DEFAULT_WIDTH, DEFAULT_HEIGHT),
868 hidpi_factor: 1.0,
869 winit_hidpi_factor: 1.0,
870 min_dimensions: None,
871 max_dimensions: None,
872 }
873 }
874}
875
876impl Default for WindowState {
877 fn default() -> Self {
878 Self {
879 title: DEFAULT_TITLE.into(),
880 size: WindowSize::default(),
881 position: None,
882 flags: WindowFlags::default(),
883 debug_state: DebugState::default(),
884 keyboard_state: KeyboardState::default(),
885 mouse_state: MouseState::default(),
886 ime_position: None,
887 platform_specific_options: PlatformSpecificOptions::default(),
888 css: Css::default(),
889 }
890 }
891}
892
893pub struct WindowCreateOptions {
895 pub state: WindowState,
897 pub renderer_type: RendererType,
901 #[cfg(debug_assertions)]
902 #[cfg(not(test))]
903 pub hot_reload_handler: Option<Box<dyn HotReloadHandler>>,
906}
907
908impl fmt::Debug for WindowCreateOptions {
909 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
910 #[cfg(debug_assertions)]
911 #[cfg(not(test))] {
912 write!(f, "WindowCreateOptions {{ state: {:?}, renderer_type: {:?}, hot_reload_handler: {:?} }}",
913 self.state, self.renderer_type, self.hot_reload_handler.is_some())
914 }
915 #[cfg(any(not(debug_assertions), test))] {
916 write!(f, "WindowCreateOptions {{ state: {:?}, renderer_type: {:?} }}", self.state, self.renderer_type)
917 }
918 }
919}
920
921impl Default for WindowCreateOptions {
922 fn default() -> Self {
923 Self {
924 state: WindowState::default(),
925 renderer_type: RendererType::default(),
926 #[cfg(debug_assertions)]
927 #[cfg(not(test))]
928 hot_reload_handler: None,
929 }
930 }
931}
932
933impl WindowCreateOptions {
934
935 pub fn new(css: Css) -> Self {
936 Self {
937 state: WindowState::new(css),
938 .. Default::default()
939 }
940 }
941
942 pub fn with_css(mut self, css: Css) -> Self {
943 self.state.css = css;
944 self
945 }
946
947 #[cfg(not(test))]
948 #[cfg(debug_assertions)]
949 pub fn new_hot_reload(hot_reload_handler: Box<dyn HotReloadHandler>) -> Self {
950 Self {
951 hot_reload_handler: Some(hot_reload_handler),
952 .. Default::default()
953 }
954 }
955}
956
957#[cfg(not(test))]
958#[cfg(debug_assertions)]
959pub struct HotReloader(pub Box<dyn HotReloadHandler>);
960
961#[cfg(not(test))]
962#[cfg(debug_assertions)]
963impl HotReloader {
964
965 pub fn new(hot_reload_handler: Box<dyn HotReloadHandler>) -> Self {
966 Self(hot_reload_handler)
967 }
968
969 pub fn get_reload_interval(&self) -> Duration {
970 self.0.get_reload_interval()
971 }
972
973 pub fn reload_style(&self) -> Result<Css, String> {
980 match self.0.reload_style() {
981 Ok(mut new_css) => {
982 new_css.sort_by_specificity();
983 Ok(new_css)
984 },
985 Err(why) => {
986 Err(format!("{}", why))
987 },
988 }
989 }
990}
991
992#[cfg_attr(not(feature = "opengl"), derive(Copy))]
1005pub enum RendererType {
1006 Default,
1008 ForceHardware,
1010 ForceSoftware,
1012 #[cfg(feature = "opengl")]
1014 Custom(Rc<dyn Gl>),
1015}
1016
1017impl RendererType {
1018 #[inline(always)]
1019 fn get_type(&self) -> RendererTypeNoData {
1020 match self {
1021 RendererType::Default => RendererTypeNoData::Default,
1022 RendererType::ForceHardware => RendererTypeNoData::ForceHardware,
1023 RendererType::ForceSoftware => RendererTypeNoData::ForceSoftware,
1024 #[cfg(feature = "opengl")]
1025 RendererType::Custom(_) => RendererTypeNoData::Custom,
1026 }
1027 }
1028}
1029
1030#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1031enum RendererTypeNoData {
1032 Default,
1033 ForceHardware,
1034 ForceSoftware,
1035 Custom,
1036}
1037
1038impl Clone for RendererType {
1039 fn clone(&self) -> Self {
1040 use self::RendererType::*;
1041 match self {
1042 Default => Default,
1043 ForceHardware => ForceHardware,
1044 ForceSoftware => ForceSoftware,
1045 #[cfg(feature = "opengl")]
1046 Custom(gl) => Custom(gl.clone()),
1047 }
1048 }
1049}
1050
1051impl PartialOrd for RendererType {
1052 fn partial_cmp(&self, other: &RendererType) -> Option<Ordering> {
1053 Some(self.cmp(other))
1054 }
1055}
1056
1057impl Ord for RendererType {
1058 fn cmp(&self, other: &RendererType) -> Ordering {
1059 self.get_type().cmp(&other.get_type())
1060 }
1061}
1062
1063impl PartialEq for RendererType {
1064 fn eq(&self, other: &RendererType) -> bool {
1065 self.get_type().eq(&other.get_type())
1066 }
1067}
1068
1069impl Eq for RendererType { }
1070
1071impl Hash for RendererType {
1072 fn hash<H>(&self, state: &mut H) where H: Hasher {
1073 self.get_type().hash(state);
1074 }
1075}
1076
1077impl fmt::Debug for RendererType {
1078 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1079 match self {
1080 RendererType::Default => write!(f, "Default"),
1081 RendererType::ForceHardware => write!(f, "ForceHardware"),
1082 RendererType::ForceSoftware => write!(f, "ForceSoftware"),
1083 #[cfg(feature = "opengl")]
1084 RendererType::Custom(_) => write!(f, "Custom"),
1085 }
1086 }
1087}
1088
1089impl Default for RendererType {
1090 fn default() -> Self {
1091 RendererType::Default
1092 }
1093}
1094
1095pub enum AzulUpdateEvent {
1098 CreateWindow {
1099 window_create_options: WindowCreateOptions,
1100 },
1101 CloseWindow {
1102 window_id: WindowId,
1103 },
1104 DoHitTest {
1105 window_id: WindowId,
1106 },
1107 RebuildUi {
1108 window_id: WindowId,
1109 },
1110 RestyleUi {
1111 window_id: WindowId,
1112 skip_layout: bool,
1113 },
1114 RelayoutUi {
1115 window_id: WindowId,
1116 },
1117 RebuildDisplayList {
1118 window_id: WindowId,
1119 },
1120 SendDisplayListToWebRender {
1121 window_id: WindowId,
1122 },
1123 UpdateScrollStates {
1124 window_id: WindowId,
1125 },
1126 UpdateAnimations {
1127 window_id: WindowId,
1128 },
1129 UpdateImages {
1130 window_id: WindowId,
1131 },
1132 }
1134
1135impl fmt::Debug for AzulUpdateEvent {
1136 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1137 use self::AzulUpdateEvent::*;
1138 match self {
1139 CreateWindow { window_create_options } => write!(f, "CreateWindow {{ window_create_options: {:?} }}", window_create_options),
1140 CloseWindow { window_id } => write!(f, "CloseWindow {{ window_id: {:?} }}", window_id),
1141 DoHitTest { window_id } => write!(f, "DoHitTest {{ window_id: {:?} }}", window_id),
1142 RebuildUi { window_id } => write!(f, "RebuildUi {{ window_id: {:?} }}", window_id),
1143 RestyleUi { window_id, skip_layout } => write!(f, "RestyleUi {{ window_id: {:?}, skip_layout: {:?} }}", window_id, skip_layout),
1144 RelayoutUi { window_id } => write!(f, "RelayoutUi {{ window_id: {:?} }}", window_id),
1145 RebuildDisplayList { window_id } => write!(f, "RebuildDisplayList {{ window_id: {:?} }}", window_id),
1146 SendDisplayListToWebRender { window_id } => write!(f, "SendDisplayListToWebRender {{ window_id: {:?} }}", window_id),
1147 UpdateScrollStates { window_id } => write!(f, "UpdateScrollStates {{ window_id: {:?} }}", window_id),
1148 UpdateAnimations { window_id } => write!(f, "UpdateAnimations {{ window_id: {:?} }}", window_id),
1149 UpdateImages { window_id } => write!(f, "UpdateImages {{ window_id: {:?} }}", window_id),
1150 }
1151 }
1152}
1153
1154#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq, Hash)]
1155pub enum UpdateFocusWarning {
1156 FocusInvalidDomId(DomId),
1157 FocusInvalidNodeId(NodeId),
1158 CouldNotFindFocusNode(CssPath),
1159}
1160
1161impl ::std::fmt::Display for UpdateFocusWarning {
1162 fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
1163 use self::UpdateFocusWarning::*;
1164 match self {
1165 FocusInvalidDomId(dom_id) => write!(f, "Focusing on DOM with invalid ID: {:?}", dom_id),
1166 FocusInvalidNodeId(node_id) => write!(f, "Focusing on node with invalid ID: {}", node_id),
1167 CouldNotFindFocusNode(css_path) => write!(f, "Could not find focus node for path: {}", css_path),
1168 }
1169 }
1170}
1171
1172pub struct DetermineCallbackResult {
1173 pub hit_test_item: Option<HitTestItem>,
1174 pub normal_callbacks: BTreeMap<EventFilter, Callback>,
1175}
1176
1177impl DetermineCallbackResult {
1178
1179 pub fn has_normal_callbacks(&self) -> bool {
1180 !self.normal_callbacks.is_empty()
1181 }
1182
1183 pub fn has_any_callbacks(&self) -> bool {
1184 self.has_normal_callbacks()
1185 }
1186}
1187
1188impl Default for DetermineCallbackResult {
1189 fn default() -> Self {
1190 DetermineCallbackResult {
1191 hit_test_item: None,
1192 normal_callbacks: BTreeMap::new(),
1193 }
1194 }
1195}
1196
1197impl Clone for DetermineCallbackResult {
1198 fn clone(&self) -> Self {
1199 DetermineCallbackResult {
1200 hit_test_item: self.hit_test_item.clone(),
1201 normal_callbacks: self.normal_callbacks.clone(),
1202 }
1203 }
1204}
1205
1206impl fmt::Debug for DetermineCallbackResult {
1207 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1208 write!(f,
1209 "DetermineCallbackResult {{ \
1210 hit_test_item: {:?},\
1211 normal_callbacks: {:#?},\
1212 }}",
1213 self.hit_test_item,
1214 self.normal_callbacks.keys().collect::<Vec<_>>(),
1215 )
1216 }
1217}
1218
1219pub struct CallbacksOfHitTest {
1220 pub nodes_with_callbacks: BTreeMap<NodeId, DetermineCallbackResult>,
1224 pub needs_relayout_anyways: bool,
1228 pub needs_redraw_anyways: bool,
1232}
1233
1234impl Default for CallbacksOfHitTest {
1235 fn default() -> Self {
1236 Self {
1237 nodes_with_callbacks: BTreeMap::new(),
1238 needs_redraw_anyways: false,
1239 needs_relayout_anyways: false,
1240 }
1241 }
1242}
1243
1244impl fmt::Debug for CallbacksOfHitTest {
1245 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1246 write!(f,
1247 "CallbacksOfHitTest {{ \
1248 nodes_with_callbacks: {:#?},
1249 needs_relayout_anyways: {:?},
1250 needs_redraw_anyways: {:?},
1251 }}",
1252 self.nodes_with_callbacks,
1253 self.needs_relayout_anyways,
1254 self.needs_redraw_anyways,
1255 )
1256 }
1257}
1258
1259impl CallbacksOfHitTest {
1260 pub fn should_call_callbacks(&self) -> bool {
1262 !self.nodes_with_callbacks.is_empty() &&
1263 self.nodes_with_callbacks.values().any(|n| n.has_any_callbacks())
1264 }
1265}
1266
1267#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
1268pub struct LogicalPosition {
1269 pub x: f32,
1270 pub y: f32,
1271}
1272
1273#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
1274pub struct LogicalSize {
1275 pub width: f32,
1276 pub height: f32,
1277}
1278
1279#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
1280pub struct PhysicalPosition<T> {
1281 pub x: T,
1282 pub y: T,
1283}
1284
1285#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
1286pub struct PhysicalSize<T> {
1287 pub width: T,
1288 pub height: T,
1289}
1290
1291impl LogicalPosition {
1292 #[inline(always)]
1293 pub const fn new(x: f32, y: f32) -> Self { Self { x, y } }
1294 #[inline(always)]
1295 pub const fn zero() -> Self { Self::new(0.0, 0.0) }
1296 #[inline(always)]
1297 pub fn to_physical(self, hidpi_factor: f32) -> PhysicalPosition<u32> {
1298 PhysicalPosition {
1299 x: (self.x * hidpi_factor) as u32,
1300 y: (self.y * hidpi_factor) as u32,
1301 }
1302 }
1303}
1304
1305impl<T> PhysicalPosition<T> {
1306 #[inline(always)]
1307 pub const fn new(x: T, y: T) -> Self { Self { x, y } }
1308}
1309
1310impl PhysicalPosition<u32> {
1311 #[inline(always)]
1312 pub const fn zero() -> Self { Self::new(0, 0) }
1313 #[inline(always)]
1314 pub fn to_logical(self, hidpi_factor: f32) -> LogicalPosition {
1315 LogicalPosition {
1316 x: self.x as f32 / hidpi_factor,
1317 y: self.y as f32 / hidpi_factor,
1318 }
1319 }
1320}
1321
1322impl PhysicalPosition<f64> {
1323 #[inline(always)]
1324 pub const fn zero() -> Self { Self::new(0.0, 0.0) }
1325 #[inline(always)]
1326 pub fn to_logical(self, hidpi_factor: f32) -> LogicalPosition {
1327 LogicalPosition {
1328 x: self.x as f32 / hidpi_factor,
1329 y: self.y as f32 / hidpi_factor,
1330 }
1331 }
1332}
1333
1334impl LogicalSize {
1335 #[inline(always)]
1336 pub const fn new(width: f32, height: f32) -> Self { Self { width, height } }
1337 #[inline(always)]
1338 pub const fn zero() -> Self { Self::new(0.0, 0.0) }
1339 #[inline(always)]
1340 pub fn to_physical(self, hidpi_factor: f32) -> PhysicalSize<u32> {
1341 PhysicalSize {
1342 width: (self.width * hidpi_factor) as u32,
1343 height: (self.height * hidpi_factor) as u32,
1344 }
1345 }
1346}
1347
1348impl<T> PhysicalSize<T> {
1349 #[inline(always)]
1350 pub const fn new(width: T, height: T) -> Self { Self { width, height } }
1351}
1352
1353impl PhysicalSize<u32> {
1354 #[inline(always)]
1355 pub const fn zero() -> Self { Self::new(0, 0) }
1356 #[inline(always)]
1357 pub fn to_logical(self, hidpi_factor: f32) -> LogicalSize {
1358 LogicalSize {
1359 width: self.width as f32 / hidpi_factor,
1360 height: self.height as f32 / hidpi_factor,
1361 }
1362 }
1363}
1364
1365#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1367pub enum AcceleratorKey {
1368 Ctrl,
1369 Alt,
1370 Shift,
1371 Key(VirtualKeyCode),
1372}
1373
1374impl AcceleratorKey {
1375
1376 pub fn matches(&self, keyboard_state: &KeyboardState) -> bool {
1380 use self::AcceleratorKey::*;
1381 match self {
1382 Ctrl => keyboard_state.ctrl_down,
1383 Alt => keyboard_state.alt_down,
1384 Shift => keyboard_state.shift_down,
1385 Key(k) => keyboard_state.pressed_virtual_keycodes.contains(k),
1386 }
1387 }
1388}
1389
1390#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1392pub enum VirtualKeyCode {
1393 Key1,
1394 Key2,
1395 Key3,
1396 Key4,
1397 Key5,
1398 Key6,
1399 Key7,
1400 Key8,
1401 Key9,
1402 Key0,
1403 A,
1404 B,
1405 C,
1406 D,
1407 E,
1408 F,
1409 G,
1410 H,
1411 I,
1412 J,
1413 K,
1414 L,
1415 M,
1416 N,
1417 O,
1418 P,
1419 Q,
1420 R,
1421 S,
1422 T,
1423 U,
1424 V,
1425 W,
1426 X,
1427 Y,
1428 Z,
1429 Escape,
1430 F1,
1431 F2,
1432 F3,
1433 F4,
1434 F5,
1435 F6,
1436 F7,
1437 F8,
1438 F9,
1439 F10,
1440 F11,
1441 F12,
1442 F13,
1443 F14,
1444 F15,
1445 F16,
1446 F17,
1447 F18,
1448 F19,
1449 F20,
1450 F21,
1451 F22,
1452 F23,
1453 F24,
1454 Snapshot,
1455 Scroll,
1456 Pause,
1457 Insert,
1458 Home,
1459 Delete,
1460 End,
1461 PageDown,
1462 PageUp,
1463 Left,
1464 Up,
1465 Right,
1466 Down,
1467 Back,
1468 Return,
1469 Space,
1470 Compose,
1471 Caret,
1472 Numlock,
1473 Numpad0,
1474 Numpad1,
1475 Numpad2,
1476 Numpad3,
1477 Numpad4,
1478 Numpad5,
1479 Numpad6,
1480 Numpad7,
1481 Numpad8,
1482 Numpad9,
1483 AbntC1,
1484 AbntC2,
1485 Add,
1486 Apostrophe,
1487 Apps,
1488 At,
1489 Ax,
1490 Backslash,
1491 Calculator,
1492 Capital,
1493 Colon,
1494 Comma,
1495 Convert,
1496 Decimal,
1497 Divide,
1498 Equals,
1499 Grave,
1500 Kana,
1501 Kanji,
1502 LAlt,
1503 LBracket,
1504 LControl,
1505 LShift,
1506 LWin,
1507 Mail,
1508 MediaSelect,
1509 MediaStop,
1510 Minus,
1511 Multiply,
1512 Mute,
1513 MyComputer,
1514 NavigateForward,
1515 NavigateBackward,
1516 NextTrack,
1517 NoConvert,
1518 NumpadComma,
1519 NumpadEnter,
1520 NumpadEquals,
1521 OEM102,
1522 Period,
1523 PlayPause,
1524 Power,
1525 PrevTrack,
1526 RAlt,
1527 RBracket,
1528 RControl,
1529 RShift,
1530 RWin,
1531 Semicolon,
1532 Slash,
1533 Sleep,
1534 Stop,
1535 Subtract,
1536 Sysrq,
1537 Tab,
1538 Underline,
1539 Unlabeled,
1540 VolumeDown,
1541 VolumeUp,
1542 Wake,
1543 WebBack,
1544 WebFavorites,
1545 WebForward,
1546 WebHome,
1547 WebRefresh,
1548 WebSearch,
1549 WebStop,
1550 Yen,
1551 Copy,
1552 Paste,
1553 Cut,
1554}
1555
1556#[derive(Debug, Clone)]
1558pub enum WindowIcon {
1559 Small { key: IconKey, rgba_bytes: Vec<u8> },
1561 Large { key: IconKey, rgba_bytes: Vec<u8> },
1563}
1564
1565impl WindowIcon {
1566 pub fn get_key(&self) -> IconKey {
1567 match &self {
1568 WindowIcon::Small { key, .. } => *key,
1569 WindowIcon::Large { key, .. } => *key,
1570 }
1571 }
1572}
1573impl PartialEq for WindowIcon {
1576 fn eq(&self, rhs: &Self) -> bool {
1577 self.get_key() == rhs.get_key()
1578 }
1579}
1580
1581impl PartialOrd for WindowIcon {
1582 fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
1583 Some((self.get_key()).cmp(&rhs.get_key()))
1584 }
1585}
1586
1587impl Eq for WindowIcon { }
1588
1589impl Ord for WindowIcon {
1590 fn cmp(&self, rhs: &Self) -> Ordering {
1591 (self.get_key()).cmp(&rhs.get_key())
1592 }
1593}
1594
1595impl Hash for WindowIcon {
1596 fn hash<H>(&self, state: &mut H) where H: Hasher {
1597 self.get_key().hash(state);
1598 }
1599}
1600
1601#[derive(Debug, Clone)]
1603pub struct TaskBarIcon {
1604 pub key: IconKey,
1605 pub rgba_bytes: Vec<u8>,
1606}
1607
1608impl PartialEq for TaskBarIcon {
1609 fn eq(&self, rhs: &Self) -> bool {
1610 self.key == rhs.key
1611 }
1612}
1613
1614impl PartialOrd for TaskBarIcon {
1615 fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
1616 Some((self.key).cmp(&rhs.key))
1617 }
1618}
1619
1620impl Eq for TaskBarIcon { }
1621
1622impl Ord for TaskBarIcon {
1623 fn cmp(&self, rhs: &Self) -> Ordering {
1624 (self.key).cmp(&rhs.key)
1625 }
1626}
1627
1628impl Hash for TaskBarIcon {
1629 fn hash<H>(&self, state: &mut H) where H: Hasher {
1630 self.key.hash(state);
1631 }
1632}