From ed2a2dc6dae0a2523cecfd272609d322ace16145 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20R=C3=B8dal?= Date: Wed, 11 May 2011 13:57:33 +0200 Subject: Improved popup and mouse event handling. Use QApplicationPrivate::sendMouseEvent() to ensure hover events are sent. Copy most of the popup handling code from qapplication_x11.cpp --- src/plugins/platforms/xcb/qxcbwindow.cpp | 18 ++++- src/widgets/kernel/qapplication_qpa.cpp | 2 +- src/widgets/kernel/qwidgetwindow_qpa.cpp | 123 +++++++++++++++++++++++++------ 3 files changed, 117 insertions(+), 26 deletions(-) diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index 4cc07a06a4..f6d9c2f762 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -109,6 +109,7 @@ QXcbWindow::QXcbWindow(QWindow *window) | XCB_EVENT_MASK_BUTTON_MOTION | XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW + | XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_FOCUS_CHANGE }; @@ -664,13 +665,26 @@ void QXcbWindow::handleMouseEvent(xcb_button_t detail, uint16_t state, xcb_times QWindowSystemInterface::handleMouseEvent(window(), time, local, global, buttons); } -void QXcbWindow::handleEnterNotifyEvent(const xcb_enter_notify_event_t *) +void QXcbWindow::handleEnterNotifyEvent(const xcb_enter_notify_event_t *event) { + if ((event->mode != XCB_NOTIFY_MODE_NORMAL && event->mode != XCB_NOTIFY_MODE_UNGRAB) + || event->detail == XCB_NOTIFY_DETAIL_VIRTUAL + || event->detail == XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL) + { + return; + } + QWindowSystemInterface::handleEnterEvent(window()); } -void QXcbWindow::handleLeaveNotifyEvent(const xcb_leave_notify_event_t *) +void QXcbWindow::handleLeaveNotifyEvent(const xcb_leave_notify_event_t *event) { + if ((event->mode != XCB_NOTIFY_MODE_NORMAL && event->mode != XCB_NOTIFY_MODE_UNGRAB) + || event->detail == XCB_NOTIFY_DETAIL_INFERIOR) + { + return; + } + QWindowSystemInterface::handleLeaveEvent(window()); } diff --git a/src/widgets/kernel/qapplication_qpa.cpp b/src/widgets/kernel/qapplication_qpa.cpp index 3930dc8d68..4ed8eda460 100644 --- a/src/widgets/kernel/qapplication_qpa.cpp +++ b/src/widgets/kernel/qapplication_qpa.cpp @@ -199,7 +199,7 @@ void QApplicationPrivate::closePopup(QWidget *popup) } -static int openPopupCount = 0; +int openPopupCount = 0; void QApplicationPrivate::openPopup(QWidget *popup) { openPopupCount++; diff --git a/src/widgets/kernel/qwidgetwindow_qpa.cpp b/src/widgets/kernel/qwidgetwindow_qpa.cpp index e8b1a6d096..a63a6499fa 100644 --- a/src/widgets/kernel/qwidgetwindow_qpa.cpp +++ b/src/widgets/kernel/qwidgetwindow_qpa.cpp @@ -48,6 +48,11 @@ QT_BEGIN_NAMESPACE QWidget *qt_button_down = 0; // widget got last button-down +// popup control +static QWidget *qt_popup_down = 0; // popup that contains the pressed widget +extern int openPopupCount; +static bool replayPopupMouseEvent = false; + QWidgetWindow::QWidgetWindow(QWidget *widget) : m_widget(widget) { @@ -111,43 +116,115 @@ void QWidgetWindow::handleEnterLeaveEvent(QEvent *event) void QWidgetWindow::handleMouseEvent(QMouseEvent *event) { + if (qApp->d_func()->inPopupMode()) { + QWidget *activePopupWidget = qApp->activePopupWidget(); + QWidget *popup = activePopupWidget; + QPoint mapped = event->pos(); + if (popup != m_widget) + mapped = popup->mapFromGlobal(event->globalPos()); + bool releaseAfter = false; + QWidget *popupChild = popup->childAt(mapped); + + if (popup != qt_popup_down) { + qt_button_down = 0; + qt_popup_down = 0; + } + + switch (event->type()) { + case QEvent::MouseButtonPress: + case QEvent::MouseButtonDblClick: + qt_button_down = popupChild; + qt_popup_down = popup; + break; + case QEvent::MouseButtonRelease: + releaseAfter = true; + break; + default: + break; // nothing for mouse move + } + + int oldOpenPopupCount = openPopupCount; + + if (popup->isEnabled()) { + // deliver event + replayPopupMouseEvent = false; + QWidget *receiver = popup; + QPoint widgetPos = mapped; + if (qt_button_down) + receiver = qt_button_down; + else if (popupChild) + receiver = popupChild; + if (receiver != popup) + widgetPos = receiver->mapFromGlobal(event->globalPos()); + QWidget *alien = m_widget->childAt(m_widget->mapFromGlobal(event->globalPos())); + QMouseEvent e(event->type(), widgetPos, event->globalPos(), event->button(), event->buttons(), event->modifiers()); + QApplicationPrivate::sendMouseEvent(receiver, &e, alien, m_widget, &qt_button_down, qt_last_mouse_receiver); + } else { + // close disabled popups when a mouse button is pressed or released + switch (event->type()) { + case QEvent::MouseButtonPress: + case QEvent::MouseButtonDblClick: + case QEvent::MouseButtonRelease: + popup->close(); + break; + default: + break; + } + } + + if (qApp->activePopupWidget() != activePopupWidget + && replayPopupMouseEvent) { + if (m_widget->windowType() != Qt::Popup) + qt_button_down = 0; + replayPopupMouseEvent = false; + } else if (event->type() == QEvent::MouseButtonPress + && event->button() == Qt::RightButton + && (openPopupCount == oldOpenPopupCount)) { + QWidget *popupEvent = popup; + if (qt_button_down) + popupEvent = qt_button_down; + else if(popupChild) + popupEvent = popupChild; + QContextMenuEvent e(QContextMenuEvent::Mouse, mapped, event->globalPos(), event->modifiers()); + QApplication::sendSpontaneousEvent(popupEvent, &e); + } + + if (releaseAfter) { + qt_button_down = 0; + qt_popup_down = 0; + } + return; + } + // which child should have it? + QWidget *widget = m_widget->childAt(event->pos()); QPoint mapped = event->pos(); - QWidget *widget = 0; - if (m_implicit_mouse_grabber) { - widget = m_implicit_mouse_grabber.data(); - mapped = widget->mapFromGlobal(m_widget->mapToGlobal(event->pos())); - } else if ((widget = m_widget->childAt(event->pos()))) { + if (widget) { mapped = widget->mapFrom(m_widget, event->pos()); - } - - if (qApp->d_func()->inPopupMode()) { - widget = qApp->activePopupWidget(); - mapped = widget->mapFromGlobal(m_widget->mapToGlobal(event->pos())); - m_implicit_mouse_grabber.clear(); - } - - if (!widget) + } else { widget = m_widget; + } - if (event->type() == QEvent::MouseButtonPress && !m_implicit_mouse_grabber) - m_implicit_mouse_grabber = widget; + if (event->type() == QEvent::MouseButtonPress && !qt_button_down) + qt_button_down = widget; - if (event->buttons() == Qt::NoButton) - m_implicit_mouse_grabber.clear(); + QWidget *receiver = QApplicationPrivate::pickMouseReceiver(m_widget, event->globalPos(), mapped, event->type(), event->buttons(), + qt_button_down, widget); - if (widget != qt_last_mouse_receiver) { - QApplicationPrivate::dispatchEnterLeave(widget, qt_last_mouse_receiver); - qt_last_mouse_receiver = widget; + if (!receiver) { + if (event->type() == QEvent::MouseButtonRelease) + QApplicationPrivate::mouse_buttons &= ~event->button(); + return; } QMouseEvent translated(event->type(), mapped, event->globalPos(), event->button(), event->buttons(), event->modifiers()); - QGuiApplication::sendSpontaneousEvent(widget, &translated); + QApplicationPrivate::sendMouseEvent(receiver, &translated, widget, m_widget, &qt_button_down, + qt_last_mouse_receiver); if (event->type() == QEvent::MouseButtonPress && event->button() == Qt::RightButton) { QContextMenuEvent e(QContextMenuEvent::Mouse, mapped, event->globalPos(), event->modifiers()); - QGuiApplication::sendSpontaneousEvent(widget, &e); + QGuiApplication::sendSpontaneousEvent(receiver, &e); } } -- cgit v1.2.3