yakui_winit/
lib.rs

1#![doc = include_str!("../README.md")]
2
3mod keys;
4
5use winit::dpi::PhysicalSize;
6use winit::event::{ElementState, MouseButton as WinitMouseButton, MouseScrollDelta, WindowEvent};
7use winit::window::Window;
8use yakui_core::event::Event;
9use yakui_core::geometry::{Rect, Vec2};
10use yakui_core::input::MouseButton;
11
12pub use self::keys::{from_winit_key, from_winit_modifiers};
13
14#[non_exhaustive]
15pub struct YakuiWinit {
16    auto_scale: bool,
17    auto_viewport: bool,
18    init: Option<InitState>,
19}
20
21struct InitState {
22    size: PhysicalSize<u32>,
23    scale: f32,
24}
25
26impl YakuiWinit {
27    #[allow(clippy::new_without_default)]
28    pub fn new(window: &Window) -> Self {
29        let size = window.inner_size();
30        let scale = window.scale_factor() as f32;
31
32        Self {
33            auto_scale: true,
34            auto_viewport: true,
35            init: Some(InitState { size, scale }),
36        }
37    }
38
39    /// Configure whether scale factor (ie DPI) should be automatically applied
40    /// from the window to scale the yakui UI.
41    ///
42    /// Defaults to `true`.
43    pub fn set_automatic_scale_factor(&mut self, enabled: bool) {
44        self.auto_scale = enabled;
45    }
46
47    /// Configure whether the viewport should be automatically updated to match
48    /// the window size.
49    ///
50    /// Defaults to `true`.
51    pub fn set_automatic_viewport(&mut self, enabled: bool) {
52        self.auto_viewport = enabled;
53    }
54
55    pub fn handle_window_event(
56        &mut self,
57        state: &mut yakui_core::Yakui,
58        event: &WindowEvent,
59    ) -> bool {
60        if let Some(init) = self.init.take() {
61            let size = Vec2::new(init.size.width as f32, init.size.height as f32);
62            state.set_surface_size(size);
63
64            if self.auto_viewport {
65                state.set_unscaled_viewport(Rect::from_pos_size(Vec2::ZERO, size));
66            }
67
68            if self.auto_scale {
69                state.set_scale_factor(init.scale);
70            }
71        }
72
73        match event {
74            WindowEvent::Resized(size) => {
75                let size = Vec2::new(size.width as f32, size.height as f32);
76                state.set_surface_size(size);
77
78                if self.auto_viewport {
79                    state.set_unscaled_viewport(Rect::from_pos_size(Vec2::ZERO, size));
80                }
81
82                false
83            }
84            WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
85                if self.auto_scale {
86                    state.set_scale_factor(*scale_factor as f32)
87                }
88
89                false
90            }
91            WindowEvent::CursorMoved { position, .. } => {
92                let pos = Vec2::new(position.x as f32, position.y as f32);
93                state.handle_event(Event::CursorMoved(Some(pos)))
94            }
95            WindowEvent::CursorLeft { .. } => state.handle_event(Event::CursorMoved(None)),
96
97            WindowEvent::MouseInput {
98                button,
99                state: button_state,
100                ..
101            } => {
102                let button = match button {
103                    WinitMouseButton::Left => MouseButton::One,
104                    WinitMouseButton::Right => MouseButton::Two,
105                    WinitMouseButton::Middle => MouseButton::Three,
106                    _ => return false,
107                };
108
109                let down = match button_state {
110                    ElementState::Pressed => true,
111                    ElementState::Released => false,
112                };
113
114                state.handle_event(Event::MouseButtonChanged { button, down })
115            }
116            WindowEvent::MouseWheel { delta, .. } => {
117                // Observed logical pixels per scroll wheel increment in Windows on Chrome
118                const LINE_HEIGHT: f32 = 100.0 / 3.0;
119
120                let delta = match *delta {
121                    MouseScrollDelta::LineDelta(x, y) => Vec2::new(x, y) * LINE_HEIGHT,
122                    MouseScrollDelta::PixelDelta(offset) => {
123                        Vec2::new(offset.x as f32, offset.y as f32)
124                            / state.layout_dom().scale_factor()
125                    }
126                };
127
128                // Flip delta axis from winit's expectations.
129                let delta = -delta;
130
131                state.handle_event(Event::MouseScroll { delta })
132            }
133            WindowEvent::ModifiersChanged(mods) => {
134                state.handle_event(Event::ModifiersChanged(from_winit_modifiers(mods.state())))
135            }
136            WindowEvent::KeyboardInput { event, .. } => {
137                if event.state == ElementState::Pressed {
138                    if let Some(text) = event.text.as_ref() {
139                        for c in text.chars() {
140                            state.handle_event(Event::TextInput(c));
141                        }
142                    }
143                }
144                let key = match event.physical_key {
145                    winit::keyboard::PhysicalKey::Code(k) => from_winit_key(k),
146                    winit::keyboard::PhysicalKey::Unidentified(_) => None,
147                };
148                if let Some(key) = key {
149                    let pressed = match event.state {
150                        ElementState::Pressed => true,
151                        ElementState::Released => false,
152                    };
153
154                    state.handle_event(Event::KeyChanged { key, down: pressed })
155                } else {
156                    false
157                }
158            }
159
160            WindowEvent::Ime(winit::event::Ime::Commit(text)) => {
161                for c in text.chars() {
162                    state.handle_event(Event::TextInput(c));
163                }
164                true
165            }
166            _ => false,
167        }
168    }
169}