From 8f74a64f3f2f5a6273c5e7de1591585a97dff82d Mon Sep 17 00:00:00 2001 From: Richard Moe Gustavsen Date: Fri, 18 Feb 2011 09:20:06 +0100 Subject: Cocoa: implement eventdispatcher flag excludeUserInputEvents When executing modal windows, the event dispatcher would not respect the QEventLoop::ExcludeUserInputEvents. This patch implements this support. The way it now works is that we fetch events manually from NSApplication and do the dispatching ourselves in those cases. From earlier research, this patch has shown to be a bit unreliable in some cases, and might have some sideeffects. Hence the reason we avoid it if we can. But it still feels better that we try to follow the flag in those few application that wants to do this. --- src/gui/kernel/qeventdispatcher_mac.mm | 60 ++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/src/gui/kernel/qeventdispatcher_mac.mm b/src/gui/kernel/qeventdispatcher_mac.mm index b4f3805..677a736 100644 --- a/src/gui/kernel/qeventdispatcher_mac.mm +++ b/src/gui/kernel/qeventdispatcher_mac.mm @@ -561,6 +561,7 @@ bool QEventDispatcherMac::processEvents(QEventLoop::ProcessEventsFlags flags) wakeUp(); emit awake(); + bool excludeUserEvents = flags & QEventLoop::ExcludeUserInputEvents; bool retVal = false; forever { if (d->interrupt) @@ -571,7 +572,7 @@ bool QEventDispatcherMac::processEvents(QEventLoop::ProcessEventsFlags flags) NSEvent* event = 0; // First, send all previously excluded input events, if any: - if (!(flags & QEventLoop::ExcludeUserInputEvents)) { + if (!excludeUserEvents) { while (!d->queuedUserInputEvents.isEmpty()) { event = static_cast(d->queuedUserInputEvents.takeFirst()); if (!filterEvent(event)) { @@ -590,9 +591,8 @@ bool QEventDispatcherMac::processEvents(QEventLoop::ProcessEventsFlags flags) // Finally, if we are to exclude user input events, we cannot call [NSApp run] // as we then loose control over which events gets dispatched: const bool canExec_3rdParty = d->nsAppRunCalledByQt || ![NSApp isRunning]; - const bool canExec_Qt = - (flags & QEventLoop::DialogExec || flags & QEventLoop::EventLoopExec) - && !(flags & QEventLoop::ExcludeUserInputEvents); + const bool canExec_Qt = !excludeUserEvents && + (flags & QEventLoop::DialogExec || flags & QEventLoop::EventLoopExec) ; if (canExec_Qt && canExec_3rdParty) { // We can use exec-mode, meaning that we can stay in a tight loop until @@ -620,22 +620,46 @@ bool QEventDispatcherMac::processEvents(QEventLoop::ProcessEventsFlags flags) // Instead we will process all current pending events and return. d->ensureNSAppInitialized(); if (NSModalSession session = d->currentModalSession()) { - if (flags & QEventLoop::WaitForMoreEvents) - qt_mac_waitForMoreModalSessionEvents(); - NSInteger status = [NSApp runModalSession:session]; - if (status != NSRunContinuesResponse && session == d->currentModalSessionCached) { - // INVARIANT: Someone called [NSApp stopModal:] from outside the event - // dispatcher (e.g to stop a native dialog). But that call wrongly stopped - // 'session' as well. As a result, we need to restart all internal sessions: - d->temporarilyStopAllModalSessions(); - } - retVal = true; - } else do { - event = [NSApp nextEventMatchingMask:NSAnyEventMask + // INVARIANT: a modal window is executing. + if (!excludeUserEvents) { + // Since we can dispatch all kinds of events, we choose + // to use cocoa's native way of running modal sessions: + if (flags & QEventLoop::WaitForMoreEvents) + qt_mac_waitForMoreModalSessionEvents(); + NSInteger status = [NSApp runModalSession:session]; + if (status != NSRunContinuesResponse && session == d->currentModalSessionCached) { + // INVARIANT: Someone called [NSApp stopModal:] from outside the event + // dispatcher (e.g to stop a native dialog). But that call wrongly stopped + // 'session' as well. As a result, we need to restart all internal sessions: + d->temporarilyStopAllModalSessions(); + } + retVal = true; + } else do { + // Dispatch all non-user events (but que non-user events up for later). In + // this case, we need more control over which events gets dispatched, and + // cannot use [NSApp runModalSession:session]: + event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:nil - inMode:NSDefaultRunLoopMode + inMode:NSModalPanelRunLoopMode dequeue: YES]; + if (event) { + if (IsMouseOrKeyEvent(event)) { + [event retain]; + d->queuedUserInputEvents.append(event); + continue; + } + if (!filterEvent(event) && qt_mac_send_event(flags, event, 0)) + retVal = true; + } + } while (!d->interrupt && event != nil); + } else do { + // INVARIANT: No modal window is executing. + event = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:nil + inMode:NSDefaultRunLoopMode + dequeue: YES]; + if (event) { if (flags & QEventLoop::ExcludeUserInputEvents) { if (IsMouseOrKeyEvent(event)) { @@ -649,7 +673,7 @@ bool QEventDispatcherMac::processEvents(QEventLoop::ProcessEventsFlags flags) } } while (!d->interrupt && event != nil); - // Be sure to flush the Qt posted events when not using mode + // Be sure to flush the Qt posted events when not using exec mode // (exec mode will always do this call from the event loop source): if (!d->interrupt) QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData); -- cgit v0.12 From 45f8d9f78c8357c087a71346e0815c23997d67b4 Mon Sep 17 00:00:00 2001 From: Richard Moe Gustavsen Date: Fri, 18 Feb 2011 09:35:29 +0100 Subject: Cocoa: use mouse location from event This is offcourse important, and the bug was revealed when calling qApp->processEvents(QEventLoop:excludeUserInputEvents), as this call would que up mouse events and dispatch them later on. And offcourse, the mouse would have changed position at that point. --- src/gui/kernel/qcursor_mac.mm | 2 +- src/gui/kernel/qt_cocoa_helpers_mac.mm | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gui/kernel/qcursor_mac.mm b/src/gui/kernel/qcursor_mac.mm index 0d57b85..0afa3ee 100644 --- a/src/gui/kernel/qcursor_mac.mm +++ b/src/gui/kernel/qcursor_mac.mm @@ -215,7 +215,7 @@ void qt_mac_update_cursor() widgetUnderMouse = qt_button_down; } else { QPoint localPoint; - QPoint globalPoint = QCursor::pos(); + QPoint globalPoint; qt_mac_getTargetForMouseEvent(0, QEvent::None, localPoint, globalPoint, 0, &widgetUnderMouse); } qt_mac_updateCursorWithWidgetUnderMouse(widgetUnderMouse); diff --git a/src/gui/kernel/qt_cocoa_helpers_mac.mm b/src/gui/kernel/qt_cocoa_helpers_mac.mm index c8132e8..af8692d 100644 --- a/src/gui/kernel/qt_cocoa_helpers_mac.mm +++ b/src/gui/kernel/qt_cocoa_helpers_mac.mm @@ -1048,7 +1048,6 @@ QWidget *qt_mac_getTargetForKeyEvent(QWidget *widgetThatReceivedEvent) // events QWidget *qt_mac_getTargetForMouseEvent( // You can call this function without providing an event. - // If so, set returnGlobalPoint before the call. NSEvent *event, QEvent::Type eventType, QPoint &returnLocalPoint, @@ -1057,7 +1056,8 @@ QWidget *qt_mac_getTargetForMouseEvent( QWidget **returnWidgetUnderMouse) { Q_UNUSED(event); - returnGlobalPoint = flipPoint([NSEvent mouseLocation]).toPoint(); + NSPoint nsglobalpoint = event ? [[event window] convertBaseToScreen:[event locationInWindow]] : [NSEvent mouseLocation]; + returnGlobalPoint = flipPoint(nsglobalpoint).toPoint(); QWidget *mouseGrabber = QWidget::mouseGrabber(); bool buttonDownNotBlockedByModal = qt_button_down && !QApplicationPrivate::isBlockedByModal(qt_button_down); QWidget *popup = QApplication::activePopupWidget(); -- cgit v0.12 From 73ae8a593b96ead99dabb854b20a687aa849b71b Mon Sep 17 00:00:00 2001 From: Richard Moe Gustavsen Date: Mon, 21 Feb 2011 10:25:57 +0100 Subject: Mac: add support for closing dialogs using Cmd-D This is how dialogs work natively on Mac; you can choose the "Don't Save" button by using the shortcut "Cmd-D" Rev-By: jbache --- src/gui/widgets/qdialogbuttonbox.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/gui/widgets/qdialogbuttonbox.cpp b/src/gui/widgets/qdialogbuttonbox.cpp index 5f17a2b..6fe87b6 100644 --- a/src/gui/widgets/qdialogbuttonbox.cpp +++ b/src/gui/widgets/qdialogbuttonbox.cpp @@ -562,6 +562,14 @@ QPushButton *QDialogButtonBoxPrivate::createButton(QDialogButtonBox::StandardBut } else { qWarning("QDialogButtonBox::createButton: Invalid ButtonRole, button not added"); } + +#ifdef Q_WS_MAC + // Since mnemonics is off by default on Mac, we add a Cmd-D + // shortcut here to e.g. make the "Don't Save" button work nativly: + if (sbutton == QDialogButtonBox::Discard) + button->setShortcut(QKeySequence(QLatin1String("Ctrl+D"))); +#endif + return button; } -- cgit v0.12