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 pub fn set_automatic_scale_factor(&mut self, enabled: bool) {
44 self.auto_scale = enabled;
45 }
46
47 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 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 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}