1 | // Aseprite UI Library |
2 | // Copyright (C) 2018-2022 Igara Studio S.A. |
3 | // Copyright (C) 2001-2018 David Capello |
4 | // |
5 | // This file is released under the terms of the MIT license. |
6 | // Read LICENSE.txt for more information. |
7 | |
8 | // #define REPORT_EVENTS |
9 | // #define REPORT_FOCUS_MOVEMENT |
10 | // #define DEBUG_PAINT_EVENTS |
11 | // #define LIMIT_DISPATCH_TIME |
12 | // #define DEBUG_UI_THREADS |
13 | #define GARBAGE_TRACE(...) |
14 | |
15 | #ifdef HAVE_CONFIG_H |
16 | #include "config.h" |
17 | #endif |
18 | |
19 | #include "ui/manager.h" |
20 | |
21 | #include "base/concurrent_queue.h" |
22 | #include "base/scoped_value.h" |
23 | #include "base/time.h" |
24 | #include "os/event.h" |
25 | #include "os/event_queue.h" |
26 | #include "os/surface.h" |
27 | #include "os/system.h" |
28 | #include "os/window.h" |
29 | #include "os/window_spec.h" |
30 | #include "ui/intern.h" |
31 | #include "ui/ui.h" |
32 | |
33 | #if defined(DEBUG_PAINT_EVENTS) || defined(DEBUG_UI_THREADS) |
34 | #include "base/thread.h" |
35 | #endif |
36 | |
37 | #include <algorithm> |
38 | #include <limits> |
39 | #include <list> |
40 | #include <memory> |
41 | #include <utility> |
42 | #include <vector> |
43 | |
44 | #if defined(_WIN32) && defined(DEBUG_PAINT_EVENTS) |
45 | #define WIN32_LEAN_AND_MEAN |
46 | #include <windows.h> |
47 | #undef min |
48 | #undef max |
49 | #endif |
50 | |
51 | namespace ui { |
52 | |
53 | namespace { |
54 | |
55 | // The redraw state is used to avoid drawing the manager when a window |
56 | // has been just closed by the user, so we delay the redrawing (the |
57 | // kPaintMessages generation) for the next generateMessages() round. |
58 | enum class RedrawState { |
59 | Normal, |
60 | AWindowHasJustBeenClosed, |
61 | RedrawDelayed, |
62 | ClosingApp, |
63 | }; |
64 | RedrawState redrawState = RedrawState::Normal; |
65 | |
66 | } // anonymous namespace |
67 | |
68 | static const int NFILTERS = (int)(kFirstRegisteredMessage+1); |
69 | |
70 | struct Filter { |
71 | int message; |
72 | Widget* widget; |
73 | |
74 | Filter(int message, Widget* widget) |
75 | : message(message) |
76 | , widget(widget) { } |
77 | }; |
78 | |
79 | typedef std::list<Message*> Messages; |
80 | typedef std::list<Filter*> Filters; |
81 | |
82 | Manager* Manager::m_defaultManager = nullptr; |
83 | |
84 | #ifdef DEBUG_UI_THREADS |
85 | static base::thread::native_id_type manager_thread = 0; |
86 | #endif |
87 | |
88 | static WidgetsList mouse_widgets_list; // List of widgets to send mouse events |
89 | static Messages msg_queue; // Messages queue |
90 | static Messages used_msg_queue; // Messages queue |
91 | static base::concurrent_queue<Message*> concurrent_msg_queue; |
92 | static Filters msg_filters[NFILTERS]; // Filters for every enqueued message |
93 | static int filter_locks = 0; |
94 | |
95 | // Current display with the mouse, used to avoid processing a |
96 | // os::Event::MouseLeave of the non-current display/window as when we |
97 | // move the mouse between two windows we can receive: |
98 | // |
99 | // 1. A os::Event::MouseEnter of the new window |
100 | // 2. A os::Event::MouseLeave of the old window |
101 | // |
102 | // Instead of the MouseLeave event of the old window first. |
103 | static Display* mouse_display = nullptr; |
104 | |
105 | static Widget* focus_widget; // The widget with the focus |
106 | static Widget* mouse_widget; // The widget with the mouse |
107 | static Widget* capture_widget; // The widget that captures the mouse |
108 | |
109 | static bool first_time = true; // true when we don't enter in poll yet |
110 | |
111 | // Don't adjust window positions automatically when it's false. Used |
112 | // when Screen/UI scaling is changed to avoid adjusting windows as |
113 | // when the os::Display is resized by the user. |
114 | static bool auto_window_adjustment = true; |
115 | |
116 | // Keyboard focus movement stuff |
117 | inline bool does_accept_focus(Widget* widget) |
118 | { |
119 | return ((((widget)->flags() & (FOCUS_STOP | |
120 | DISABLED | |
121 | HIDDEN | |
122 | DECORATIVE)) == FOCUS_STOP) && |
123 | ((widget)->isVisible())); |
124 | } |
125 | |
126 | static int count_widgets_accept_focus(Widget* widget); |
127 | static bool child_accept_focus(Widget* widget, bool first); |
128 | static Widget* next_widget(Widget* widget); |
129 | static int cmp_left(Widget* widget, int x, int y); |
130 | static int cmp_right(Widget* widget, int x, int y); |
131 | static int cmp_up(Widget* widget, int x, int y); |
132 | static int cmp_down(Widget* widget, int x, int y); |
133 | |
134 | namespace { |
135 | |
136 | class LockFilters { |
137 | public: |
138 | LockFilters() { |
139 | ++filter_locks; |
140 | } |
141 | ~LockFilters() { |
142 | ASSERT(filter_locks > 0); |
143 | --filter_locks; |
144 | |
145 | if (filter_locks == 0) { |
146 | // Clear empty filters |
147 | for (Filters& msg_filter : msg_filters) { |
148 | for (auto it = msg_filter.begin(); it != msg_filter.end(); ) { |
149 | Filter* filter = *it; |
150 | if (filter->widget == nullptr) { |
151 | delete filter; |
152 | it = msg_filter.erase(it); |
153 | } |
154 | else { |
155 | ++it; |
156 | } |
157 | } |
158 | } |
159 | } |
160 | } |
161 | }; |
162 | |
163 | os::Hit handle_native_hittest(os::Window* osWindow, |
164 | const gfx::Point& pos) |
165 | { |
166 | Display* display = Manager::getDisplayFromNativeWindow(osWindow); |
167 | if (display) { |
168 | auto window = static_cast<Window*>(display->containedWidget()); |
169 | switch (window->hitTest(pos)) { |
170 | case HitTestNowhere: return os::Hit::Content; |
171 | case HitTestCaption: return os::Hit::TitleBar; |
172 | case HitTestClient: return os::Hit::Content; |
173 | case HitTestBorderNW: return os::Hit::TopLeft; |
174 | case HitTestBorderN: return os::Hit::Top; |
175 | case HitTestBorderNE: return os::Hit::TopRight; |
176 | case HitTestBorderE: return os::Hit::Right; |
177 | case HitTestBorderSE: return os::Hit::BottomRight; |
178 | case HitTestBorderS: return os::Hit::Bottom; |
179 | case HitTestBorderSW: return os::Hit::BottomLeft; |
180 | case HitTestBorderW: return os::Hit::Left; |
181 | } |
182 | } |
183 | return os::Hit::None; |
184 | } |
185 | |
186 | } // anonymous namespace |
187 | |
188 | // static |
189 | bool Manager::widgetAssociatedToManager(Widget* widget) |
190 | { |
191 | return (focus_widget == widget || |
192 | mouse_widget == widget || |
193 | capture_widget == widget || |
194 | std::find(mouse_widgets_list.begin(), |
195 | mouse_widgets_list.end(), |
196 | widget) != mouse_widgets_list.end()); |
197 | } |
198 | |
199 | Manager::Manager(const os::WindowRef& nativeWindow) |
200 | : Widget(kManagerWidget) |
201 | , m_display(nullptr, nativeWindow, this) |
202 | , m_eventQueue(os::instance()->eventQueue()) |
203 | , m_lockedWindow(nullptr) |
204 | , m_mouseButton(kButtonNone) |
205 | { |
206 | // The native window can be nullptr when running tests |
207 | if (nativeWindow) |
208 | nativeWindow->setUserData(&m_display); |
209 | |
210 | #ifdef DEBUG_UI_THREADS |
211 | ASSERT(!manager_thread); |
212 | manager_thread = base::this_thread::native_id(); |
213 | #endif |
214 | |
215 | if (!m_defaultManager) { |
216 | // Empty lists |
217 | ASSERT(msg_queue.empty()); |
218 | mouse_widgets_list.clear(); |
219 | |
220 | // Reset variables |
221 | focus_widget = nullptr; |
222 | mouse_widget = nullptr; |
223 | capture_widget = nullptr; |
224 | } |
225 | |
226 | setBounds(m_display.bounds()); |
227 | setVisible(true); |
228 | |
229 | // Default manager is the first one (and is always visible). |
230 | if (!m_defaultManager) |
231 | m_defaultManager = this; |
232 | |
233 | // TODO check if this is needed |
234 | onNewDisplayConfiguration(&m_display); |
235 | } |
236 | |
237 | Manager::~Manager() |
238 | { |
239 | #ifdef DEBUG_UI_THREADS |
240 | ASSERT(manager_thread == base::this_thread::native_id()); |
241 | #endif |
242 | |
243 | // There are some messages in queue? Dispatch everything. |
244 | dispatchMessages(); |
245 | collectGarbage(); |
246 | |
247 | // Finish the main manager. |
248 | if (m_defaultManager == this) { |
249 | // No more cursor |
250 | set_mouse_cursor(kNoCursor); |
251 | |
252 | // Check timers & filters |
253 | #ifdef _DEBUG |
254 | if (get_app_state() != AppState::kClosingWithException) { |
255 | ASSERT(!Timer::haveTimers()); |
256 | for (Filters& msg_filter : msg_filters) |
257 | ASSERT(msg_filter.empty()); |
258 | } |
259 | ASSERT(msg_queue.empty()); |
260 | #endif |
261 | |
262 | // No more default manager |
263 | m_defaultManager = nullptr; |
264 | |
265 | // Shutdown system |
266 | mouse_widgets_list.clear(); |
267 | } |
268 | } |
269 | |
270 | // static |
271 | Display* Manager::getDisplayFromNativeWindow(os::Window* window) |
272 | { |
273 | if (window) |
274 | return window->userData<Display>(); |
275 | else |
276 | return nullptr; |
277 | } |
278 | |
279 | void Manager::run() |
280 | { |
281 | MessageLoop loop(this); |
282 | |
283 | if (first_time) { |
284 | first_time = false; |
285 | |
286 | invalidate(); |
287 | set_mouse_cursor(kArrowCursor); |
288 | } |
289 | |
290 | while (!children().empty()) |
291 | loop.pumpMessages(); |
292 | } |
293 | |
294 | void Manager::flipAllDisplays() |
295 | { |
296 | OverlayManager* overlays = OverlayManager::instance(); |
297 | |
298 | update_cursor_overlay(); |
299 | |
300 | // Draw overlays. |
301 | overlays->drawOverlays(); |
302 | |
303 | m_display.flipDisplay(); |
304 | if (get_multiple_displays()) { |
305 | for (auto child : children()) { |
306 | auto window = static_cast<Window*>(child); |
307 | if (window->ownDisplay()) |
308 | window->display()->flipDisplay(); |
309 | } |
310 | } |
311 | } |
312 | |
313 | void Manager::updateAllDisplaysWithNewScale(int scale) |
314 | { |
315 | os::Window* nativeWindow = m_display.nativeWindow(); |
316 | nativeWindow->setScale(scale); |
317 | |
318 | if (get_multiple_displays()) { |
319 | for (auto child : children()) { |
320 | auto window = static_cast<Window*>(child); |
321 | if (window->ownDisplay()) { |
322 | Display* display = static_cast<Window*>(child)->display(); |
323 | display->nativeWindow()->setScale(scale); |
324 | onNewDisplayConfiguration(display); |
325 | } |
326 | } |
327 | } |
328 | |
329 | onNewDisplayConfiguration(&m_display); |
330 | } |
331 | |
332 | bool Manager::generateMessages() |
333 | { |
334 | #ifdef DEBUG_UI_THREADS |
335 | ASSERT(manager_thread == base::this_thread::native_id()); |
336 | #endif |
337 | |
338 | // First check: there are windows to manage? |
339 | if (children().empty()) |
340 | return false; |
341 | |
342 | // Generate messages from other threads |
343 | if (!concurrent_msg_queue.empty()) { |
344 | Message* msg = nullptr; |
345 | while (concurrent_msg_queue.try_pop(msg)) |
346 | msg_queue.push_back(msg); |
347 | } |
348 | |
349 | // Generate messages from OS input |
350 | generateMessagesFromOSEvents(); |
351 | |
352 | // Generate messages for timers |
353 | Timer::pollTimers(); |
354 | |
355 | // Returns true if we have to dispatch messages (if the redraw was |
356 | // delayed, we have to pump messages because there is where paint |
357 | // messages are flushed) |
358 | if (!msg_queue.empty() || redrawState != RedrawState::Normal) |
359 | return true; |
360 | else |
361 | return false; |
362 | } |
363 | |
364 | void Manager::generateSetCursorMessage(Display* display, |
365 | const gfx::Point& mousePos, |
366 | KeyModifiers modifiers, |
367 | PointerType pointerType) |
368 | { |
369 | if (get_mouse_cursor() == kOutsideDisplay) |
370 | return; |
371 | |
372 | Widget* dst = (capture_widget ? capture_widget: mouse_widget); |
373 | if (dst) |
374 | enqueueMessage( |
375 | newMouseMessage( |
376 | kSetCursorMessage, |
377 | display, dst, |
378 | mousePos, |
379 | pointerType, |
380 | m_mouseButton, |
381 | modifiers)); |
382 | else |
383 | set_mouse_cursor(kArrowCursor); |
384 | } |
385 | |
386 | static MouseButton mouse_button_from_os_to_ui(const os::Event& osEvent) |
387 | { |
388 | static_assert((int)os::Event::NoneButton == (int)ui::kButtonNone && |
389 | (int)os::Event::LeftButton == (int)ui::kButtonLeft && |
390 | (int)os::Event::RightButton == (int)ui::kButtonRight && |
391 | (int)os::Event::MiddleButton == (int)ui::kButtonMiddle && |
392 | (int)os::Event::X1Button == (int)ui::kButtonX1 && |
393 | (int)os::Event::X2Button == (int)ui::kButtonX2, |
394 | "Mouse button constants do not match" ); |
395 | return (MouseButton)osEvent.button(); |
396 | } |
397 | |
398 | void Manager::generateMessagesFromOSEvents() |
399 | { |
400 | #ifdef DEBUG_UI_THREADS |
401 | ASSERT(manager_thread == base::this_thread::native_id()); |
402 | #endif |
403 | |
404 | os::Event lastMouseMoveEvent; |
405 | |
406 | // Events from laf-os |
407 | os::Event osEvent; |
408 | for (;;) { |
409 | // Calculate how much time we can wait for the next message in the |
410 | // event queue. |
411 | double timeout = 0.0; |
412 | if (msg_queue.empty() && redrawState == RedrawState::Normal) { |
413 | if (!Timer::getNextTimeout(timeout)) |
414 | timeout = os::EventQueue::kWithoutTimeout; |
415 | } |
416 | |
417 | if (timeout == os::EventQueue::kWithoutTimeout && used_msg_queue.empty()) |
418 | collectGarbage(); |
419 | #if _DEBUG |
420 | else if (!m_garbage.empty()) { |
421 | GARBAGE_TRACE("collectGarbage() wasn't called #objects=%d" |
422 | " (msg_queue=%d used_msg_queue=%d redrawState=%d timeout=%.16g)\n" , |
423 | int(m_garbage.size()), |
424 | msg_queue.size(), |
425 | used_msg_queue.size(), |
426 | int(redrawState), |
427 | timeout); |
428 | } |
429 | #endif |
430 | |
431 | m_eventQueue->getEvent(osEvent, timeout); |
432 | if (osEvent.type() == os::Event::None) |
433 | break; |
434 | |
435 | Display* display = getDisplayFromNativeWindow(osEvent.window().get()); |
436 | if (!display) |
437 | display = this->display(); |
438 | |
439 | switch (osEvent.type()) { |
440 | |
441 | case os::Event::CloseApp: { |
442 | Message* msg = new Message(kCloseDisplayMessage); |
443 | msg->setDisplay(display); |
444 | msg->setRecipient(this); |
445 | msg->setPropagateToChildren(true); |
446 | enqueueMessage(msg); |
447 | break; |
448 | } |
449 | |
450 | case os::Event::CloseWindow: { |
451 | Message* msg = new Message(kCloseDisplayMessage); |
452 | msg->setDisplay(display); |
453 | msg->setRecipient(this); |
454 | msg->setPropagateToChildren(true); |
455 | enqueueMessage(msg); |
456 | break; |
457 | } |
458 | |
459 | case os::Event::ResizeWindow: { |
460 | Message* msg = new Message(kResizeDisplayMessage); |
461 | msg->setDisplay(display); |
462 | msg->setRecipient(this); |
463 | msg->setPropagateToChildren(false); |
464 | enqueueMessage(msg); |
465 | break; |
466 | } |
467 | |
468 | case os::Event::DropFiles: { |
469 | Message* msg = new DropFilesMessage(osEvent.files()); |
470 | msg->setDisplay(display); |
471 | msg->setRecipient(this); |
472 | enqueueMessage(msg); |
473 | break; |
474 | } |
475 | |
476 | case os::Event::KeyDown: |
477 | case os::Event::KeyUp: { |
478 | Message* msg = new KeyMessage( |
479 | (osEvent.type() == os::Event::KeyDown ? |
480 | kKeyDownMessage: |
481 | kKeyUpMessage), |
482 | osEvent.scancode(), |
483 | osEvent.modifiers(), |
484 | osEvent.unicodeChar(), |
485 | osEvent.repeat()); |
486 | |
487 | msg->setDisplay(display); |
488 | |
489 | if (osEvent.isDeadKey()) |
490 | static_cast<KeyMessage*>(msg)->setDeadKey(true); |
491 | |
492 | broadcastKeyMsg(msg); |
493 | enqueueMessage(msg); |
494 | break; |
495 | } |
496 | |
497 | case os::Event::MouseEnter: { |
498 | if (get_multiple_displays()) { |
499 | if (osEvent.window()) { |
500 | ASSERT(display != nullptr); |
501 | _internal_set_mouse_display(display); |
502 | } |
503 | } |
504 | set_mouse_cursor(kArrowCursor); |
505 | lastMouseMoveEvent = osEvent; |
506 | mouse_display = display; |
507 | break; |
508 | } |
509 | |
510 | case os::Event::MouseLeave: { |
511 | if (mouse_display == display) { |
512 | set_mouse_cursor(kOutsideDisplay); |
513 | setMouse(nullptr); |
514 | |
515 | _internal_no_mouse_position(); |
516 | mouse_display = nullptr; |
517 | |
518 | // To avoid calling kSetCursorMessage when the mouse leaves |
519 | // the window. |
520 | lastMouseMoveEvent = os::Event(); |
521 | } |
522 | break; |
523 | } |
524 | |
525 | case os::Event::MouseMove: { |
526 | handleMouseMove( |
527 | display, |
528 | osEvent.position(), |
529 | osEvent.modifiers(), |
530 | osEvent.pointerType(), |
531 | osEvent.pressure()); |
532 | lastMouseMoveEvent = osEvent; |
533 | break; |
534 | } |
535 | |
536 | case os::Event::MouseDown: { |
537 | handleMouseDown( |
538 | display, |
539 | osEvent.position(), |
540 | m_mouseButton = mouse_button_from_os_to_ui(osEvent), |
541 | osEvent.modifiers(), |
542 | osEvent.pointerType(), |
543 | osEvent.pressure()); |
544 | break; |
545 | } |
546 | |
547 | case os::Event::MouseUp: { |
548 | handleMouseUp( |
549 | display, |
550 | osEvent.position(), |
551 | mouse_button_from_os_to_ui(osEvent), |
552 | osEvent.modifiers(), |
553 | osEvent.pointerType()); |
554 | m_mouseButton = kButtonNone; |
555 | break; |
556 | } |
557 | |
558 | case os::Event::MouseDoubleClick: { |
559 | handleMouseDoubleClick( |
560 | display, |
561 | osEvent.position(), |
562 | m_mouseButton = mouse_button_from_os_to_ui(osEvent), |
563 | osEvent.modifiers(), |
564 | osEvent.pointerType(), |
565 | osEvent.pressure()); |
566 | break; |
567 | } |
568 | |
569 | case os::Event::MouseWheel: { |
570 | handleMouseWheel(display, |
571 | osEvent.position(), |
572 | osEvent.modifiers(), |
573 | osEvent.pointerType(), |
574 | osEvent.wheelDelta(), |
575 | osEvent.preciseWheel()); |
576 | break; |
577 | } |
578 | |
579 | case os::Event::TouchMagnify: { |
580 | handleTouchMagnify(display, |
581 | osEvent.position(), |
582 | osEvent.modifiers(), |
583 | osEvent.magnification()); |
584 | break; |
585 | } |
586 | |
587 | case os::Event::Callback: { |
588 | // Call from the UI thread |
589 | osEvent.execCallback(); |
590 | break; |
591 | } |
592 | |
593 | } |
594 | } |
595 | |
596 | // Generate just one kSetCursorMessage for the last mouse position |
597 | if (lastMouseMoveEvent.type() != os::Event::None) { |
598 | osEvent = lastMouseMoveEvent; |
599 | |
600 | Display* display = getDisplayFromNativeWindow(osEvent.window().get()); |
601 | if (!display) |
602 | display = this->display(); |
603 | |
604 | generateSetCursorMessage(display, |
605 | osEvent.position(), |
606 | osEvent.modifiers(), |
607 | osEvent.pointerType()); |
608 | } |
609 | } |
610 | |
611 | void Manager::handleMouseMove(Display* display, |
612 | const gfx::Point& mousePos, |
613 | const KeyModifiers modifiers, |
614 | const PointerType pointerType, |
615 | const float pressure) |
616 | { |
617 | updateMouseWidgets(mousePos, display); |
618 | |
619 | // Send the mouse movement message |
620 | Widget* dst = (capture_widget ? capture_widget: mouse_widget); |
621 | enqueueMessage( |
622 | newMouseMessage( |
623 | kMouseMoveMessage, |
624 | display, dst, |
625 | mousePos, |
626 | pointerType, |
627 | m_mouseButton, |
628 | modifiers, |
629 | gfx::Point(0, 0), |
630 | false, |
631 | pressure)); |
632 | } |
633 | |
634 | void Manager::handleMouseDown(Display* display, |
635 | const gfx::Point& mousePos, |
636 | MouseButton mouseButton, |
637 | KeyModifiers modifiers, |
638 | PointerType pointerType, |
639 | const float pressure) |
640 | { |
641 | handleWindowZOrder(); |
642 | |
643 | enqueueMessage( |
644 | newMouseMessage( |
645 | kMouseDownMessage, |
646 | display, |
647 | (capture_widget ? capture_widget: mouse_widget), |
648 | mousePos, |
649 | pointerType, |
650 | mouseButton, |
651 | modifiers, |
652 | gfx::Point(0, 0), |
653 | false, |
654 | pressure)); |
655 | } |
656 | |
657 | void Manager::handleMouseUp(Display* display, |
658 | const gfx::Point& mousePos, |
659 | MouseButton mouseButton, |
660 | KeyModifiers modifiers, |
661 | PointerType pointerType) |
662 | { |
663 | enqueueMessage( |
664 | newMouseMessage( |
665 | kMouseUpMessage, |
666 | display, |
667 | (capture_widget ? capture_widget: mouse_widget), |
668 | mousePos, |
669 | pointerType, |
670 | mouseButton, |
671 | modifiers)); |
672 | } |
673 | |
674 | void Manager::handleMouseDoubleClick(Display* display, |
675 | const gfx::Point& mousePos, |
676 | MouseButton mouseButton, |
677 | KeyModifiers modifiers, |
678 | PointerType pointerType, |
679 | const float pressure) |
680 | { |
681 | Widget* dst = (capture_widget ? capture_widget: mouse_widget); |
682 | if (dst) { |
683 | enqueueMessage( |
684 | newMouseMessage( |
685 | kDoubleClickMessage, |
686 | display, dst, mousePos, pointerType, |
687 | mouseButton, modifiers, |
688 | gfx::Point(0, 0), false, |
689 | pressure)); |
690 | } |
691 | } |
692 | |
693 | void Manager::handleMouseWheel(Display* display, |
694 | const gfx::Point& mousePos, |
695 | KeyModifiers modifiers, |
696 | PointerType pointerType, |
697 | const gfx::Point& wheelDelta, |
698 | bool preciseWheel) |
699 | { |
700 | enqueueMessage(newMouseMessage( |
701 | kMouseWheelMessage, |
702 | display, |
703 | (capture_widget ? capture_widget: mouse_widget), |
704 | mousePos, pointerType, m_mouseButton, modifiers, |
705 | wheelDelta, preciseWheel)); |
706 | } |
707 | |
708 | void Manager::handleTouchMagnify(Display* display, |
709 | const gfx::Point& mousePos, |
710 | const KeyModifiers modifiers, |
711 | const double magnification) |
712 | { |
713 | Widget* widget = (capture_widget ? capture_widget: mouse_widget); |
714 | if (widget) { |
715 | Message* msg = new TouchMessage( |
716 | kTouchMagnifyMessage, |
717 | modifiers, |
718 | mousePos, |
719 | magnification); |
720 | |
721 | msg->setDisplay(display); |
722 | msg->setRecipient(widget); |
723 | |
724 | enqueueMessage(msg); |
725 | } |
726 | } |
727 | |
728 | // Handles Z order: Send the window to top (only when you click in a |
729 | // window that aren't the desktop). |
730 | // |
731 | // TODO code similar to Display::handleWindowZOrder() |
732 | void Manager::handleWindowZOrder() |
733 | { |
734 | if (capture_widget || !mouse_widget) |
735 | return; |
736 | |
737 | // The clicked window |
738 | Window* window = mouse_widget->window(); |
739 | Manager* win_manager = (window ? window->manager(): nullptr); |
740 | |
741 | if ((window) && |
742 | // We cannot change Z-order of desktop windows |
743 | (!window->isDesktop()) && |
744 | // We cannot change Z order of foreground windows because a |
745 | // foreground window can launch other background windows |
746 | // which should be kept on top of the foreground one. |
747 | (!window->isForeground()) && |
748 | // If the window is not already the top window of the manager. |
749 | (window != win_manager->getTopWindow())) { |
750 | base::ScopedValue<Widget*> scoped(m_lockedWindow, window, nullptr); |
751 | |
752 | window->display()->handleWindowZOrder(window); |
753 | |
754 | // Put it in the top of the list |
755 | win_manager->removeChild(window); |
756 | |
757 | if (window->isOnTop()) |
758 | win_manager->insertChild(0, window); |
759 | else { |
760 | int pos = (int)win_manager->children().size(); |
761 | |
762 | for (auto it=win_manager->children().rbegin(), |
763 | end=win_manager->children().rend(); |
764 | it != end; ++it) { |
765 | if (static_cast<Window*>(*it)->isOnTop()) |
766 | break; |
767 | |
768 | --pos; |
769 | } |
770 | win_manager->insertChild(pos, window); |
771 | } |
772 | |
773 | if (!window->ownDisplay()) |
774 | window->invalidate(); |
775 | } |
776 | |
777 | // Put the focus |
778 | setFocus(mouse_widget); |
779 | } |
780 | |
781 | // If display is nullptr, mousePos is in screen coordinates, if not, |
782 | // it's relative to the display content rect. |
783 | void Manager::updateMouseWidgets(const gfx::Point& mousePos, |
784 | Display* display) |
785 | { |
786 | gfx::Point screenPos = (display ? display->nativeWindow()->pointToScreen(mousePos): |
787 | mousePos); |
788 | |
789 | // Get the list of widgets to send mouse messages. |
790 | mouse_widgets_list.clear(); |
791 | broadcastMouseMessage(screenPos, |
792 | mouse_widgets_list); |
793 | |
794 | // Get the widget under the mouse |
795 | Widget* widget = nullptr; |
796 | for (auto mouseWidget : mouse_widgets_list) { |
797 | if (get_multiple_displays()) { |
798 | if (display) { |
799 | if (display != mouseWidget->display()) { |
800 | widget = mouseWidget->display()->containedWidget()->pickFromScreenPos(screenPos); |
801 | } |
802 | else { |
803 | widget = mouseWidget->pick(mousePos); |
804 | } |
805 | } |
806 | else { |
807 | widget = mouseWidget->display()->containedWidget()->pickFromScreenPos(screenPos); |
808 | } |
809 | } |
810 | else { |
811 | if (display) |
812 | widget = mouseWidget->pick(mousePos); |
813 | else |
814 | widget = mouseWidget->pickFromScreenPos(screenPos); |
815 | } |
816 | if (widget) { |
817 | // Get the first ancestor of the picked widget that doesn't |
818 | // ignore mouse events. |
819 | while (widget && widget->hasFlags(IGNORE_MOUSE)) |
820 | widget = widget->parent(); |
821 | break; |
822 | } |
823 | } |
824 | |
825 | // Fixup "mouse" flag |
826 | if (widget != mouse_widget) { |
827 | if (!widget) { |
828 | freeMouse(); |
829 | } |
830 | else { |
831 | setMouse(widget); |
832 | } |
833 | } |
834 | } |
835 | |
836 | void Manager::dispatchMessages() |
837 | { |
838 | // Send messages in the queue (mouse/key/timer/etc. events) This |
839 | // might change the state of widgets, etc. In case pumpQueue() |
840 | // returns a number greater than 0, it means that we've processed |
841 | // some messages, so we've to redraw the screen. |
842 | if (pumpQueue() > 0 || redrawState == RedrawState::RedrawDelayed) { |
843 | if (redrawState == RedrawState::ClosingApp) { |
844 | // Do nothing, we don't flush nor process paint messages |
845 | } |
846 | // If a window has just been closed with Manager::_closeWindow() |
847 | // after processing messages, we'll wait the next event generation |
848 | // to process painting events (so the manager doesn't lost the |
849 | // DIRTY flag right now). |
850 | else if (redrawState == RedrawState::AWindowHasJustBeenClosed) { |
851 | redrawState = RedrawState::RedrawDelayed; |
852 | } |
853 | else { |
854 | if (redrawState == RedrawState::RedrawDelayed) |
855 | redrawState = RedrawState::Normal; |
856 | |
857 | // Generate and send just kPaintMessages with the latest UI state. |
858 | flushRedraw(); |
859 | pumpQueue(); |
860 | |
861 | // Flip back-buffers to real displays. |
862 | flipAllDisplays(); |
863 | } |
864 | } |
865 | } |
866 | |
867 | void Manager::addToGarbage(Widget* widget) |
868 | { |
869 | ASSERT(widget); |
870 | m_garbage.push_back(widget); |
871 | } |
872 | |
873 | void Manager::enqueueMessage(Message* msg) |
874 | { |
875 | ASSERT(msg); |
876 | |
877 | if (is_ui_thread()) |
878 | msg_queue.push_back(msg); |
879 | else |
880 | concurrent_msg_queue.push(msg); |
881 | } |
882 | |
883 | Window* Manager::getTopWindow() |
884 | { |
885 | return static_cast<Window*>(UI_FIRST_WIDGET(children())); |
886 | } |
887 | |
888 | Window* Manager::getDesktopWindow() |
889 | { |
890 | for (auto child : children()) { |
891 | Window* window = static_cast<Window*>(child); |
892 | if (window->isDesktop()) |
893 | return window; |
894 | } |
895 | return nullptr; |
896 | } |
897 | |
898 | Window* Manager::getForegroundWindow() |
899 | { |
900 | for (auto child : children()) { |
901 | Window* window = static_cast<Window*>(child); |
902 | if (window->isForeground() || |
903 | window->isDesktop()) |
904 | return window; |
905 | } |
906 | return nullptr; |
907 | } |
908 | |
909 | Display* Manager::getForegroundDisplay() |
910 | { |
911 | if (get_multiple_displays()) { |
912 | Window* window = getForegroundWindow(); |
913 | if (window) |
914 | return window->display(); |
915 | } |
916 | return &m_display; |
917 | } |
918 | |
919 | Widget* Manager::getFocus() |
920 | { |
921 | return focus_widget; |
922 | } |
923 | |
924 | Widget* Manager::getMouse() |
925 | { |
926 | return mouse_widget; |
927 | } |
928 | |
929 | Widget* Manager::getCapture() |
930 | { |
931 | return capture_widget; |
932 | } |
933 | |
934 | void Manager::setFocus(Widget* widget) |
935 | { |
936 | if ((focus_widget != widget) |
937 | && (!(widget) |
938 | || (!(widget->hasFlags(DISABLED)) |
939 | && !(widget->hasFlags(HIDDEN)) |
940 | && !(widget->hasFlags(DECORATIVE)) |
941 | && someParentIsFocusStop(widget)))) { |
942 | Widget* commonAncestor = findLowestCommonAncestor(focus_widget, widget); |
943 | |
944 | // Fetch the focus |
945 | if (focus_widget && focus_widget != commonAncestor) { |
946 | auto msg = new Message(kFocusLeaveMessage); |
947 | msg->setRecipient(focus_widget); |
948 | msg->setPropagateToParent(true); |
949 | msg->setCommonAncestor(commonAncestor); |
950 | enqueueMessage(msg); |
951 | |
952 | // Remove HAS_FOCUS from all hierarchy |
953 | auto a = focus_widget; |
954 | while (a && a != commonAncestor) { |
955 | a->disableFlags(HAS_FOCUS); |
956 | a = a->parent(); |
957 | } |
958 | } |
959 | |
960 | // Put the focus |
961 | focus_widget = widget; |
962 | if (widget) { |
963 | auto msg = new Message(kFocusEnterMessage); |
964 | msg->setRecipient(widget); |
965 | msg->setPropagateToParent(true); |
966 | msg->setCommonAncestor(commonAncestor); |
967 | enqueueMessage(msg); |
968 | |
969 | // Add HAS_FOCUS to all hierarchy |
970 | auto a = focus_widget; |
971 | while (a && a != commonAncestor) { |
972 | if (a->hasFlags(FOCUS_STOP)) |
973 | a->enableFlags(HAS_FOCUS); |
974 | a = a->parent(); |
975 | } |
976 | } |
977 | } |
978 | } |
979 | |
980 | void Manager::setMouse(Widget* widget) |
981 | { |
982 | #ifdef REPORT_EVENTS |
983 | TRACEARGS("Manager::setMouse " , |
984 | (widget ? typeid(*widget).name(): "null" ), |
985 | (widget ? widget->id(): "" )); |
986 | #endif |
987 | |
988 | if ((mouse_widget != widget) && (!capture_widget)) { |
989 | Widget* commonAncestor = findLowestCommonAncestor(mouse_widget, widget); |
990 | |
991 | // Fetch the mouse |
992 | if (mouse_widget && mouse_widget != commonAncestor) { |
993 | auto msg = new Message(kMouseLeaveMessage); |
994 | msg->setRecipient(mouse_widget); |
995 | msg->setPropagateToParent(true); |
996 | msg->setCommonAncestor(commonAncestor); |
997 | enqueueMessage(msg); |
998 | |
999 | // Remove HAS_MOUSE from all the hierarchy |
1000 | auto a = mouse_widget; |
1001 | while (a && a != commonAncestor) { |
1002 | a->disableFlags(HAS_MOUSE); |
1003 | a = a->parent(); |
1004 | } |
1005 | } |
1006 | |
1007 | // Put the mouse |
1008 | mouse_widget = widget; |
1009 | if (widget) { |
1010 | Display* display = mouse_widget->display(); |
1011 | gfx::Point mousePos = display->nativeWindow()->pointFromScreen(get_mouse_position()); |
1012 | |
1013 | auto msg = newMouseMessage( |
1014 | kMouseEnterMessage, |
1015 | display, nullptr, |
1016 | mousePos, |
1017 | PointerType::Unknown, |
1018 | m_mouseButton, |
1019 | kKeyUninitializedModifier); |
1020 | |
1021 | msg->setRecipient(widget); |
1022 | msg->setPropagateToParent(true); |
1023 | msg->setCommonAncestor(commonAncestor); |
1024 | enqueueMessage(msg); |
1025 | generateSetCursorMessage(display, |
1026 | mousePos, |
1027 | kKeyUninitializedModifier, |
1028 | PointerType::Unknown); |
1029 | |
1030 | // Add HAS_MOUSE to all the hierarchy |
1031 | auto a = mouse_widget; |
1032 | while (a && a != commonAncestor) { |
1033 | a->enableFlags(HAS_MOUSE); |
1034 | a = a->parent(); |
1035 | } |
1036 | } |
1037 | } |
1038 | } |
1039 | |
1040 | void Manager::setCapture(Widget* widget) |
1041 | { |
1042 | // To set the capture, we set first the mouse_widget (because |
1043 | // mouse_widget shouldn't be != capture_widget) |
1044 | setMouse(widget); |
1045 | |
1046 | widget->enableFlags(HAS_CAPTURE); |
1047 | capture_widget = widget; |
1048 | |
1049 | Display* display = (widget ? widget->display(): &m_display); |
1050 | ASSERT(display && display->nativeWindow()); |
1051 | if (display && display->nativeWindow()) |
1052 | display->nativeWindow()->captureMouse(); |
1053 | } |
1054 | |
1055 | // Sets the focus to the "magnetic" widget inside the window |
1056 | void Manager::attractFocus(Widget* widget) |
1057 | { |
1058 | // Get the magnetic widget |
1059 | Widget* magnet = findMagneticWidget(widget->window()); |
1060 | |
1061 | // If magnetic widget exists and it doesn't have the focus |
1062 | if (magnet && !magnet->hasFocus()) |
1063 | setFocus(magnet); |
1064 | } |
1065 | |
1066 | void Manager::focusFirstChild(Widget* widget) |
1067 | { |
1068 | for (Widget* it=widget->window(); it; it=next_widget(it)) { |
1069 | if (does_accept_focus(it) && !(child_accept_focus(it, true))) { |
1070 | setFocus(it); |
1071 | break; |
1072 | } |
1073 | } |
1074 | } |
1075 | |
1076 | void Manager::freeFocus() |
1077 | { |
1078 | setFocus(nullptr); |
1079 | } |
1080 | |
1081 | void Manager::freeMouse() |
1082 | { |
1083 | setMouse(nullptr); |
1084 | } |
1085 | |
1086 | void Manager::freeCapture() |
1087 | { |
1088 | if (capture_widget) { |
1089 | Display* display = capture_widget->display(); |
1090 | |
1091 | capture_widget->disableFlags(HAS_CAPTURE); |
1092 | capture_widget = nullptr; |
1093 | |
1094 | ASSERT(display && display->nativeWindow()); |
1095 | if (display && display->nativeWindow()) |
1096 | display->nativeWindow()->releaseMouse(); |
1097 | } |
1098 | } |
1099 | |
1100 | void Manager::freeWidget(Widget* widget) |
1101 | { |
1102 | #ifdef DEBUG_UI_THREADS |
1103 | ASSERT(manager_thread == base::this_thread::native_id()); |
1104 | #endif |
1105 | |
1106 | if (widget->hasFocus() || (widget == focus_widget)) |
1107 | freeFocus(); |
1108 | |
1109 | // We shouldn't free widgets that are locked, it means, widgets that |
1110 | // will be re-added soon (e.g. when the stack of windows is |
1111 | // temporarily modified). |
1112 | if (m_lockedWindow == widget) |
1113 | return; |
1114 | |
1115 | // Break any relationship with the GUI manager |
1116 | if (widget->hasCapture() || (widget == capture_widget)) |
1117 | freeCapture(); |
1118 | |
1119 | if (widget->hasMouse() || (widget == mouse_widget)) |
1120 | freeMouse(); |
1121 | |
1122 | auto it = std::find(mouse_widgets_list.begin(), |
1123 | mouse_widgets_list.end(), |
1124 | widget); |
1125 | if (it != mouse_widgets_list.end()) |
1126 | mouse_widgets_list.erase(it); |
1127 | |
1128 | ASSERT(!Manager::widgetAssociatedToManager(widget)); |
1129 | } |
1130 | |
1131 | void Manager::removeMessagesFor(Widget* widget) |
1132 | { |
1133 | #ifdef DEBUG_UI_THREADS |
1134 | ASSERT(manager_thread == base::this_thread::native_id()); |
1135 | #endif |
1136 | |
1137 | for (Message* msg : msg_queue) |
1138 | msg->removeRecipient(widget); |
1139 | |
1140 | for (Message* msg : used_msg_queue) |
1141 | msg->removeRecipient(widget); |
1142 | } |
1143 | |
1144 | void Manager::removeMessagesFor(Widget* widget, MessageType type) |
1145 | { |
1146 | #ifdef DEBUG_UI_THREADS |
1147 | ASSERT(manager_thread == base::this_thread::native_id()); |
1148 | #endif |
1149 | |
1150 | for (Message* msg : msg_queue) |
1151 | if (msg->type() == type) |
1152 | msg->removeRecipient(widget); |
1153 | |
1154 | for (Message* msg : used_msg_queue) |
1155 | if (msg->type() == type) |
1156 | msg->removeRecipient(widget); |
1157 | } |
1158 | |
1159 | void Manager::removeMessagesForTimer(Timer* timer) |
1160 | { |
1161 | #ifdef DEBUG_UI_THREADS |
1162 | ASSERT(manager_thread == base::this_thread::native_id()); |
1163 | #endif |
1164 | |
1165 | for (Message* msg : msg_queue) { |
1166 | if (msg->type() == kTimerMessage && |
1167 | static_cast<TimerMessage*>(msg)->timer() == timer) { |
1168 | msg->removeRecipient(msg->recipient()); |
1169 | static_cast<TimerMessage*>(msg)->_resetTimer(); |
1170 | } |
1171 | } |
1172 | |
1173 | for (Message* msg : used_msg_queue) { |
1174 | if (msg->type() == kTimerMessage && |
1175 | static_cast<TimerMessage*>(msg)->timer() == timer) { |
1176 | msg->removeRecipient(msg->recipient()); |
1177 | static_cast<TimerMessage*>(msg)->_resetTimer(); |
1178 | } |
1179 | } |
1180 | } |
1181 | |
1182 | void Manager::removeMessagesForDisplay(Display* display) |
1183 | { |
1184 | #ifdef DEBUG_UI_THREADS |
1185 | ASSERT(manager_thread == base::this_thread::native_id()); |
1186 | #endif |
1187 | |
1188 | for (Message* msg : msg_queue) { |
1189 | if (msg->display() == display) { |
1190 | msg->removeRecipient(msg->recipient()); |
1191 | msg->setDisplay(nullptr); |
1192 | } |
1193 | } |
1194 | |
1195 | for (Message* msg : used_msg_queue) { |
1196 | if (msg->display() == display) { |
1197 | msg->removeRecipient(msg->recipient()); |
1198 | msg->setDisplay(nullptr); |
1199 | } |
1200 | } |
1201 | } |
1202 | |
1203 | void Manager::removePaintMessagesForDisplay(Display* display) |
1204 | { |
1205 | #ifdef DEBUG_UI_THREADS |
1206 | ASSERT(manager_thread == base::this_thread::native_id()); |
1207 | #endif |
1208 | |
1209 | for (auto it=msg_queue.begin(); it != msg_queue.end(); ) { |
1210 | Message* msg = *it; |
1211 | if (msg->type() == kPaintMessage && |
1212 | msg->display() == display) { |
1213 | delete msg; |
1214 | it = msg_queue.erase(it); |
1215 | } |
1216 | else |
1217 | ++it; |
1218 | } |
1219 | } |
1220 | |
1221 | void Manager::addMessageFilter(int message, Widget* widget) |
1222 | { |
1223 | #ifdef DEBUG_UI_THREADS |
1224 | ASSERT(manager_thread == base::this_thread::native_id()); |
1225 | #endif |
1226 | |
1227 | LockFilters lock; |
1228 | int c = message; |
1229 | if (c >= kFirstRegisteredMessage) |
1230 | c = kFirstRegisteredMessage; |
1231 | |
1232 | msg_filters[c].push_back(new Filter(message, widget)); |
1233 | } |
1234 | |
1235 | void Manager::removeMessageFilter(int message, Widget* widget) |
1236 | { |
1237 | #ifdef DEBUG_UI_THREADS |
1238 | ASSERT(manager_thread == base::this_thread::native_id()); |
1239 | #endif |
1240 | |
1241 | LockFilters lock; |
1242 | int c = message; |
1243 | if (c >= kFirstRegisteredMessage) |
1244 | c = kFirstRegisteredMessage; |
1245 | |
1246 | Filters& msg_filter = msg_filters[c]; |
1247 | for (Filter* filter : msg_filter) { |
1248 | if (filter->widget == widget) |
1249 | filter->widget = nullptr; |
1250 | } |
1251 | } |
1252 | |
1253 | void Manager::removeMessageFilterFor(Widget* widget) |
1254 | { |
1255 | #ifdef DEBUG_UI_THREADS |
1256 | ASSERT(manager_thread == base::this_thread::native_id()); |
1257 | #endif |
1258 | |
1259 | LockFilters lock; |
1260 | for (Filters& msg_filter : msg_filters) { |
1261 | for (Filter* filter : msg_filter) { |
1262 | if (filter->widget == widget) |
1263 | filter->widget = nullptr; |
1264 | } |
1265 | } |
1266 | } |
1267 | |
1268 | bool Manager::isFocusMovementMessage(Message* msg) |
1269 | { |
1270 | if (msg->type() != kKeyDownMessage && |
1271 | msg->type() != kKeyUpMessage) |
1272 | return false; |
1273 | |
1274 | switch (static_cast<KeyMessage*>(msg)->scancode()) { |
1275 | case kKeyTab: |
1276 | case kKeyLeft: |
1277 | case kKeyRight: |
1278 | case kKeyUp: |
1279 | case kKeyDown: |
1280 | return true; |
1281 | } |
1282 | return false; |
1283 | } |
1284 | |
1285 | Widget* Manager::pickFromScreenPos(const gfx::Point& screenPos) const |
1286 | { |
1287 | Display* mainDisplay = display(); |
1288 | |
1289 | if (get_multiple_displays()) { |
1290 | for (auto child : children()) { |
1291 | auto window = static_cast<Window*>(child); |
1292 | if (window->ownDisplay() || |
1293 | window->display() != mainDisplay) { |
1294 | os::Window* nativeWindow = window->display()->nativeWindow(); |
1295 | if (nativeWindow->frame().contains(screenPos)) |
1296 | return window->pick(nativeWindow->pointFromScreen(screenPos)); |
1297 | } |
1298 | } |
1299 | |
1300 | gfx::Point displayPos = display()->nativeWindow()->pointFromScreen(screenPos); |
1301 | for (auto child : children()) { |
1302 | auto window = static_cast<Window*>(child); |
1303 | if (window->display() == mainDisplay) { |
1304 | if (auto picked = window->pick(displayPos)) |
1305 | return picked; |
1306 | } |
1307 | } |
1308 | } |
1309 | return Widget::pickFromScreenPos(screenPos); |
1310 | } |
1311 | |
1312 | void Manager::_closingAppWithException() |
1313 | { |
1314 | redrawState = RedrawState::ClosingApp; |
1315 | } |
1316 | |
1317 | // Configures the window for begin the loop |
1318 | void Manager::_openWindow(Window* window, bool center) |
1319 | { |
1320 | Display* parentDisplay = getForegroundDisplay(); |
1321 | ASSERT(parentDisplay); |
1322 | |
1323 | // Opening other window in the "close app" state, ok, let's back to normal. |
1324 | if (redrawState == RedrawState::ClosingApp) |
1325 | redrawState = RedrawState::Normal; |
1326 | |
1327 | // Free all widgets of special states. |
1328 | if (window->isWantFocus()) { |
1329 | freeCapture(); |
1330 | freeMouse(); |
1331 | freeFocus(); |
1332 | } |
1333 | |
1334 | // Add the window to manager. |
1335 | insertChild(0, window); |
1336 | |
1337 | // Broadcast the open message. |
1338 | { |
1339 | Message msg(kOpenMessage); |
1340 | window->sendMessage(&msg); |
1341 | } |
1342 | |
1343 | // Relayout |
1344 | if (center) |
1345 | window->centerWindow(parentDisplay); |
1346 | else |
1347 | window->layout(); |
1348 | |
1349 | // If the window already was set a display, we don't setup it |
1350 | // (i.e. in the case of combobox popup/window the display field is |
1351 | // set to the same display where the ComboBox widget is located) |
1352 | if (!window->hasDisplaySet()) { |
1353 | // In other case, we can try to create a display/native window for |
1354 | // the UI window. |
1355 | if (get_multiple_displays() |
1356 | && window->shouldCreateNativeWindow()) { |
1357 | const int scale = parentDisplay->nativeWindow()->scale(); |
1358 | |
1359 | os::WindowSpec spec; |
1360 | gfx::Rect frame; |
1361 | bool changeFrame; |
1362 | if (!window->lastNativeFrame().isEmpty()) { |
1363 | frame = window->lastNativeFrame(); |
1364 | changeFrame = true; |
1365 | } |
1366 | else { |
1367 | gfx::Rect relativeToFrame = parentDisplay->nativeWindow()->contentRect(); |
1368 | frame = window->bounds(); |
1369 | frame *= scale; |
1370 | frame.offset(relativeToFrame.origin()); |
1371 | changeFrame = false; |
1372 | } |
1373 | |
1374 | limit_with_workarea(parentDisplay, frame); |
1375 | |
1376 | spec.position(os::WindowSpec::Position::Frame); |
1377 | spec.frame(frame); |
1378 | spec.scale(scale); |
1379 | // Only desktop will have the real native window title bar |
1380 | // TODO in the future other windows could use the native title bar |
1381 | // when there are no special decorators (or we could just add |
1382 | // the possibility to create new buttons in the native window |
1383 | // title bar) |
1384 | spec.titled(window->isDesktop()); |
1385 | spec.floating(!window->isDesktop()); |
1386 | spec.resizable(window->isDesktop() || window->isSizeable()); |
1387 | spec.maximizable(spec.resizable()); |
1388 | spec.minimizable(window->isDesktop()); |
1389 | spec.borderless(!window->isDesktop()); |
1390 | spec.transparent(window->isTransparent()); |
1391 | |
1392 | if (!window->isDesktop()) { |
1393 | spec.parent(parentDisplay->nativeWindow()); |
1394 | } |
1395 | |
1396 | os::WindowRef newNativeWindow = os::instance()->makeWindow(spec); |
1397 | ui::Display* newDisplay = new ui::Display(parentDisplay, newNativeWindow, window); |
1398 | |
1399 | newNativeWindow->setUserData(newDisplay); |
1400 | window->setDisplay(newDisplay, true); |
1401 | |
1402 | // Set native title bar text |
1403 | newNativeWindow->setTitle(window->text()); |
1404 | |
1405 | // Activate only non-floating windows |
1406 | if (!spec.floating()) |
1407 | newNativeWindow->activate(); |
1408 | else |
1409 | m_display.nativeWindow()->activate(); |
1410 | |
1411 | // Move all widgets to the os::Display origin (0,0) |
1412 | if (changeFrame) { |
1413 | window->setBounds(newNativeWindow->bounds() / scale); |
1414 | } |
1415 | else { |
1416 | window->offsetWidgets(-window->origin().x, -window->origin().y); |
1417 | } |
1418 | |
1419 | // Handle native hit testing. Required to be able to move/resize |
1420 | // a window with a non-mouse pointer (e.g. stylus) on Windows. |
1421 | newNativeWindow->handleHitTest = handle_native_hittest; |
1422 | } |
1423 | else { |
1424 | // Same display for desktop window or when multiple displays is |
1425 | // disabled. |
1426 | window->setDisplay(this->display(), false); |
1427 | } |
1428 | } |
1429 | |
1430 | // Dirty the entire window and show it |
1431 | window->setVisible(true); |
1432 | window->invalidate(); |
1433 | |
1434 | // Attract the focus to the magnetic widget... |
1435 | // 1) get the magnetic widget |
1436 | Widget* magnet = findMagneticWidget(window); |
1437 | // 2) if magnetic widget exists and it doesn't have the focus |
1438 | if (magnet && !magnet->hasFocus()) |
1439 | setFocus(magnet); |
1440 | // 3) if not, put the focus in the first child |
1441 | else if (window->isWantFocus()) |
1442 | focusFirstChild(window); |
1443 | |
1444 | // Update mouse widget (as it can be a widget below the |
1445 | // recently opened window). |
1446 | updateMouseWidgets(ui::get_mouse_position(), nullptr); |
1447 | } |
1448 | |
1449 | void Manager::_closeWindow(Window* window, bool redraw_background) |
1450 | { |
1451 | if (!hasChild(window)) |
1452 | return; |
1453 | |
1454 | gfx::Region reg1; |
1455 | if (!window->ownDisplay()) { |
1456 | if (redraw_background) |
1457 | window->getRegion(reg1); |
1458 | } |
1459 | |
1460 | // Close all windows to this desktop |
1461 | if (window->isDesktop()) { |
1462 | while (!children().empty()) { |
1463 | Window* child = static_cast<Window*>(children().front()); |
1464 | if (child == window) |
1465 | break; |
1466 | else { |
1467 | gfx::Region reg2; |
1468 | window->getRegion(reg2); |
1469 | reg1 |= reg2; |
1470 | |
1471 | _closeWindow(child, false); |
1472 | } |
1473 | } |
1474 | } |
1475 | |
1476 | // Free all widgets of special states. |
1477 | if (capture_widget && capture_widget->window() == window) |
1478 | freeCapture(); |
1479 | |
1480 | if (mouse_widget && mouse_widget->window() == window) |
1481 | freeMouse(); |
1482 | |
1483 | if (focus_widget && focus_widget->window() == window) |
1484 | freeFocus(); |
1485 | |
1486 | // Hide window. |
1487 | window->setVisible(false); |
1488 | |
1489 | // Close message. |
1490 | { |
1491 | Message msg(kCloseMessage); |
1492 | window->sendMessage(&msg); |
1493 | } |
1494 | |
1495 | // Destroy native window associated with this window's display if needed |
1496 | Display* windowDisplay = window->display(); |
1497 | Display* parentDisplay; |
1498 | if (// The display can be nullptr if the window was not opened or |
1499 | // was closed before. |
1500 | window->ownDisplay()) { |
1501 | parentDisplay = (windowDisplay ? windowDisplay->parentDisplay(): nullptr); |
1502 | ASSERT(parentDisplay); |
1503 | ASSERT(windowDisplay); |
1504 | ASSERT(windowDisplay != this->display()); |
1505 | |
1506 | // We are receiving several crashes from Windows users where |
1507 | // parentDisplay != nullptr and parentDisplay->nativeWindow() == |
1508 | // nullptr, so we have to do some extra checks in these places |
1509 | // (anyway this might produce some crashes in other places) |
1510 | os::Window* nativeWindow = (windowDisplay ? windowDisplay->nativeWindow(): nullptr); |
1511 | os::Window* parentNativeWindow = (parentDisplay ? parentDisplay->nativeWindow(): nullptr); |
1512 | ASSERT(nativeWindow); |
1513 | ASSERT(parentNativeWindow); |
1514 | |
1515 | // Just as we've set the origin of the window bounds to (0, 0) |
1516 | // when we created the native window, we have to restore the |
1517 | // ui::Window bounds' origin now that we are going to remove/close |
1518 | // the native window. |
1519 | if (parentNativeWindow && nativeWindow) { |
1520 | const int scale = parentNativeWindow->scale(); |
1521 | const gfx::Point parentOrigin = parentNativeWindow->contentRect().origin(); |
1522 | const gfx::Point origin = nativeWindow->contentRect().origin(); |
1523 | const gfx::Rect newBounds((origin - parentOrigin) / scale, |
1524 | window->bounds().size()); |
1525 | window->setBounds(newBounds); |
1526 | } |
1527 | |
1528 | // Set the native window user data to nullptr so any other queued |
1529 | // native message is not processed. |
1530 | window->setDisplay(nullptr, false); |
1531 | if (nativeWindow) |
1532 | nativeWindow->setUserData<void*>(nullptr); |
1533 | |
1534 | // Remove all messages for this display. |
1535 | removeMessagesForDisplay(windowDisplay); |
1536 | |
1537 | // Remove the mouse cursor from the display that we are going to |
1538 | // delete. |
1539 | _internal_set_mouse_display(parentDisplay); |
1540 | |
1541 | // Remove the display that we're going to delete (windowDisplay) |
1542 | // as parent of any other existent display. |
1543 | for (auto otherChild : children()) { |
1544 | if (auto otherWindow = static_cast<Window*>(otherChild)) { |
1545 | if (otherWindow != window && |
1546 | otherWindow->display() && |
1547 | otherWindow->display()->parentDisplay() == windowDisplay) { |
1548 | otherWindow->display()->_setParentDisplay(parentDisplay); |
1549 | } |
1550 | } |
1551 | } |
1552 | |
1553 | // The ui::Display should destroy the os::Window |
1554 | delete windowDisplay; |
1555 | |
1556 | // Activate main windows |
1557 | if (parentNativeWindow) |
1558 | parentNativeWindow->activate(); |
1559 | } |
1560 | else { |
1561 | parentDisplay = windowDisplay; |
1562 | window->setDisplay(nullptr, false); |
1563 | } |
1564 | |
1565 | // Update manager list stuff. |
1566 | removeChild(window); |
1567 | |
1568 | // Redraw background. |
1569 | parentDisplay->containedWidget()->invalidateRegion(reg1); |
1570 | |
1571 | // Update mouse widget (as it can be a widget below the |
1572 | // recently closed window). |
1573 | updateMouseWidgets(ui::get_mouse_position(), nullptr); |
1574 | |
1575 | if (redrawState != RedrawState::ClosingApp) { |
1576 | if (children().empty()) { |
1577 | // All windows were closed... |
1578 | redrawState = RedrawState::ClosingApp; |
1579 | } |
1580 | else { |
1581 | redrawState = RedrawState::AWindowHasJustBeenClosed; |
1582 | } |
1583 | } |
1584 | } |
1585 | |
1586 | void Manager::_runModalWindow(Window* window) |
1587 | { |
1588 | MessageLoop loop(manager()); |
1589 | while (!window->hasFlags(HIDDEN)) |
1590 | loop.pumpMessages(); |
1591 | } |
1592 | |
1593 | void Manager::_updateMouseWidgets() |
1594 | { |
1595 | // Update mouse widget. |
1596 | updateMouseWidgets(ui::get_mouse_position(), nullptr); |
1597 | } |
1598 | |
1599 | bool Manager::onProcessMessage(Message* msg) |
1600 | { |
1601 | switch (msg->type()) { |
1602 | |
1603 | case kPaintMessage: |
1604 | // Draw nothing (the manager should be invisible). On Windows, |
1605 | // after closing the main window, the manager will not refresh |
1606 | // the os::Display content, so we'll avoid a gray background |
1607 | // (the last main window content is kept until the Display is |
1608 | // finally closed.) |
1609 | return true; |
1610 | |
1611 | case kCloseDisplayMessage: { |
1612 | if (msg->display() != &m_display) { |
1613 | if (Window* window = dynamic_cast<Window*>(msg->display()->containedWidget())) { |
1614 | window->closeWindow(this); |
1615 | } |
1616 | } |
1617 | break; |
1618 | } |
1619 | |
1620 | case kResizeDisplayMessage: |
1621 | onNewDisplayConfiguration(msg->display()); |
1622 | break; |
1623 | |
1624 | case kKeyDownMessage: |
1625 | case kKeyUpMessage: { |
1626 | KeyMessage* keymsg = static_cast<KeyMessage*>(msg); |
1627 | keymsg->setPropagateToChildren(true); |
1628 | keymsg->setPropagateToParent(false); |
1629 | |
1630 | // Continue sending the message to the children of all windows |
1631 | // (until a desktop or foreground window). |
1632 | Window* win = nullptr; |
1633 | for (auto manchild : children()) { |
1634 | win = static_cast<Window*>(manchild); |
1635 | |
1636 | // Send to the window. |
1637 | for (auto winchild : win->children()) |
1638 | if (winchild->sendMessage(msg)) |
1639 | return true; |
1640 | |
1641 | if (win->isForeground() || |
1642 | win->isDesktop()) |
1643 | break; |
1644 | } |
1645 | |
1646 | // Check the focus movement for foreground (non-desktop) windows. |
1647 | if (win && win->isForeground()) { |
1648 | if (msg->type() == kKeyDownMessage) |
1649 | processFocusMovementMessage(msg); |
1650 | return true; |
1651 | } |
1652 | else |
1653 | return false; |
1654 | } |
1655 | |
1656 | } |
1657 | |
1658 | return Widget::onProcessMessage(msg); |
1659 | } |
1660 | |
1661 | void Manager::onResize(ResizeEvent& ev) |
1662 | { |
1663 | gfx::Rect old_pos = bounds(); |
1664 | gfx::Rect new_pos = ev.bounds(); |
1665 | setBoundsQuietly(new_pos); |
1666 | |
1667 | // The whole manager area is invalid now. |
1668 | m_display.setInvalidRegion(gfx::Region(new_pos)); |
1669 | |
1670 | const int dx = new_pos.x - old_pos.x; |
1671 | const int dy = new_pos.y - old_pos.y; |
1672 | const int dw = new_pos.w - old_pos.w; |
1673 | const int dh = new_pos.h - old_pos.h; |
1674 | |
1675 | for (auto child : children()) { |
1676 | Window* window = static_cast<Window*>(child); |
1677 | if (window->ownDisplay()) |
1678 | continue; |
1679 | |
1680 | if (window->isDesktop()) { |
1681 | window->setBounds(new_pos); |
1682 | break; |
1683 | } |
1684 | |
1685 | gfx::Rect bounds = window->bounds(); |
1686 | const int cx = bounds.x+bounds.w/2; |
1687 | const int cy = bounds.y+bounds.h/2; |
1688 | |
1689 | if (auto_window_adjustment) { |
1690 | if (cx > old_pos.x+old_pos.w*3/5) { |
1691 | bounds.x += dw; |
1692 | } |
1693 | else if (cx > old_pos.x+old_pos.w*2/5) { |
1694 | bounds.x += dw / 2; |
1695 | } |
1696 | |
1697 | if (cy > old_pos.y+old_pos.h*3/5) { |
1698 | bounds.y += dh; |
1699 | } |
1700 | else if (cy > old_pos.y+old_pos.h*2/5) { |
1701 | bounds.y += dh / 2; |
1702 | } |
1703 | |
1704 | bounds.offset(dx, dy); |
1705 | } |
1706 | else { |
1707 | if (bounds.x2() > new_pos.x2()) { |
1708 | bounds.x = new_pos.x2() - bounds.w; |
1709 | } |
1710 | if (bounds.y2() > new_pos.y2()) { |
1711 | bounds.y = new_pos.y2() - bounds.h; |
1712 | } |
1713 | } |
1714 | window->setBounds(bounds); |
1715 | } |
1716 | } |
1717 | |
1718 | void Manager::onBroadcastMouseMessage(const gfx::Point& screenPos, |
1719 | WidgetsList& targets) |
1720 | { |
1721 | // Ask to the first window in the "children" list to know how to |
1722 | // propagate mouse messages. |
1723 | Widget* widget = UI_FIRST_WIDGET(children()); |
1724 | if (widget) |
1725 | widget->broadcastMouseMessage(screenPos, targets); |
1726 | } |
1727 | |
1728 | void Manager::onInitTheme(InitThemeEvent& ev) |
1729 | { |
1730 | Widget::onInitTheme(ev); |
1731 | |
1732 | // Remap the windows |
1733 | const int oldUIScale = ui::details::old_guiscale(); |
1734 | const int newUIScale = ui::guiscale(); |
1735 | for (auto widget : children()) { |
1736 | if (widget->type() == kWindowWidget) { |
1737 | auto window = static_cast<Window*>(widget); |
1738 | if (window->isDesktop()) { |
1739 | window->layout(); |
1740 | } |
1741 | else { |
1742 | gfx::Size displaySize = m_display.size(); |
1743 | gfx::Rect bounds = window->bounds(); |
1744 | bounds *= newUIScale; |
1745 | bounds /= oldUIScale; |
1746 | bounds.x = std::clamp(bounds.x, 0, displaySize.w - bounds.w); |
1747 | bounds.y = std::clamp(bounds.y, 0, displaySize.h - bounds.h); |
1748 | window->setBounds(bounds); |
1749 | } |
1750 | } |
1751 | } |
1752 | } |
1753 | |
1754 | LayoutIO* Manager::onGetLayoutIO() |
1755 | { |
1756 | return nullptr; |
1757 | } |
1758 | |
1759 | void Manager::onNewDisplayConfiguration(Display* display) |
1760 | { |
1761 | ASSERT(display); |
1762 | Widget* container = display->containedWidget(); |
1763 | |
1764 | gfx::Size displaySize = display->size(); |
1765 | if ((bounds().w != displaySize.w || |
1766 | bounds().h != displaySize.h)) { |
1767 | container->setBounds(gfx::Rect(displaySize)); |
1768 | } |
1769 | |
1770 | // The native window can be nullptr when running tests. |
1771 | if (!display->nativeWindow()) |
1772 | return; |
1773 | |
1774 | _internal_set_mouse_display(display); |
1775 | container->invalidate(); |
1776 | container->flushRedraw(); |
1777 | } |
1778 | |
1779 | void Manager::onSizeHint(SizeHintEvent& ev) |
1780 | { |
1781 | int w = 0, h = 0; |
1782 | |
1783 | if (!parent()) { // hasn' parent? |
1784 | w = bounds().w; |
1785 | h = bounds().h; |
1786 | } |
1787 | else { |
1788 | gfx::Rect pos = parent()->childrenBounds(); |
1789 | |
1790 | for (auto child : children()) { |
1791 | gfx::Rect cpos = child->bounds(); |
1792 | pos = pos.createUnion(cpos); |
1793 | } |
1794 | |
1795 | w = pos.w; |
1796 | h = pos.h; |
1797 | } |
1798 | |
1799 | ev.setSizeHint(gfx::Size(w, h)); |
1800 | } |
1801 | |
1802 | int Manager::pumpQueue() |
1803 | { |
1804 | #ifdef DEBUG_UI_THREADS |
1805 | ASSERT(manager_thread == base::this_thread::native_id()); |
1806 | #endif |
1807 | |
1808 | #ifdef LIMIT_DISPATCH_TIME |
1809 | base::tick_t t = base::current_tick(); |
1810 | #endif |
1811 | |
1812 | int count = 0; // Number of processed messages |
1813 | while (!msg_queue.empty()) { |
1814 | #ifdef LIMIT_DISPATCH_TIME |
1815 | if (base::current_tick()-t > 250) |
1816 | break; |
1817 | #endif |
1818 | |
1819 | // The message to process |
1820 | auto it = msg_queue.begin(); |
1821 | Message* msg = *it; |
1822 | ASSERT(msg); |
1823 | |
1824 | // Move the message from msg_queue to used_msg_queue |
1825 | msg_queue.erase(it); |
1826 | auto eraseIt = used_msg_queue.insert(used_msg_queue.end(), msg); |
1827 | |
1828 | // Call Timer::tick() if this is a tick message. |
1829 | if (msg->type() == kTimerMessage) { |
1830 | // The timer can be nullptr if it was removed with removeMessagesForTimer() |
1831 | if (auto timer = static_cast<TimerMessage*>(msg)->timer()) |
1832 | timer->tick(); |
1833 | } |
1834 | |
1835 | bool done = false; |
1836 | |
1837 | // Send this message to filters |
1838 | { |
1839 | Filters& msg_filter = msg_filters[std::min(msg->type(), kFirstRegisteredMessage)]; |
1840 | if (!msg_filter.empty()) { |
1841 | LockFilters lock; |
1842 | for (Filter* filter : msg_filter) { |
1843 | // The widget can be nullptr in case that the filter was |
1844 | // "pre-removed" (it'll finally erased from the |
1845 | // msg_filter list from ~LockFilters()). |
1846 | if (filter->widget != nullptr && |
1847 | msg->type() == filter->message) { |
1848 | msg->setFromFilter(true); |
1849 | done = sendMessageToWidget(msg, filter->widget); |
1850 | msg->setFromFilter(false); |
1851 | |
1852 | if (done) |
1853 | break; |
1854 | } |
1855 | } |
1856 | } |
1857 | } |
1858 | |
1859 | if (!done) { |
1860 | // Then send the message to its recipient |
1861 | if (Widget* widget = msg->recipient()) |
1862 | done = sendMessageToWidget(msg, widget); |
1863 | } |
1864 | |
1865 | // Remove the message from the used_msg_queue |
1866 | used_msg_queue.erase(eraseIt); |
1867 | |
1868 | // Destroy the message |
1869 | delete msg; |
1870 | ++count; |
1871 | } |
1872 | |
1873 | return count; |
1874 | } |
1875 | |
1876 | bool Manager::sendMessageToWidget(Message* msg, Widget* widget) |
1877 | { |
1878 | #ifdef DEBUG_UI_THREADS |
1879 | ASSERT(manager_thread == base::this_thread::native_id()); |
1880 | #endif |
1881 | |
1882 | if (!widget) |
1883 | return false; |
1884 | |
1885 | #ifdef REPORT_EVENTS |
1886 | { |
1887 | static const char* msg_name[] = { |
1888 | "kOpenMessage" , |
1889 | "kCloseMessage" , |
1890 | "kCloseDisplayMessage" , |
1891 | "kResizeDisplayMessage" , |
1892 | "kPaintMessage" , |
1893 | "kTimerMessage" , |
1894 | "kDropFilesMessage" , |
1895 | "kWinMoveMessage" , |
1896 | |
1897 | "kKeyDownMessage" , |
1898 | "kKeyUpMessage" , |
1899 | "kFocusEnterMessage" , |
1900 | "kFocusLeaveMessage" , |
1901 | |
1902 | "kMouseDownMessage" , |
1903 | "kMouseUpMessage" , |
1904 | "kDoubleClickMessage" , |
1905 | "kMouseEnterMessage" , |
1906 | "kMouseLeaveMessage" , |
1907 | "kMouseMoveMessage" , |
1908 | "kSetCursorMessage" , |
1909 | "kMouseWheelMessage" , |
1910 | "kTouchMagnifyMessage" , |
1911 | }; |
1912 | static_assert(kOpenMessage == 0 && |
1913 | kTouchMagnifyMessage == sizeof(msg_name)/sizeof(const char*)-1, |
1914 | "MessageType enum has changed" ); |
1915 | const char* string = |
1916 | (msg->type() >= 0 && |
1917 | msg->type() < sizeof(msg_name)/sizeof(const char*)) ? |
1918 | msg_name[msg->type()]: "Unknown" ; |
1919 | |
1920 | TRACEARGS("Event" , msg->type(), "(" , string, ")" , |
1921 | "for" , ((void*)widget), |
1922 | typeid(*widget).name(), |
1923 | widget->id().empty()); |
1924 | } |
1925 | #endif |
1926 | |
1927 | bool used = false; |
1928 | |
1929 | // We need to configure the clip region for paint messages |
1930 | // before we call Widget::sendMessage(). |
1931 | if (msg->type() == kPaintMessage) { |
1932 | if (widget->hasFlags(HIDDEN)) |
1933 | return false; |
1934 | |
1935 | // Ignore all paint messages when we are closing the app |
1936 | if (redrawState == RedrawState::ClosingApp) |
1937 | return false; |
1938 | |
1939 | PaintMessage* paintMsg = static_cast<PaintMessage*>(msg); |
1940 | Display* display = paintMsg->display(); |
1941 | |
1942 | // TODO use paintMsg->display() here |
1943 | // Restore overlays in the region that we're going to paint. |
1944 | OverlayManager::instance()->restoreOverlappedAreas(paintMsg->rect()); |
1945 | |
1946 | os::SurfaceRef surface(base::AddRef(display->surface())); |
1947 | surface->saveClip(); |
1948 | |
1949 | if (surface->clipRect(paintMsg->rect())) { |
1950 | #ifdef REPORT_EVENTS |
1951 | TRACEARGS(" - clipRect " , paintMsg->rect()); |
1952 | #endif |
1953 | |
1954 | #ifdef DEBUG_PAINT_EVENTS |
1955 | { |
1956 | os::SurfaceLock lock(surface); |
1957 | os::Paint p; |
1958 | p.color(gfx::rgba(0, 0, 255)); |
1959 | p.style(os::Paint::Fill); |
1960 | surface->drawRect(paintMsg->rect(), p); |
1961 | |
1962 | display->nativeWindow() |
1963 | ->invalidateRegion(gfx::Region(paintMsg->rect())); |
1964 | |
1965 | #ifdef _WIN32 // TODO add a display->nativeWindow()->updateNow() method ?? |
1966 | HWND hwnd = (HWND)display->nativeWindow()->nativeHandle(); |
1967 | UpdateWindow(hwnd); |
1968 | #else |
1969 | base::this_thread::sleep_for(0.002); |
1970 | #endif |
1971 | } |
1972 | #endif |
1973 | |
1974 | // Call the message handler |
1975 | used = widget->sendMessage(msg); |
1976 | } |
1977 | |
1978 | // Restore clip region for paint messages. |
1979 | surface->restoreClip(); |
1980 | |
1981 | // As this kPaintMessage's rectangle was updated, we can |
1982 | // remove it from "m_invalidRegion". |
1983 | display->subtractInvalidRegion(gfx::Region(paintMsg->rect())); |
1984 | } |
1985 | else { |
1986 | // Call the message handler |
1987 | used = widget->sendMessage(msg); |
1988 | } |
1989 | |
1990 | return used; |
1991 | } |
1992 | |
1993 | // It's like Widget::onInvalidateRegion() but optimized for the |
1994 | // Manager (as we know that all children in a Manager will be windows, |
1995 | // we can use this knowledge to avoid some calculations). |
1996 | // |
1997 | // TODO similar to Window::onInvalidateRegion |
1998 | void Manager::onInvalidateRegion(const gfx::Region& region) |
1999 | { |
2000 | if (!isVisible() || region.contains(bounds()) == gfx::Region::Out) |
2001 | return; |
2002 | |
2003 | // Intersect only with manager bounds, we don't need to use |
2004 | // getDrawableRegion() because each window will be processed in the |
2005 | // following for() loop (and it's highly probable that a desktop |
2006 | // Window will use the whole manager portion anyway). |
2007 | gfx::Region reg1; |
2008 | reg1.createIntersection(region, gfx::Region(bounds())); |
2009 | |
2010 | // Redraw windows from top to background. |
2011 | bool withDesktop = false; |
2012 | for (auto child : children()) { |
2013 | ASSERT(dynamic_cast<Window*>(child)); |
2014 | ASSERT(child->type() == kWindowWidget); |
2015 | Window* window = static_cast<Window*>(child); |
2016 | |
2017 | // Invalidating the manager only works for the main display, to |
2018 | // invalidate windows you have to invalidate them. |
2019 | if (window->ownDisplay()) |
2020 | continue; |
2021 | |
2022 | // Invalidate regions of this window |
2023 | window->invalidateRegion(reg1); |
2024 | |
2025 | // There is desktop? |
2026 | if (window->isDesktop()) { |
2027 | withDesktop = true; |
2028 | break; // Work done |
2029 | } |
2030 | |
2031 | // Clip this window area for the next window. |
2032 | gfx::Region reg2; |
2033 | window->getRegion(reg2); |
2034 | reg1.createSubtraction(reg1, reg2); |
2035 | } |
2036 | |
2037 | // Invalidate areas outside windows (only when there are not a |
2038 | // desktop window). |
2039 | if (!withDesktop) { |
2040 | // TODO we should be able to modify m_updateRegion directly here, |
2041 | // so we avoid the getDrawableRegion() call from |
2042 | // Widget::onInvalidateRegion(). |
2043 | Widget::onInvalidateRegion(reg1); |
2044 | } |
2045 | } |
2046 | |
2047 | LayoutIO* Manager::getLayoutIO() |
2048 | { |
2049 | return onGetLayoutIO(); |
2050 | } |
2051 | |
2052 | void Manager::collectGarbage() |
2053 | { |
2054 | if (m_garbage.empty()) |
2055 | return; |
2056 | |
2057 | GARBAGE_TRACE("Manager::collectGarbage() #objects=%d\n" , int(m_garbage.size())); |
2058 | |
2059 | for (auto widget : m_garbage) { |
2060 | GARBAGE_TRACE(" -> deleting %s %s ---\n" , |
2061 | typeid(*widget).name(), |
2062 | widget->id().c_str()); |
2063 | delete widget; |
2064 | } |
2065 | m_garbage.clear(); |
2066 | } |
2067 | |
2068 | /********************************************************************** |
2069 | Internal routines |
2070 | **********************************************************************/ |
2071 | |
2072 | // static |
2073 | Widget* Manager::findLowestCommonAncestor(Widget* a, Widget* b) |
2074 | { |
2075 | if (!a || !b) |
2076 | return nullptr; |
2077 | |
2078 | Widget* u = a; |
2079 | Widget* v = b; |
2080 | int aDepth = 0; |
2081 | int bDepth = 0; |
2082 | while (u) { |
2083 | ++aDepth; |
2084 | u = u->parent(); |
2085 | } |
2086 | while (v) { |
2087 | ++bDepth; |
2088 | v = v->parent(); |
2089 | } |
2090 | |
2091 | while (aDepth > bDepth) { |
2092 | --aDepth; |
2093 | a = a->parent(); |
2094 | } |
2095 | while (bDepth > aDepth) { |
2096 | --bDepth; |
2097 | b = b->parent(); |
2098 | } |
2099 | |
2100 | while (a && b) { |
2101 | if (a == b) |
2102 | break; |
2103 | a = a->parent(); |
2104 | b = b->parent(); |
2105 | } |
2106 | return a; |
2107 | } |
2108 | |
2109 | // static |
2110 | bool Manager::someParentIsFocusStop(Widget* widget) |
2111 | { |
2112 | if (widget->isFocusStop()) |
2113 | return true; |
2114 | |
2115 | if (widget->parent()) |
2116 | return someParentIsFocusStop(widget->parent()); |
2117 | else |
2118 | return false; |
2119 | } |
2120 | |
2121 | // static |
2122 | Widget* Manager::findMagneticWidget(Widget* widget) |
2123 | { |
2124 | Widget* found; |
2125 | |
2126 | for (auto child : widget->children()) { |
2127 | found = findMagneticWidget(child); |
2128 | if (found) |
2129 | return found; |
2130 | } |
2131 | |
2132 | if (widget->isFocusMagnet()) |
2133 | return widget; |
2134 | else |
2135 | return nullptr; |
2136 | } |
2137 | |
2138 | // static |
2139 | Message* Manager::newMouseMessage( |
2140 | MessageType type, |
2141 | Display* display, |
2142 | Widget* widget, |
2143 | const gfx::Point& mousePos, |
2144 | PointerType pointerType, |
2145 | MouseButton button, |
2146 | KeyModifiers modifiers, |
2147 | const gfx::Point& wheelDelta, |
2148 | bool preciseWheel, |
2149 | float pressure) |
2150 | { |
2151 | #ifdef __APPLE__ |
2152 | // Convert Ctrl+left click -> right-click |
2153 | if (widget && |
2154 | widget->isVisible() && |
2155 | widget->isEnabled() && |
2156 | widget->hasFlags(CTRL_RIGHT_CLICK) && |
2157 | (modifiers & kKeyCtrlModifier) && |
2158 | (button == kButtonLeft)) { |
2159 | modifiers = KeyModifiers(int(modifiers) & ~int(kKeyCtrlModifier)); |
2160 | button = kButtonRight; |
2161 | } |
2162 | #endif |
2163 | |
2164 | Message* msg = new MouseMessage( |
2165 | type, pointerType, button, modifiers, mousePos, |
2166 | wheelDelta, preciseWheel, pressure); |
2167 | |
2168 | if (display) |
2169 | msg->setDisplay(display); |
2170 | |
2171 | if (widget) |
2172 | msg->setRecipient(widget); |
2173 | |
2174 | return msg; |
2175 | } |
2176 | |
2177 | // static |
2178 | void Manager::broadcastKeyMsg(Message* msg) |
2179 | { |
2180 | // Send the message to the widget with capture |
2181 | if (capture_widget) { |
2182 | msg->setRecipient(capture_widget); |
2183 | } |
2184 | // Send the msg to the focused widget |
2185 | else if (focus_widget) { |
2186 | msg->setRecipient(focus_widget); |
2187 | } |
2188 | // Finally, send the message to the manager, it'll know what to do |
2189 | else { |
2190 | msg->setRecipient(this); |
2191 | } |
2192 | } |
2193 | |
2194 | /*********************************************************************** |
2195 | Focus Movement |
2196 | ***********************************************************************/ |
2197 | |
2198 | // TODO rewrite this function, it is based in an old code from the |
2199 | // Allegro library GUI code |
2200 | |
2201 | bool Manager::processFocusMovementMessage(Message* msg) |
2202 | { |
2203 | int (*cmp)(Widget*, int, int) = nullptr; |
2204 | Widget* focus = nullptr; |
2205 | Widget* it; |
2206 | bool ret = false; |
2207 | Window* window = nullptr; |
2208 | int c, count; |
2209 | |
2210 | // Who have the focus |
2211 | if (focus_widget) { |
2212 | window = focus_widget->window(); |
2213 | } |
2214 | else if (!this->children().empty()) { |
2215 | window = this->getTopWindow(); |
2216 | } |
2217 | |
2218 | if (!window) |
2219 | return false; |
2220 | |
2221 | // How many children want the focus in this window? |
2222 | count = count_widgets_accept_focus(window); |
2223 | |
2224 | // One at least |
2225 | if (count > 0) { |
2226 | std::vector<Widget*> list(count); |
2227 | |
2228 | c = 0; |
2229 | |
2230 | // Create a list of possible candidates to receive the focus |
2231 | for (it=focus_widget; it; it=next_widget(it)) { |
2232 | if (does_accept_focus(it) && !(child_accept_focus(it, true))) |
2233 | list[c++] = it; |
2234 | } |
2235 | for (it=window; it != focus_widget; it=next_widget(it)) { |
2236 | if (does_accept_focus(it) && !(child_accept_focus(it, true))) |
2237 | list[c++] = it; |
2238 | } |
2239 | |
2240 | // Depending on the pressed key... |
2241 | switch (static_cast<KeyMessage*>(msg)->scancode()) { |
2242 | |
2243 | case kKeyTab: |
2244 | // Reverse tab |
2245 | if ((msg->modifiers() & (kKeyShiftModifier | kKeyCtrlModifier | kKeyAltModifier)) != 0) { |
2246 | focus = list[count-1]; |
2247 | } |
2248 | // Normal tab |
2249 | else if (count > 1) { |
2250 | focus = list[1]; |
2251 | } |
2252 | ret = true; |
2253 | break; |
2254 | |
2255 | // Arrow keys |
2256 | case kKeyLeft: if (!cmp) cmp = cmp_left; [[fallthrough]]; |
2257 | case kKeyRight: if (!cmp) cmp = cmp_right; [[fallthrough]]; |
2258 | case kKeyUp: if (!cmp) cmp = cmp_up; [[fallthrough]]; |
2259 | case kKeyDown: if (!cmp) cmp = cmp_down; |
2260 | // More than one widget |
2261 | if (count > 1) { |
2262 | // Position where the focus come |
2263 | gfx::Point pt = (focus_widget ? focus_widget->bounds().center(): |
2264 | window->bounds().center()); |
2265 | |
2266 | c = (focus_widget ? 1: 0); |
2267 | |
2268 | // Rearrange the list |
2269 | for (int i=c; i<count-1; ++i) { |
2270 | for (int j=i+1; j<count; ++j) { |
2271 | // Sort the list in ascending order |
2272 | if ((*cmp)(list[i], pt.x, pt.y) > (*cmp)(list[j], pt.x, pt.y)) |
2273 | std::swap(list[i], list[j]); |
2274 | } |
2275 | } |
2276 | |
2277 | #ifdef REPORT_FOCUS_MOVEMENT |
2278 | // Print list of widgets |
2279 | for (int i=c; i<count-1; ++i) { |
2280 | TRACE("list[%d] = %d (%s)\n" , |
2281 | i, (*cmp)(list[i], pt.x, pt.y), |
2282 | typeid(*list[i]).name()); |
2283 | } |
2284 | #endif |
2285 | |
2286 | // Check if the new widget to put the focus is not in the wrong way. |
2287 | if ((*cmp)(list[c], pt.x, pt.y) < std::numeric_limits<int>::max()) |
2288 | focus = list[c]; |
2289 | } |
2290 | // If only there are one widget, put the focus in this |
2291 | else |
2292 | focus = list[0]; |
2293 | |
2294 | ret = true; |
2295 | break; |
2296 | } |
2297 | |
2298 | if ((focus) && (focus != focus_widget)) |
2299 | setFocus(focus); |
2300 | } |
2301 | |
2302 | return ret; |
2303 | } |
2304 | |
2305 | static int count_widgets_accept_focus(Widget* widget) |
2306 | { |
2307 | int count = 0; |
2308 | |
2309 | for (auto child : widget->children()) |
2310 | count += count_widgets_accept_focus(child); |
2311 | |
2312 | if ((count == 0) && (does_accept_focus(widget))) |
2313 | count++; |
2314 | |
2315 | return count; |
2316 | } |
2317 | |
2318 | static bool child_accept_focus(Widget* widget, bool first) |
2319 | { |
2320 | for (auto child : widget->children()) |
2321 | if (child_accept_focus(child, false)) |
2322 | return true; |
2323 | |
2324 | return (first ? false: does_accept_focus(widget)); |
2325 | } |
2326 | |
2327 | static Widget* next_widget(Widget* widget) |
2328 | { |
2329 | if (!widget->children().empty()) |
2330 | return UI_FIRST_WIDGET(widget->children()); |
2331 | |
2332 | while (widget->parent() && |
2333 | widget->parent()->type() != kManagerWidget) { |
2334 | WidgetsList::const_iterator begin = widget->parent()->children().begin(); |
2335 | WidgetsList::const_iterator end = widget->parent()->children().end(); |
2336 | WidgetsList::const_iterator it = std::find(begin, end, widget); |
2337 | |
2338 | ASSERT(it != end); |
2339 | |
2340 | if ((it+1) != end) |
2341 | return *(it+1); |
2342 | else |
2343 | widget = widget->parent(); |
2344 | } |
2345 | |
2346 | return nullptr; |
2347 | } |
2348 | |
2349 | static int cmp_left(Widget* widget, int x, int y) |
2350 | { |
2351 | int z = x - (widget->bounds().x+widget->bounds().w/2); |
2352 | if (z <= 0) |
2353 | return std::numeric_limits<int>::max(); |
2354 | return z + ABS((widget->bounds().y+widget->bounds().h/2) - y) * 8; |
2355 | } |
2356 | |
2357 | static int cmp_right(Widget* widget, int x, int y) |
2358 | { |
2359 | int z = (widget->bounds().x+widget->bounds().w/2) - x; |
2360 | if (z <= 0) |
2361 | return std::numeric_limits<int>::max(); |
2362 | return z + ABS((widget->bounds().y+widget->bounds().h/2) - y) * 8; |
2363 | } |
2364 | |
2365 | static int cmp_up(Widget* widget, int x, int y) |
2366 | { |
2367 | int z = y - (widget->bounds().y+widget->bounds().h/2); |
2368 | if (z <= 0) |
2369 | return std::numeric_limits<int>::max(); |
2370 | return z + ABS((widget->bounds().x+widget->bounds().w/2) - x) * 8; |
2371 | } |
2372 | |
2373 | static int cmp_down(Widget* widget, int x, int y) |
2374 | { |
2375 | int z = (widget->bounds().y+widget->bounds().h/2) - y; |
2376 | if (z <= 0) |
2377 | return std::numeric_limits<int>::max(); |
2378 | return z + ABS((widget->bounds().x+widget->bounds().w/2) - x) * 8; |
2379 | } |
2380 | |
2381 | } // namespace ui |
2382 | |