1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
use accesskit::Action;
use accesskit_winit::ActionRequestEvent;
use freya_common::EventMessage;
use freya_core::prelude::*;
use freya_elements::events::keyboard::{from_winit_to_code, get_modifiers, get_non_text_keys};
use torin::geometry::CursorPoint;
use winit::event::{
    ElementState, Event, KeyEvent, MouseScrollDelta, StartCause, Touch, TouchPhase, WindowEvent,
};
use winit::event_loop::{EventLoop, EventLoopProxy};
use winit::keyboard::{KeyCode, ModifiersState, PhysicalKey};

use crate::app::App;
use crate::HoveredNode;

// https://github.com/emilk/egui/issues/461
// https://github.com/rust-windowing/winit/issues/22
// https://github.com/flutter/flutter/issues/71385
const WHEEL_SPEED_MODIFIER: f32 = 53.0;

pub fn run_event_loop<State: Clone>(
    mut app: App<State>,
    event_loop: EventLoop<EventMessage>,
    proxy: EventLoopProxy<EventMessage>,
    hovered_node: HoveredNode,
) {
    let mut cursor_pos = CursorPoint::default();
    let mut modifiers_state = ModifiersState::empty();

    let window_env = app.window_env();

    window_env.run_on_setup();

    event_loop
        .run(move |event, event_loop| {
            match event {
                Event::NewEvents(StartCause::Init) => {
                    _ = proxy.send_event(EventMessage::PollVDOM);
                }
                Event::UserEvent(EventMessage::FocusAccessibilityNode(id)) => {
                    app.accessibility().set_accessibility_focus(id);
                }
                Event::UserEvent(EventMessage::RequestRerender) => {
                    app.window_env().window_mut().request_redraw();
                }
                Event::UserEvent(EventMessage::RequestRedraw) => app.render(&hovered_node),
                Event::UserEvent(EventMessage::RequestRelayout) => {
                    app.process_layout();
                }
                Event::UserEvent(EventMessage::RemeasureTextGroup(text_id)) => {
                    app.measure_text_group(&text_id);
                }
                Event::UserEvent(EventMessage::ActionRequestEvent(ActionRequestEvent {
                    request,
                    ..
                })) => {
                    if Action::Focus == request.action {
                        app.accessibility().set_accessibility_focus(request.target);
                    }
                }
                Event::UserEvent(EventMessage::SetCursorIcon(icon)) => {
                    app.window_env().window.set_cursor_icon(icon)
                }
                Event::UserEvent(ev) => {
                    if let EventMessage::UpdateTemplate(template) = ev {
                        app.vdom_replace_template(template);
                    }

                    if matches!(ev, EventMessage::PollVDOM)
                        || matches!(ev, EventMessage::UpdateTemplate(_))
                    {
                        app.poll_vdom();
                    }
                }
                Event::WindowEvent { event, .. } => {
                    app.process_accessibility_event(&event);
                    match event {
                        WindowEvent::CloseRequested => event_loop.exit(),
                        WindowEvent::RedrawRequested => {
                            app.process_layout();
                            app.render(&hovered_node);
                            app.tick();
                        }
                        //WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
                        WindowEvent::MouseInput { state, button, .. } => {
                            let event_name = match state {
                                ElementState::Pressed => "mousedown",
                                ElementState::Released => "click",
                            };

                            app.send_event(FreyaEvent::Mouse {
                                name: event_name.to_string(),
                                cursor: cursor_pos,
                                button: Some(button),
                            });
                        }
                        WindowEvent::MouseWheel { delta, phase, .. } => {
                            if TouchPhase::Moved == phase {
                                let scroll_data = {
                                    match delta {
                                        MouseScrollDelta::LineDelta(x, y) => (
                                            (x * WHEEL_SPEED_MODIFIER) as f64,
                                            (y * WHEEL_SPEED_MODIFIER) as f64,
                                        ),
                                        MouseScrollDelta::PixelDelta(pos) => (pos.x, pos.y),
                                    }
                                };

                                app.send_event(FreyaEvent::Wheel {
                                    name: "wheel".to_string(),
                                    scroll: CursorPoint::from(scroll_data),
                                    cursor: cursor_pos,
                                });
                            }
                        }
                        WindowEvent::ModifiersChanged(modifiers) => {
                            modifiers_state = modifiers.state();
                        }
                        WindowEvent::KeyboardInput {
                            event:
                                KeyEvent {
                                    physical_key,
                                    logical_key,
                                    state,
                                    ..
                                },
                            ..
                        } => {
                            if state == ElementState::Pressed
                                && physical_key == PhysicalKey::Code(KeyCode::Tab)
                            {
                                let direction = if modifiers_state.shift_key() {
                                    AccessibilityFocusDirection::Backward
                                } else {
                                    AccessibilityFocusDirection::Forward
                                };

                                app.focus_next_node(direction);

                                return;
                            }

                            let event_name = match state {
                                ElementState::Pressed => "keydown",
                                ElementState::Released => "keyup",
                            };
                            app.send_event(FreyaEvent::Keyboard {
                                name: event_name.to_string(),
                                key: get_non_text_keys(&logical_key),
                                code: from_winit_to_code(&physical_key),
                                modifiers: get_modifiers(modifiers_state),
                            })
                        }
                        WindowEvent::CursorMoved { position, .. } => {
                            cursor_pos = CursorPoint::from((position.x, position.y));

                            app.send_event(FreyaEvent::Mouse {
                                name: "mouseover".to_string(),
                                cursor: cursor_pos,
                                button: None,
                            });
                        }
                        WindowEvent::Touch(Touch {
                            location,
                            phase,
                            id,
                            force,
                            ..
                        }) => {
                            cursor_pos = CursorPoint::from((location.x, location.y));

                            let event_name = match phase {
                                TouchPhase::Cancelled => "touchcancel",
                                TouchPhase::Ended => "touchend",
                                TouchPhase::Moved => "touchmove",
                                TouchPhase::Started => "touchstart",
                            };

                            app.send_event(FreyaEvent::Touch {
                                name: event_name.to_string(),
                                location: cursor_pos,
                                finger_id: id,
                                phase,
                                force,
                            });
                        }
                        WindowEvent::Resized(size) => {
                            app.resize(size);
                        }
                        _ => {}
                    }
                }
                Event::LoopExiting => {
                    app.window_env().run_on_exit();
                }
                _ => (),
            }
        })
        .expect("Failed to run Eventloop.");
}