azul_core/
window.rs

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/// Each default callback is identified by its ID (not by it's function pointer),
39/// since multiple IDs could point to the same function.
40#[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/// Key that is used for checking whether a window icon has changed -
52/// this way azul doesn't need to diff the actual bytes, just the icon key.
53/// Use `IconKey::new()` to generate a new, unique key
54#[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
108/// Hardware-dependent keyboard scan code.
109pub type ScanCode = u32;
110
111/// Determines which keys are pressed currently (modifiers, etc.)
112#[derive(Default, Debug, Clone, PartialEq)]
113pub struct KeyboardState {
114    /// Tracks, if the `Shift` key is currently pressed - (READONLY)
115    pub shift_down: bool,
116    /// Tracks, if the `Ctrl` key is currently pressed - (READONLY)
117    pub ctrl_down: bool,
118    /// Tracks, if the `Alt` key is currently pressed - (READONLY)
119    pub alt_down: bool,
120    /// Tracks, if the `Super / Windows / Command` key is currently pressed - (READONLY)
121    pub super_down: bool,
122    /// Currently pressed key, already converted to a `char` - (READONLY)
123    pub current_char: Option<char>,
124    /// Same as `current_char`, but .
125    ///
126    /// **DO NOT USE THIS FOR TEXT INPUT, USE `current_char` and `On::TextInput` instead.**
127    /// For example entering `à` will fire a `VirtualKeyCode::Grave`, then `VirtualKeyCode::A`,
128    /// so to correctly combine characters, use the `current_char` field.
129    pub current_virtual_keycode: Option<VirtualKeyCode>,
130    /// Currently pressed virtual keycodes (READONLY) - it can happen that more t
131    ///
132    /// This is essentially an "extension" of `current_scancodes` - `current_keys` stores the characters, but what if the
133    /// pressed key is not a character (such as `ArrowRight` or `PgUp`)?
134    ///
135    /// Note that this can have an overlap, so pressing "a" on the keyboard will insert
136    /// both a `VirtualKeyCode::A` into `current_virtual_keycodes` and an `"a"` as a char into `current_keys`.
137    pub pressed_virtual_keycodes: HashSet<VirtualKeyCode>,
138    /// Same as `current_virtual_keycodes`, but the scancode identifies the physical key pressed,
139    /// independent of the keyboard layout. The scancode does not change if the user adjusts the host's keyboard map.
140    /// Use when the physical location of the key is more important than the key's host GUI semantics,
141    /// such as for movement controls in a first-person game (German keyboard: Z key, UK keyboard: Y key, etc.)
142    pub pressed_scancodes: HashSet<ScanCode>,
143}
144
145/// Mouse position, cursor type, user scroll input, etc.
146#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
147pub struct MouseState {
148    /// Current mouse cursor type, set to `None` if the cursor is hidden. (READWRITE)
149    pub mouse_cursor_type: Option<MouseCursorType>,
150    /// Where is the mouse cursor currently? Set to `None` if the window is not focused. (READWRITE)
151    pub cursor_position: CursorPosition,
152    /// Is the mouse cursor locked to the current window (important for applications like games)? (READWRITE)
153    pub is_cursor_locked: bool,
154    /// Is the left mouse button down? (READONLY)
155    pub left_down: bool,
156    /// Is the right mouse button down? (READONLY)
157    pub right_down: bool,
158    /// Is the middle mouse button down? (READONLY)
159    pub middle_down: bool,
160    /// Scroll amount in pixels in the horizontal direction. Gets reset to 0 after every frame (READONLY)
161    pub scroll_x: Option<f32>,
162    /// Scroll amount in pixels in the vertical direction. Gets reset to 0 after every frame (READONLY)
163    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    /// Returns whether any mouse button (left, right or center) is currently held down
184    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/// Toggles webrender debug flags (will make stuff appear on
224/// the screen that you might not want to - used for debugging purposes)
225#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
226pub struct DebugState {
227    /// Toggles `webrender::DebugFlags::PROFILER_DBG`
228    pub profiler_dbg: bool,
229    /// Toggles `webrender::DebugFlags::RENDER_TARGET_DBG`
230    pub render_target_dbg: bool,
231    /// Toggles `webrender::DebugFlags::TEXTURE_CACHE_DBG`
232    pub texture_cache_dbg: bool,
233    /// Toggles `webrender::DebugFlags::GPU_TIME_QUERIES`
234    pub gpu_time_queries: bool,
235    /// Toggles `webrender::DebugFlags::GPU_SAMPLE_QUERIES`
236    pub gpu_sample_queries: bool,
237    /// Toggles `webrender::DebugFlags::DISABLE_BATCHING`
238    pub disable_batching: bool,
239    /// Toggles `webrender::DebugFlags::EPOCHS`
240    pub epochs: bool,
241    /// Toggles `webrender::DebugFlags::COMPACT_PROFILER`
242    pub compact_profiler: bool,
243    /// Toggles `webrender::DebugFlags::ECHO_DRIVER_MESSAGES`
244    pub echo_driver_messages: bool,
245    /// Toggles `webrender::DebugFlags::NEW_FRAME_INDICATOR`
246    pub new_frame_indicator: bool,
247    /// Toggles `webrender::DebugFlags::NEW_SCENE_INDICATOR`
248    pub new_scene_indicator: bool,
249    /// Toggles `webrender::DebugFlags::SHOW_OVERDRAW`
250    pub show_overdraw: bool,
251    /// Toggles `webrender::DebugFlagsFGPU_CACHE_DBG`
252    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    /// Set the scroll amount - does not update the `entry.used_this_frame`,
271    /// since that is only relevant when we are actually querying the renderer.
272    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    /// NOTE: This has to be a getter, because we need to update
279    #[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    /// Updating (add to) the existing scroll amount does not update the `entry.used_this_frame`,
286    /// since that is only relevant when we are actually querying the renderer.
287    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    /// Removes all scroll states that weren't used in the last frame
294    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    /// Amount in pixel that the current node is scrolled
302    pub scroll_position: LayoutPoint,
303    /// Was the scroll amount used in this frame?
304    pub used_this_frame: bool,
305}
306
307impl ScrollState {
308
309    /// Return the current position of the scroll state
310    pub fn get(&self) -> LayoutPoint {
311        self.scroll_position
312    }
313
314    /// Add a scroll X / Y onto the existing scroll state
315    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    /// Set the scroll state to a new position
321    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    /// Returns the scroll position and also set the "used_this_frame" flag
327    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
342/// Overwrites all fields of the `FullWindowState` with the fields of the `WindowState`,
343/// but leaves the extra fields such as `.hover_nodes` untouched
344pub 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
359/// Resets the mouse states `scroll_x` and `scroll_y` to 0
360pub 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    /// A "document" in WebRender usually corresponds to one tab (i.e. in Azuls case, the whole window).
367    pub document_id: DocumentId,
368    /// One "document" (tab) can have multiple "pipelines" (important for hit-testing).
369    ///
370    /// A document can have multiple pipelines, for example in Firefox the tab / navigation bar,
371    /// the actual browser window and the inspector are seperate pipelines, but contained in one document.
372    /// In Azul, one pipeline = one document (this could be improved later on).
373    pub pipeline_id: PipelineId,
374    /// The "epoch" is a frame counter, to remove outdated images, fonts and OpenGL textures
375    /// when they're not in use anymore.
376    pub epoch: Epoch,
377    /// Current display list active in this window (useful for debugging)
378    pub cached_display_list: CachedDisplayList,
379    /// Currently active, layouted rectangles
380    pub layout_result: SolvedLayoutCache,
381    /// Currently GL textures inside the active CachedDisplayList
382    pub gl_texture_cache: GlTextureCache,
383    /// Current scroll states of nodes (x and y position of where they are scrolled)
384    pub scrolled_nodes: BTreeMap<DomId, ScrolledNodes>,
385    /// States of scrolling animations, updated every frame
386    pub scroll_states: ScrollStates,
387}
388
389impl WindowInternal {
390
391    /// Returns a copy of the current scroll states + scroll positions
392    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/// State, size, etc of the window, for comparing to the last frame
417#[derive(Debug, Clone, PartialEq)]
418pub struct WindowState {
419    /// Current title of the window
420    pub title: String,
421    /// Size of the window + max width / max height: 800 x 600 by default
422    pub size: WindowSize,
423    /// The x and y position, or None to let the WM decide where to put the window (default)
424    pub position: Option<PhysicalPosition<u32>>,
425    /// Flags such as whether the window is minimized / maximized, fullscreen, etc.
426    pub flags: WindowFlags,
427    /// Mostly used for debugging, shows WebRender-builtin graphs on the screen.
428    /// Used for performance monitoring and displaying frame times (rendering-only).
429    pub debug_state: DebugState,
430    /// Current keyboard state - NOTE: mutating this field (currently) does nothing
431    /// (doesn't get synchronized with OS-level window)!
432    pub keyboard_state: KeyboardState,
433    /// Current mouse state
434    pub mouse_state: MouseState,
435    /// Sets location of IME candidate box in client area coordinates
436    /// relative to the top left of the window.
437    pub ime_position: Option<LogicalPosition>,
438    /// Window options that can only be set on a certain platform
439    /// (`WindowsWindowOptions` / `LinuxWindowOptions` / `MacWindowOptions`).
440    pub platform_specific_options: PlatformSpecificOptions,
441    /// The style of this window
442    pub css: Css,
443}
444
445#[derive(Debug, Clone, PartialEq)]
446pub struct FullWindowState {
447    /// Current title of the window
448    pub title: String,
449    /// Size of the window + max width / max height: 800 x 600 by default
450    pub size: WindowSize,
451    /// The x and y position, or None to let the WM decide where to put the window (default)
452    pub position: Option<PhysicalPosition<u32>>,
453    /// Flags such as whether the window is minimized / maximized, fullscreen, etc.
454    pub flags: WindowFlags,
455    /// Mostly used for debugging, shows WebRender-builtin graphs on the screen.
456    /// Used for performance monitoring and displaying frame times (rendering-only).
457    pub debug_state: DebugState,
458    /// Current keyboard state - NOTE: mutating this field (currently) does nothing
459    /// (doesn't get synchronized with OS-level window)!
460    pub keyboard_state: KeyboardState,
461    /// Current mouse state
462    pub mouse_state: MouseState,
463    /// Sets location of IME candidate box in client area coordinates
464    /// relative to the top left of the window.
465    pub ime_position: Option<LogicalPosition>,
466    /// Window options that can only be set on a certain platform
467    /// (`WindowsWindowOptions` / `LinuxWindowOptions` / `MacWindowOptions`).
468    pub platform_specific_options: PlatformSpecificOptions,
469    /// The style of this window
470    pub css: Css,
471
472    // --
473
474    /// Previous window state, used for determining mouseout, etc. events
475    pub previous_window_state: Option<Box<FullWindowState>>,
476    /// Whether there is a file currently hovering over the window
477    pub hovered_file: Option<PathBuf>,
478    /// Whether there was a file currently dropped on the window
479    pub dropped_file: Option<PathBuf>,
480    /// What node is currently hovered over, default to None. Only necessary internal
481    /// to the crate, for emitting `On::FocusReceived` and `On::FocusLost` events,
482    /// as well as styling `:focus` elements
483    pub focused_node: Option<(DomId, NodeId)>,
484    /// Currently hovered nodes, default to an empty Vec. Important for
485    /// styling `:hover` elements.
486    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            // --
504
505            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    /// Returns the window state of the previous frame, useful for calculating
533    /// metrics for dragging motions. Note that you can't call this function
534    /// recursively - calling `get_previous_window_state()` on the returned
535    /// `WindowState` will yield a `None` value.
536    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    /// Creates a FullWindowState from a regular WindowState, fills non-available
543    /// fields with their default values
544    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    /// Whether the UI should be rendered anyways due to a (programmatic or user input) scroll event
584    pub should_scroll_render: bool,
585    /// Whether the callbacks say to rebuild the UI or not
586    pub callbacks_update_screen: UpdateScreen,
587    /// WindowState that was (potentially) modified in the callbacks
588    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    /// Is the window currently maximized
613    pub is_maximized: bool,
614    /// Is the window currently fullscreened?
615    pub is_fullscreen: bool,
616    /// Does the window have decorations (close, minimize, maximize, title bar)?
617    pub has_decorations: bool,
618    /// Is the window currently visible?
619    pub is_visible: bool,
620    /// Is the window always on top?
621    pub is_always_on_top: bool,
622    /// Whether the window is resizable
623    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    // empty for now
652}
653
654#[cfg(target_os = "windows")]
655#[derive(Debug, Default, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
656pub struct WindowsWindowOptions {
657    /// STARTUP ONLY: Sets `WS_EX_NOREDIRECTIONBITMAP`
658    pub no_redirection_bitmap: bool,
659    /// STARTUP ONLY: Window icon (decoded bytes), appears at the top right corner of the window
660    pub window_icon: Option<WindowIcon>,
661    /// READWRITE: Taskbar icon (decoded bytes), usually 256x256x4 bytes large (`ICON_BIG`).
662    ///
663    /// Can be changed in callbacks / at runtime.
664    pub taskbar_icon: Option<TaskBarIcon>,
665    /// STARTUP ONLY: Pointer (casted to void pointer) to a HWND handle
666    pub parent_window: Option<*mut c_void>,
667}
668
669/// X window type. Maps directly to
670/// [`_NET_WM_WINDOW_TYPE`](https://specifications.freedesktop.org/wm-spec/wm-spec-1.5.html).
671#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
672pub enum XWindowType {
673    /// A desktop feature. This can include a single window containing desktop icons with the same dimensions as the
674    /// screen, allowing the desktop environment to have full control of the desktop, without the need for proxying
675    /// root window clicks.
676    Desktop,
677    /// A dock or panel feature. Typically a Window Manager would keep such windows on top of all other windows.
678    Dock,
679    /// Toolbar windows. "Torn off" from the main application.
680    Toolbar,
681    /// Pinnable menu windows. "Torn off" from the main application.
682    Menu,
683    /// A small persistent utility window, such as a palette or toolbox.
684    Utility,
685    /// The window is a splash screen displayed as an application is starting up.
686    Splash,
687    /// This is a dialog window.
688    Dialog,
689    /// A dropdown menu that usually appears when the user clicks on an item in a menu bar.
690    /// This property is typically used on override-redirect windows.
691    DropdownMenu,
692    /// A popup menu that usually appears when the user right clicks on an object.
693    /// This property is typically used on override-redirect windows.
694    PopupMenu,
695    /// A tooltip window. Usually used to show additional information when hovering over an object with the cursor.
696    /// This property is typically used on override-redirect windows.
697    Tooltip,
698    /// The window is a notification.
699    /// This property is typically used on override-redirect windows.
700    Notification,
701    /// This should be used on the windows that are popped up by combo boxes.
702    /// This property is typically used on override-redirect windows.
703    Combo,
704    /// This indicates the the window is being dragged.
705    /// This property is typically used on override-redirect windows.
706    Dnd,
707    /// This is a normal, top-level window.
708    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    /// (Unimplemented) - Can only be set at window creation, can't be changed in callbacks.
721    pub x11_visual: Option<*const ()>,
722    /// (Unimplemented) - Can only be set at window creation, can't be changed in callbacks.
723    pub x11_screen: Option<i32>,
724    /// Build window with `WM_CLASS` hint; defaults to the name of the binary. Only relevant on X11.
725    /// Can only be set at window creation, can't be changed in callbacks.
726    pub x11_wm_classes: Vec<(String, String)>,
727    /// Build window with override-redirect flag; defaults to false. Only relevant on X11.
728    /// Can only be set at window creation, can't be changed in callbacks.
729    pub x11_override_redirect: bool,
730    /// Build window with `_NET_WM_WINDOW_TYPE` hint; defaults to `Normal`. Only relevant on X11.
731    /// Can only be set at window creation, can't be changed in callbacks.
732    pub x11_window_types: Vec<XWindowType>,
733    /// Build window with `_GTK_THEME_VARIANT` hint set to the specified value. Currently only relevant on X11.
734    /// Can only be set at window creation, can't be changed in callbacks.
735    pub x11_gtk_theme_variant: Option<String>,
736    /// Build window with resize increment hint. Only implemented on X11.
737    /// Can only be set at window creation, can't be changed in callbacks.
738    pub x11_resize_increments: Option<LogicalSize>,
739    /// Build window with base size hint. Only implemented on X11.
740    /// Can only be set at window creation, can't be changed in callbacks.
741    pub x11_base_size: Option<LogicalSize>,
742    /// Build window with a given application ID. It should match the `.desktop` file distributed with
743    /// your program. Only relevant on Wayland.
744    /// Can only be set at window creation, can't be changed in callbacks.
745    ///
746    /// For details about application ID conventions, see the
747    /// [Desktop Entry Spec](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#desktop-file-id)
748    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    /// Creates a new, default `WindowState` with the given CSS style
763    pub fn new(css: Css) -> Self { Self { css, .. Default::default() } }
764
765    /// Same as `WindowState::new` but to be used as a builder method
766    pub fn with_css(self, css: Css) -> Self { Self { css, .. self } }
767
768    /// Returns the current keyboard keyboard state. We don't want the library
769    /// user to be able to modify this state, only to read it.
770    pub fn get_mouse_state(&self) -> &MouseState {
771        &self.mouse_state
772    }
773
774    /// Returns the current windows mouse state. We don't want the library
775    /// user to be able to modify this state, only to read it.
776    pub fn get_keyboard_state(&self) -> &KeyboardState {
777        &self.keyboard_state
778    }
779
780    /// Returns the physical (width, height) in pixel of this window
781    pub fn get_physical_size(&self) -> (usize, usize) {
782        (self.size.dimensions.width as usize, self.size.dimensions.height as usize)
783    }
784
785    /// Returns the current HiDPI factor for this window.
786    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    /// - macOS: If the window is in windowed mode, transitions it slowly to fullscreen mode
794    /// - other: Does the same as `FastFullScreen`.
795    SlowFullScreen,
796    /// Window should immediately go into fullscreen mode (on macOS this is not the default behaviour).
797    FastFullScreen,
798    /// - macOS: If the window is in fullscreen mode, transitions slowly back to windowed state.
799    /// - other: Does the same as `FastWindowed`.
800    SlowWindowed,
801    /// If the window is in fullscreen mode, will immediately go back to windowed mode (on macOS this is not the default behaviour).
802    FastWindowed,
803}
804
805#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
806pub struct WaylandTheme {
807    /// Primary color when the window is focused
808    pub primary_active: [u8; 4],
809    /// Primary color when the window is unfocused
810    pub primary_inactive: [u8; 4],
811    /// Secondary color when the window is focused
812    pub secondary_active: [u8; 4],
813    /// Secondary color when the window is unfocused
814    pub secondary_inactive: [u8; 4],
815    /// Close button color when hovered over
816    pub close_button_hovered: [u8; 4],
817    /// Close button color
818    pub close_button: [u8; 4],
819    /// Close button color when hovered over
820    pub maximize_button_hovered: [u8; 4],
821    /// Maximize button color
822    pub maximize_button: [u8; 4],
823    /// Minimize button color when hovered over
824    pub minimize_button_hovered: [u8; 4],
825    /// Minimize button color
826    pub minimize_button: [u8; 4],
827}
828
829#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
830pub struct WindowSize {
831    /// Width and height of the window, in logical
832    /// units (may not correspond to the physical on-screen size)
833    pub dimensions: LogicalSize,
834    /// DPI factor of the window
835    pub hidpi_factor: f32,
836    /// (Internal only, unused): winit HiDPI factor
837    pub winit_hidpi_factor: f32,
838    /// Minimum dimensions of the window
839    pub min_dimensions: Option<LogicalSize>,
840    /// Maximum dimensions of the window
841    pub max_dimensions: Option<LogicalSize>,
842}
843
844impl WindowSize {
845
846    /// Get the actual logical size
847    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    /// Get a size that is usually smaller than the logical one, so that the winit DPI factor is compensated for.
856    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
893/// Options on how to initially create the window
894pub struct WindowCreateOptions {
895    /// State of the window, set the initial title / width / height here.
896    pub state: WindowState,
897    // /// Which monitor should the window be created on?
898    // pub monitor: Monitor,
899    /// Renderer type: Hardware-with-software-fallback, pure software or pure hardware renderer?
900    pub renderer_type: RendererType,
901    #[cfg(debug_assertions)]
902    #[cfg(not(test))]
903    /// An optional style hot-reloader for the current window, only available with debug_assertions
904    /// enabled
905    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    /// Reloads the CSS (if possible).
974    ///
975    /// Returns:
976    ///
977    /// - Ok(css) if the CSS has been successfully reloaded
978    /// - Err(why) if the CSS failed to hot-reload.
979    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/// Force a specific renderer.
993/// By default, Azul will try to use the hardware renderer and fall
994/// back to the software renderer if it can't create an OpenGL 3.2 context.
995/// However, in some cases a hardware renderer might create problems
996/// or you want to force either a software or hardware renderer.
997///
998/// If the field `renderer_type` on the `WindowCreateOptions` is not
999/// `RendererType::Default`, the `create_window` method will try to create
1000/// a window with the specific renderer type and **crash** if the renderer is
1001/// not available for whatever reason.
1002///
1003/// If you don't know what any of this means, leave it at `Default`.
1004#[cfg_attr(not(feature = "opengl"), derive(Copy))]
1005pub enum RendererType {
1006    /// Use the hardware renderer first, then fall back to OSMesa
1007    Default,
1008    /// Force hardware rendering
1009    ForceHardware,
1010    /// Force software rendering
1011    ForceSoftware,
1012    /// Render using a custom OpenGL implementation
1013    #[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
1095/// Custom event type, to construct an `EventLoop<AzulWindowUpdateEvent>`.
1096/// This is dispatched into the `EventLoop` (to send a "custom" event)
1097pub 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    // ... etc.
1133}
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    /// A BTreeMap where each item is already filtered by the proper hit-testing type,
1221    /// meaning in order to get the proper callbacks, you simply have to iterate through
1222    /// all node IDs
1223    pub nodes_with_callbacks: BTreeMap<NodeId, DetermineCallbackResult>,
1224    /// Same as `needs_redraw_anyways`, but for reusing the layout from the previous frame.
1225    /// Each `:hover` and `:active` group stores whether it modifies the layout, as
1226    /// a performance optimization.
1227    pub needs_relayout_anyways: bool,
1228    /// Whether the screen should be redrawn even if no Callback returns an `UpdateScreen::Redraw`.
1229    /// This is necessary for `:hover` and `:active` mouseovers - otherwise the screen would
1230    /// only update on the next resize.
1231    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    /// Returns whether there is any
1261    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/// Utility function for easier creation of a keymap - i.e. `[vec![Ctrl, S], my_function]`
1366#[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    /// Checks if the current keyboard state contains the given char or modifier,
1377    /// i.e. if the keyboard state currently has the shift key pressed and the
1378    /// accelerator key is `Shift`, evaluates to true, otherwise to false.
1379    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/// Symbolic name for a keyboard key, does NOT take the keyboard locale into account
1391#[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// Window icon that usually appears in the top-left corner of the window
1557#[derive(Debug, Clone)]
1558pub enum WindowIcon {
1559    /// 16x16x4 bytes icon
1560    Small { key: IconKey, rgba_bytes: Vec<u8> },
1561    /// 32x32x4 bytes icon
1562    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}
1573// -- Only compare the IconKey (for WindowIcon and TaskBarIcon)
1574
1575impl 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/// 256x256x4 bytes window icon
1602#[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}