API Documentation
Loading...
Searching...
No Matches
NDEVRMouseManager.h
Go to the documentation of this file.
1/*--------------------------------------------------------------------------------------------
2Copyright (c) 2019, NDEVR LLC
3tyler.parke@ndevr.org
4 __ __ ____ _____ __ __ _______
5 | \ | | | __ \ | ___|\ \ / / | __ \
6 | \ | | | | \ \ | |___ \ \ / / | |__) |
7 | . \| | | |__/ / | |___ \ V / | _ /
8 | |\ |_|_____/__|_____|___\_/____| | \ \
9 |__| \__________________________________| \__\
10
11Subject to the terms of the Enterprise+ Agreement, NDEVR hereby grants
12Licensee a limited, non-exclusive, non-transferable, royalty-free license
13(without the right to sublicense) to use the API solely for the purpose of
14Licensee's internal development efforts to develop applications for which
15the API was provided.
16
17The above copyright notice and this permission notice shall be included in all
18copies or substantial portions of the Software.
19
20THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
21INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
22PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
23FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
24OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25DEALINGS IN THE SOFTWARE.
26
27Library: NDEVR
28File: NDEVRMouseManager
29Included in API: True
30Author(s): Tyler Parke
31 *-----------------------------------------------------------------------------------------**/
32#pragma once
33#include <NDEVR/WidgetOptions.h>
34#include <NDEVR/WindowInstance.h>
35#include <NDEVR/Thread.h>
36#include <NDEVR/TimeSpan.h>
37#include <QMouseEvent>
38#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
39#define position pos
40#define points touchPoints
41#endif
42#if NDEVR_SUPPORTS_THREADING
43#define RUN_MOUSE_MANAGER_THREADED 1
44#else
45#define RUN_MOUSE_MANAGER_THREADED 0
46#endif
47#if NDEVR_VIEWPORT
48namespace NDEVR
49{
50 class NDEVRMouseManager
51#if NDEVR_SUPPORTS_THREADING
52 : public Thread
53#endif
54 {
55 public:
56 NDEVRMouseManager()
57 : m_is_init(false)
58 , m_use_mouse_clicks(true)
59 , m_last_touch_scale_factor(Constant<fltp08>::NaN)
60 , m_last_mouse_press_time(0)
61 {
62#if NDEVR_SUPPORTS_THREADING
63 setThreadName("Mouse Manager");
64#endif
65 }
66 static NDEVRMouseManager& defaultInstance()
67 {
68 static NDEVRMouseManager manager;
69 if (!manager.m_is_init)
70 {
71#if RUN_MOUSE_MANAGER_THREADED
72 manager.start();
73#endif
74 manager.m_is_init = true;
75 }
76 return manager;
77 }
78 void setUseMouseClicks(bool use_mouse_clicks)
79 {
80 m_use_mouse_clicks = use_mouse_clicks;
81 }
82 void processMouseEvent()
83 {
84 if (!m_last_mouse_events.isEmpty())
85 {
86 while (!m_last_mouse_events.isEmpty())
87 {
88 MouseEvent event;
89 {
90#if RUN_MOUSE_MANAGER_THREADED
91 std::unique_lock<std::mutex> crit_lock(m_critical_section);
92#endif
93 event = m_last_mouse_events[0];
94 m_last_mouse_events.removeIndex(0);
95 }
96 if(event.window_instance)
97 event.window_instance->createEvent(event);
98 }
99#if RUN_MOUSE_MANAGER_THREADED
100 ThreadFunctions::RequestSleep(TimeSpan(0.01));
101#endif
102 }
103 }
104 void eraseOldestEvent()
105 {
106#if RUN_MOUSE_MANAGER_THREADED
107 std::unique_lock<std::mutex> crit_lock(m_critical_section);
108#endif
109 if (!m_last_mouse_events.isEmpty())
110 m_last_mouse_events.removeIndex(0);
111 }
112 MouseEvent oldestEvent() const
113 {
114#if RUN_MOUSE_MANAGER_THREADED
115 std::unique_lock<std::mutex> crit_lock(m_critical_section);
116#endif
117 return m_last_mouse_events.get(0);
118 }
119 void clearEvents(const WindowInstance* instance)
120 {
121#if RUN_MOUSE_MANAGER_THREADED
122 std::unique_lock<std::mutex> crit_lock(m_critical_section);
123#endif
124 for (uint04 i = m_last_mouse_events.size() - 1; !isNaN(i); --i)
125 {
126 if (m_last_mouse_events[i].window_instance == instance)
127 m_last_mouse_events.removeIndex(i);
128 }
129 }
130
131#if RUN_MOUSE_MANAGER_THREADED
132 void stopThread() override
133 {
134 {
135 std::unique_lock<std::mutex> crit_lock(m_critical_section);
136 m_last_mouse_events.clear();
137 m_is_running = false;
138 ConcurrentOperation::notifyAll(&m_critical_section);
139 }
140 Thread::stopThread();
141 }
142#endif
143
144#if RUN_MOUSE_MANAGER_THREADED
145 virtual void run() override
146 {
147 while (m_is_running)
148 {
149 processMouseEvent();
150 std::unique_lock<std::mutex> crit_lock(m_critical_section);
151 while(m_is_running && m_last_mouse_events.size() == 0)
152 ConcurrentOperation::wait(&m_critical_section, 100000000, crit_lock);
153 }
154 }
155#endif
156 void addTouchEvent(Buffer<Vector<2, fltp04>> location, Vector<2, uint04> size, bool touch_pressed, WindowInstance* instance = nullptr)
157 {
158 if (location.size() == 0)
159 {
160 MouseEvent mouse_event(instance);
161 mouse_event.click_type = MouseEvent::e_left_button;
162 mouse_event.event_type = MouseEvent::e_mouse_exited;
163 addMouseEvent(mouse_event);
164 }
165 if (location.size() == 1)
166 {
167 MouseEvent mouse_event(instance);
168 mouse_event.click_type = MouseEvent::e_left_button;
169 mouse_event.event_location = location[A];
170 mouse_event.event_type = MouseEvent::e_mouse_dragged;
171 addMouseEvent(mouse_event);
172 }
173 if (location.size() == 2)
174 {
175 fltp04 scale_factor = distance<fltp04>(location[A], location[B]);
176
177 //m_last_mouse_press_point
178 if (touch_pressed)
179 {
180 // if one of the fingers is released, remember the current scale
181 // factor so that adding another finger later will continue zooming
182 // by adding new scale factor to the existing remembered value.
183 m_last_touch_press_point[0] = location[A];
184 m_last_touch_press_point[1] = location[B];
185 m_last_touch_scale_factor = scale_factor;
186 /*MouseEvent mouse_event(instance);
187 mouse_event.event_location = (location[A] + location[B]) / 2.0f;
188 mouse_event.number_of_clicks = 1;
189 mouse_event.click_type = MouseEvent::e_right_button;
190 mouse_event.event_type = MouseEvent::e_mouse_pressed;
191 addMouseEvent(mouse_event);*/
192
193 }
194 else
195 {
196 if (isNaN(m_last_touch_press_point))
197 {
198 m_last_touch_press_point[0] = location[A];
199 m_last_touch_press_point[1] = location[B];
200 m_last_touch_scale_factor = scale_factor;
201 }
202 else
203 {
204 MouseEvent mouse_event(instance);
205 mouse_event.event_location = (location[A] + location[B]) / 2.0f;
206 mouse_event.number_of_clicks = 1;
207 /*{
208
209 mouse_event.event_type = MouseEvent::e_mouse_dragged;
210 mouse_event.click_type = MouseEvent::e_right_button;
211 addMouseEvent(mouse_event);
212 }*/
213 {
214 mouse_event.event_type = MouseEvent::e_touch_zoomed;
215 mouse_event.distance_scrolled[X] = 0.0f;
216 mouse_event.distance_scrolled[Y] = 2000.0 * (scale_factor - m_last_touch_scale_factor) / size.magnitude<fltp08>();
217 mouse_event.click_type = MouseEvent::e_center_button;
218 m_last_touch_scale_factor = scale_factor;
219 addMouseEvent(mouse_event);
220 }
221 }
222
223 }
224 }
225 }
226
227 void clearTouchEvent(WindowInstance* instance)
228 {
229 m_last_touch_scale_factor = Constant<fltp08>::NaN;
230 if (!isNaN(m_last_touch_press_point[0]))
231 {
232 MouseEvent mouse_event(instance);
233 mouse_event.event_location = (m_last_touch_press_point[0], m_last_touch_press_point[1]) / 2.0f;
234 mouse_event.number_of_clicks = 1;
235 mouse_event.click_type = MouseEvent::e_right_button;
236 mouse_event.event_type = MouseEvent::e_mouse_released;
237
238 m_last_touch_press_point[0] = Constant<fltp04>::NaN;
239 m_last_touch_press_point[1] = Constant<fltp04>::NaN;
240 addMouseEvent(mouse_event);
241 }
242 }
243 bool addMouseEvent(QEvent* event, Vector<2, uint04> size, WindowInstance* instance = nullptr)
244 {
245 QEvent::Type q_event = event->type();
246 switch (q_event)
247 {
248 case QEvent::TouchEnd:
249 clearTouchEvent(instance);
250 break;
251 case QEvent::TouchBegin:
252 case QEvent::TouchUpdate:
253 {
254 QTouchEvent* touchEvent = static_cast<QTouchEvent*>(event);
255 QList<QTouchEvent::TouchPoint> touch_points = touchEvent->points();
256 if (touch_points.count() == 2)
257 {
258 // determine scale factor
259 const QTouchEvent::TouchPoint& touchPoint0 = touch_points.first();
260 const QTouchEvent::TouchPoint& touchPoint1 = touch_points.last();
261 Vector<2, fltp04> p0(touchPoint0.position().x(), touchPoint0.position().y());
262 Vector<2, fltp04> p1(touchPoint1.position().x(), touchPoint1.position().y());
263 addTouchEvent({ p0, p1 }, size, touchEvent->touchPointStates() & Qt::TouchPointPressed, instance);
264 }
265 return true;
266 } break;
267 case QEvent::Leave:
268 {
269 MouseEvent mouse_event(instance);
270 m_last_touch_scale_factor = Constant<fltp08>::NaN;
271 mouse_event.event_type = MouseEvent::e_mouse_exited;
272 addMouseEvent(mouse_event);
273 clearTouchEvent(instance);
274 } break;
275 case QEvent::MouseButtonPress:
276 case QEvent::MouseButtonRelease:
277 clearTouchEvent(instance);
278 [[fallthrough]];
279 case QEvent::MouseMove:
280 if (!isNaN(m_last_touch_scale_factor))
281 break;
282 [[fallthrough]];
283 case QEvent::MouseTrackingChange:
284 case QEvent::MouseButtonDblClick:
285 {
286 //requestActivate();
287
288 m_last_touch_scale_factor = Constant<fltp08>::NaN;
289 const QMouseEvent* qt_mouse_event = dynamic_cast<QMouseEvent*>(event);
290 if (qt_mouse_event == nullptr)
291 break;
292
293 MouseEvent mouse_event(instance);
294 mouse_event.event_location[X] = cast<fltp04>(qt_mouse_event->pos().x());
295 mouse_event.event_location[Y] = cast<fltp04>(qt_mouse_event->pos().y());
296 switch (qt_mouse_event->button())
297 {
298 case Qt::NoButton:
299 if (WidgetOptions::IsTouchOptimized() && m_last_mouse_press_time != 0 && qt_mouse_event->timestamp() - m_last_mouse_press_time > 500)
300 mouse_event.click_type = MouseEvent::e_right_button;
301 break;// m_mouse_pressed; break;
302 case Qt::LeftButton: mouse_event.click_type = MouseEvent::e_left_button; break;
303 case Qt::RightButton: mouse_event.click_type = MouseEvent::e_right_button; break;
304 case Qt::MiddleButton: mouse_event.click_type = MouseEvent::e_center_button; break;
305 default: break;
306 }
307 mouse_event.number_of_clicks = 1;
308 mouse_event.key_modifier = 0;
309 Qt::KeyboardModifiers mods = qt_mouse_event->modifiers();
310 if (mods & Qt::KeyboardModifier::ShiftModifier)
311 mouse_event.key_modifier |= KeyEvent::SHIFT;
312 if (mods & Qt::KeyboardModifier::ControlModifier)
313 mouse_event.key_modifier |= KeyEvent::CTRL;
314 if (mods & Qt::KeyboardModifier::AltModifier)
315 mouse_event.key_modifier |= KeyEvent::ALT;
316 switch (event->type())
317 {
318 case QEvent::MouseButtonPress:
319 {
320 mouse_event.event_type = MouseEvent::e_mouse_pressed;
321 //m_mouse_pressed = mouse_event.click_type;
322 m_last_mouse_press_time = qt_mouse_event->timestamp();
323 m_last_mouse_press_point = mouse_event.event_location;
324 } break;
325 case QEvent::MouseButtonRelease:
326 {
327 mouse_event.event_type = MouseEvent::e_mouse_released;
328 if (m_use_mouse_clicks && qt_mouse_event->timestamp() - m_last_mouse_press_time < 200)
329 mouse_event.event_type = MouseEvent::e_mouse_clicked;
330 m_last_mouse_press_time = 0;
331 m_last_mouse_press_point = Constant<Vector<2, fltp04>>::NaN;
332 } break;
333 case QEvent::MouseButtonDblClick:
334 {
335 m_last_mouse_press_time = 0;
336 mouse_event.event_type = MouseEvent::e_mouse_clicked;
337 mouse_event.number_of_clicks = 2;
338 } break;
339 case QEvent::MouseTrackingChange:
340 mouse_event.event_type = MouseEvent::e_mouse_scrolled;
341 break;
342 case QEvent::DragMove:
343 case QEvent::MouseMove:
344 {
345 Qt::MouseButtons qt_buttons = qt_mouse_event->buttons();
346 switch (qt_buttons)
347 {
348 case Qt::LeftButton: mouse_event.click_type = MouseEvent::e_left_button; break;
349 case Qt::RightButton: mouse_event.click_type = MouseEvent::e_right_button; break;
350 case Qt::MiddleButton: mouse_event.click_type = MouseEvent::e_center_button; break;
351 default: mouse_event.event_type = MouseEvent::e_mouse_moved;
352 }
353 if (mouse_event.click_type != MouseEvent::none)
354 {
355 if (distance(m_last_mouse_press_point / size.as<2, fltp04>(), mouse_event.event_location / size.as<2, fltp04>()) > 0.03)
356 m_last_mouse_press_time = 0;
357 mouse_event.event_type = MouseEvent::e_mouse_dragged;
358 }
359 else
360 {
361 mouse_event.event_type = MouseEvent::e_mouse_moved;
362 }
363 } break;
364 default:
365 break;
366 }
367 addMouseEvent(mouse_event);
368 return true;
369 } break;
370 case QEvent::Wheel:
371 {
372 QWheelEvent* wheel_event = dynamic_cast<QWheelEvent*>(event);
373 MouseEvent mouse_event(instance);
374 mouse_event.event_type = MouseEvent::e_mouse_scrolled;
375 mouse_event.distance_scrolled[X] = wheel_event->angleDelta().x();
376 mouse_event.distance_scrolled[Y] = wheel_event->angleDelta().y();
377 if (wheel_event->inverted())
378 mouse_event.distance_scrolled = -mouse_event.distance_scrolled;
379 mouse_event.event_location[X] = cast<fltp04>(wheel_event->position().x());
380 mouse_event.event_location[Y] = cast<fltp04>(wheel_event->position().y());
381 mouse_event.number_of_clicks = 1;
382 mouse_event.click_type = MouseEvent::e_center_button;
383 addMouseEvent(mouse_event);
384 return true;
385 } break;
386 default:
387 break;
388 }
389 return false;
390
391 }
392 void addMouseEvent(MouseEvent& mouse_event)
393 {
394#if RUN_MOUSE_MANAGER_THREADED
395 std::unique_lock<std::mutex> crit_lock(m_critical_section);
396#endif
397 switch (mouse_event.event_type)
398 {
399 case MouseEvent::e_mouse_scrolled:
400 if (m_last_mouse_events.isEmpty() || m_last_mouse_events.last().event_type != MouseEvent::e_mouse_scrolled)
401 m_last_mouse_events.add(mouse_event);
402 else
403 m_last_mouse_events.last().distance_scrolled += mouse_event.distance_scrolled;
404 break;
405 case MouseEvent::e_touch_zoomed:
406 if (m_last_mouse_events.isEmpty() || m_last_mouse_events.last().event_type != MouseEvent::e_touch_zoomed)
407 m_last_mouse_events.add(mouse_event);
408 else
409 m_last_mouse_events.last().distance_scrolled += mouse_event.distance_scrolled;
410 break;
411 case MouseEvent::e_mouse_moved:
412 case MouseEvent::e_mouse_dragged:
413 if (m_last_mouse_events.isEmpty() || m_last_mouse_events.last().event_type != mouse_event.event_type)
414 m_last_mouse_events.add(mouse_event);
415 else
416 m_last_mouse_events.last().event_location = mouse_event.event_location;
417 break;
418 case MouseEvent::e_mouse_released:
419 m_last_mouse_events.add(mouse_event);
420 break;
421 default:
422 m_last_mouse_events.add(mouse_event);
423 break;
424 }
425#if !RUN_MOUSE_MANAGER_THREADED
426 processMouseEvent();
427#else
428 ConcurrentOperation::notifyAll(&m_critical_section);
429#endif
430 }
431 const Buffer<MouseEvent>& mouseEvents() const
432 {
433 return m_last_mouse_events;
434 }
435 static ApplicationOption<bool> seperate_mouse_thread;
436 protected:
437 bool m_is_init;
438 bool m_use_mouse_clicks;
439#if RUN_MOUSE_MANAGER_THREADED
440 mutable std::mutex m_critical_section;
441#endif
442 Buffer<MouseEvent> m_last_mouse_events;
443 Vector<2, fltp04> m_last_mouse_press_point;
444
445
446 Vector<2, Vector<2, fltp04>> m_last_touch_press_point = Constant<Vector<2, Vector<2, fltp04>>>::NaN;
447
448 fltp08 m_last_touch_scale_factor = Constant<fltp08>::NaN;
449 uint08 m_last_mouse_press_time;
450 };
451}
452#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
453#undef position
454#undef points
455#endif
456#endif
Definition ACIColor.h:37