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
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
use raui_core::{
    application::Application,
    interactive::{
        default_interactions_engine::{
            DefaultInteractionsEngine, DefaultInteractionsEngineResult, Interaction, PointerButton,
        },
        InteractionsEngine,
    },
    layout::CoordsMapping,
    widget::{
        component::interactive::navigation::{NavJump, NavScroll, NavSignal, NavTextChange},
        utils::Vec2,
    },
    Scalar,
};
use tetra::{
    input::{get_text_input, is_key_modifier_down, Key, KeyModifier, MouseButton},
    Context, Event,
};

#[derive(Debug)]
pub struct TetraInteractionsEngine {
    pub engine: DefaultInteractionsEngine,
    pub single_scroll_units: Vec2,
    pointer_position: Vec2,
}

impl Default for TetraInteractionsEngine {
    fn default() -> Self {
        Self::with_capacity(32, 32, 1024, 32, 32, 32, 32, 32)
    }
}

impl TetraInteractionsEngine {
    fn default_single_scroll_units() -> Vec2 {
        Vec2 { x: 10.0, y: 10.0 }
    }

    pub fn new() -> Self {
        Self {
            engine: Default::default(),
            single_scroll_units: Self::default_single_scroll_units(),
            pointer_position: Default::default(),
        }
    }

    #[allow(clippy::too_many_arguments)]
    pub fn with_capacity(
        resize_listeners: usize,
        relative_layout_listeners: usize,
        interactions_queue: usize,
        containers: usize,
        buttons: usize,
        text_inputs: usize,
        scroll_views: usize,
        selected_chain: usize,
    ) -> Self {
        Self {
            engine: DefaultInteractionsEngine::with_capacity(
                resize_listeners,
                relative_layout_listeners,
                interactions_queue,
                containers,
                buttons,
                text_inputs,
                scroll_views,
                selected_chain,
            ),
            single_scroll_units: Self::default_single_scroll_units(),
            pointer_position: Default::default(),
        }
    }

    pub fn update(&mut self, context: &Context) {
        if self.engine.focused_text_input().is_some() {
            if let Some(text) = get_text_input(context) {
                for c in text.chars() {
                    self.engine
                        .interact(Interaction::Navigate(NavSignal::TextChange(
                            NavTextChange::InsertCharacter(c),
                        )));
                }
            }
        }
    }

    pub fn event(&mut self, context: &Context, event: &Event, mapping: &CoordsMapping) {
        match event {
            Event::MouseMoved { position, .. } => {
                self.pointer_position = mapping.real_to_virtual_vec2(
                    Vec2 {
                        x: position.x,
                        y: position.y,
                    },
                    false,
                );
                self.engine
                    .interact(Interaction::PointerMove(self.pointer_position));
            }
            Event::MouseButtonPressed { button } => match button {
                MouseButton::Left => {
                    self.engine.interact(Interaction::PointerDown(
                        PointerButton::Trigger,
                        self.pointer_position,
                    ));
                }
                MouseButton::Right => {
                    self.engine.interact(Interaction::PointerDown(
                        PointerButton::Context,
                        self.pointer_position,
                    ));
                }
                _ => {}
            },
            Event::MouseButtonReleased { button } => match button {
                MouseButton::Left => {
                    self.engine.interact(Interaction::PointerUp(
                        PointerButton::Trigger,
                        self.pointer_position,
                    ));
                }
                MouseButton::Right => {
                    self.engine.interact(Interaction::PointerUp(
                        PointerButton::Context,
                        self.pointer_position,
                    ));
                }
                _ => {}
            },
            Event::MouseWheelMoved { amount } => {
                let value = Vec2 {
                    x: -self.single_scroll_units.x * amount.x as Scalar,
                    y: -self.single_scroll_units.y * amount.y as Scalar,
                };
                self.engine
                    .interact(Interaction::Navigate(NavSignal::Jump(NavJump::Scroll(
                        NavScroll::Units(value, true),
                    ))));
            }
            Event::KeyPressed { key } => {
                if self.engine.focused_text_input().is_some() {
                    match key {
                        Key::Left => {
                            self.engine
                                .interact(Interaction::Navigate(NavSignal::TextChange(
                                    NavTextChange::MoveCursorLeft,
                                )))
                        }
                        Key::Right => {
                            self.engine
                                .interact(Interaction::Navigate(NavSignal::TextChange(
                                    NavTextChange::MoveCursorRight,
                                )))
                        }
                        Key::Home => {
                            self.engine
                                .interact(Interaction::Navigate(NavSignal::TextChange(
                                    NavTextChange::MoveCursorStart,
                                )))
                        }
                        Key::End => {
                            self.engine
                                .interact(Interaction::Navigate(NavSignal::TextChange(
                                    NavTextChange::MoveCursorEnd,
                                )))
                        }
                        Key::Backspace => {
                            self.engine
                                .interact(Interaction::Navigate(NavSignal::TextChange(
                                    NavTextChange::DeleteLeft,
                                )))
                        }
                        Key::Delete => {
                            self.engine
                                .interact(Interaction::Navigate(NavSignal::TextChange(
                                    NavTextChange::DeleteRight,
                                )))
                        }
                        Key::Enter | Key::NumPadEnter => {
                            self.engine
                                .interact(Interaction::Navigate(NavSignal::TextChange(
                                    NavTextChange::NewLine,
                                )))
                        }
                        Key::Escape => {
                            self.engine
                                .interact(Interaction::Navigate(NavSignal::FocusTextInput(
                                    ().into(),
                                )));
                        }
                        _ => {}
                    }
                } else {
                    match key {
                        Key::Up | Key::W => {
                            self.engine.interact(Interaction::Navigate(NavSignal::Up))
                        }
                        Key::Down | Key::S => {
                            self.engine.interact(Interaction::Navigate(NavSignal::Down))
                        }
                        Key::Left | Key::A => {
                            if is_key_modifier_down(context, KeyModifier::Shift) {
                                self.engine.interact(Interaction::Navigate(NavSignal::Prev));
                            } else {
                                self.engine.interact(Interaction::Navigate(NavSignal::Left));
                            }
                        }
                        Key::Right | Key::D => {
                            if is_key_modifier_down(context, KeyModifier::Shift) {
                                self.engine.interact(Interaction::Navigate(NavSignal::Next));
                            } else {
                                self.engine
                                    .interact(Interaction::Navigate(NavSignal::Right));
                            }
                        }
                        Key::Enter | Key::NumPadEnter | Key::Space => {
                            self.engine
                                .interact(Interaction::Navigate(NavSignal::Accept(true)));
                        }
                        Key::Escape => {
                            self.engine
                                .interact(Interaction::Navigate(NavSignal::Cancel(true)));
                        }
                        _ => {}
                    }
                }
            }
            Event::KeyReleased { key } => {
                if self.engine.focused_text_input().is_none() {
                    match key {
                        Key::Enter | Key::NumPadEnter | Key::Space => {
                            self.engine
                                .interact(Interaction::Navigate(NavSignal::Accept(false)));
                        }
                        Key::Escape => {
                            self.engine
                                .interact(Interaction::Navigate(NavSignal::Cancel(false)));
                        }
                        _ => {}
                    }
                }
            }
            _ => {}
        }
    }
}

impl InteractionsEngine<DefaultInteractionsEngineResult, ()> for TetraInteractionsEngine {
    fn perform_interactions(
        &mut self,
        app: &mut Application,
    ) -> Result<DefaultInteractionsEngineResult, ()> {
        self.engine.perform_interactions(app)
    }
}