Cocoa: Correct enter/leave event handling.
Make top-level windows generate enter/leave events for sub-windows. Keep track of the current "under mouse" window in mouseMoved and send EnterLeave events when it changes. mouseEntered/mouseExited handles enters and leaves from the top-level window. Add tests/manual/cocoa/nativewidgets. Task-number: QTBUG-27550 Task-number: QTBUG-29751 Change-Id: If4b9f9e0f39d9fb05fdab45a100ffdcf107965ad Reviewed-by: Gabriel de Dietrich <gabriel.dedietrich@digia.com>
This commit is contained in:
parent
a2fc37044c
commit
b2c73c73cd
@ -150,6 +150,7 @@ public:
|
|||||||
qreal devicePixelRatio() const;
|
qreal devicePixelRatio() const;
|
||||||
void exposeWindow();
|
void exposeWindow();
|
||||||
void obscureWindow();
|
void obscureWindow();
|
||||||
|
QWindow *childWindowAt(QPoint windowPoint);
|
||||||
protected:
|
protected:
|
||||||
// NSWindow handling. The QCocoaWindow/QNSView can either be displayed
|
// NSWindow handling. The QCocoaWindow/QNSView can either be displayed
|
||||||
// in an existing NSWindow or in one created by Qt.
|
// in an existing NSWindow or in one created by Qt.
|
||||||
@ -177,6 +178,7 @@ public: // for QNSView
|
|||||||
Qt::WindowState m_synchedWindowState;
|
Qt::WindowState m_synchedWindowState;
|
||||||
Qt::WindowModality m_windowModality;
|
Qt::WindowModality m_windowModality;
|
||||||
QPointer<QWindow> m_activePopupWindow;
|
QPointer<QWindow> m_activePopupWindow;
|
||||||
|
QPointer<QWindow> m_underMouseWindow;
|
||||||
|
|
||||||
bool m_inConstructor;
|
bool m_inConstructor;
|
||||||
QCocoaGLContext *m_glContext;
|
QCocoaGLContext *m_glContext;
|
||||||
|
@ -927,6 +927,21 @@ void QCocoaWindow::obscureWindow()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QWindow *QCocoaWindow::childWindowAt(QPoint windowPoint)
|
||||||
|
{
|
||||||
|
QWindow *targetWindow = window();
|
||||||
|
foreach (QObject *child, targetWindow->children()) {
|
||||||
|
if (QWindow *childWindow = qobject_cast<QWindow *>(child)) {
|
||||||
|
if (childWindow->geometry().contains(windowPoint)) {
|
||||||
|
QCocoaWindow* platformWindow = static_cast<QCocoaWindow*>(childWindow->handle());
|
||||||
|
targetWindow = platformWindow->childWindowAt(windowPoint - childWindow->position());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return targetWindow;
|
||||||
|
}
|
||||||
|
|
||||||
QMargins QCocoaWindow::frameMargins() const
|
QMargins QCocoaWindow::frameMargins() const
|
||||||
{
|
{
|
||||||
NSRect frameW = [m_nsWindow frame];
|
NSRect frameW = [m_nsWindow frame];
|
||||||
|
@ -599,6 +599,7 @@ static QTouchDevice *touchDevice = 0;
|
|||||||
if (NSIsEmptyRect([self visibleRect]))
|
if (NSIsEmptyRect([self visibleRect]))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Remove current trakcing areas:
|
||||||
QCocoaAutoReleasePool pool;
|
QCocoaAutoReleasePool pool;
|
||||||
if (NSArray *trackingArray = [self trackingAreas]) {
|
if (NSArray *trackingArray = [self trackingAreas]) {
|
||||||
NSUInteger size = [trackingArray count];
|
NSUInteger size = [trackingArray count];
|
||||||
@ -611,7 +612,7 @@ static QTouchDevice *touchDevice = 0;
|
|||||||
// Ideally, we shouldn't have NSTrackingMouseMoved events included below, it should
|
// Ideally, we shouldn't have NSTrackingMouseMoved events included below, it should
|
||||||
// only be turned on if mouseTracking, hover is on or a tool tip is set.
|
// only be turned on if mouseTracking, hover is on or a tool tip is set.
|
||||||
// Unfortunately, Qt will send "tooltip" events on mouse moves, so we need to
|
// Unfortunately, Qt will send "tooltip" events on mouse moves, so we need to
|
||||||
// turn it on in ALL case. That means EVERY QCocoaView gets to pay the cost of
|
// turn it on in ALL case. That means EVERY QWindow gets to pay the cost of
|
||||||
// mouse moves delivered to it (Apple recommends keeping it OFF because there
|
// mouse moves delivered to it (Apple recommends keeping it OFF because there
|
||||||
// is a performance hit). So it goes.
|
// is a performance hit). So it goes.
|
||||||
NSUInteger trackingOptions = NSTrackingMouseEnteredAndExited | NSTrackingActiveInActiveApp
|
NSUInteger trackingOptions = NSTrackingMouseEnteredAndExited | NSTrackingActiveInActiveApp
|
||||||
@ -628,23 +629,58 @@ static QTouchDevice *touchDevice = 0;
|
|||||||
{
|
{
|
||||||
if (m_window->flags() & Qt::WindowTransparentForInput)
|
if (m_window->flags() & Qt::WindowTransparentForInput)
|
||||||
return [super mouseMoved:theEvent];
|
return [super mouseMoved:theEvent];
|
||||||
[self handleMouseEvent:theEvent];
|
|
||||||
|
QPoint windowPoint, screenPoint;
|
||||||
|
[self convertFromEvent:theEvent toWindowPoint:&windowPoint andScreenPoint:&screenPoint];
|
||||||
|
QWindow *childWindow = m_platformWindow->childWindowAt(windowPoint);
|
||||||
|
|
||||||
|
// Top-level windows generate enter-leave events for sub-windows.
|
||||||
|
// Qt wants to know which window (if any) will be entered at the
|
||||||
|
// the time of the leave. This is dificult to accomplish by
|
||||||
|
// handling mouseEnter and mouseLeave envents, since they are sent
|
||||||
|
// individually to different views.
|
||||||
|
if (m_platformWindow->m_nsWindow && childWindow) {
|
||||||
|
if (childWindow != m_platformWindow->m_underMouseWindow) {
|
||||||
|
QWindowSystemInterface::handleEnterLeaveEvent(childWindow, m_platformWindow->m_underMouseWindow, windowPoint, screenPoint);
|
||||||
|
m_platformWindow->m_underMouseWindow = childWindow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cocoa keeps firing mouse move events for obscured parent views. Qt should not
|
||||||
|
// send those events so filter them out here.
|
||||||
|
if (childWindow != m_window)
|
||||||
|
return;
|
||||||
|
|
||||||
|
[self handleMouseEvent: theEvent];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)mouseEntered:(NSEvent *)theEvent
|
- (void)mouseEntered:(NSEvent *)theEvent
|
||||||
{
|
{
|
||||||
if (m_window->flags() & Qt::WindowTransparentForInput)
|
if (m_window->flags() & Qt::WindowTransparentForInput)
|
||||||
return [super mouseEntered:theEvent];
|
return [super mouseEntered:theEvent];
|
||||||
|
|
||||||
|
// Top-level windows generate enter events for sub-windows.
|
||||||
|
if (!m_platformWindow->m_nsWindow)
|
||||||
|
return;
|
||||||
|
|
||||||
QPoint windowPoint, screenPoint;
|
QPoint windowPoint, screenPoint;
|
||||||
[self convertFromEvent:theEvent toWindowPoint:&windowPoint andScreenPoint:&screenPoint];
|
[self convertFromEvent:theEvent toWindowPoint:&windowPoint andScreenPoint:&screenPoint];
|
||||||
QWindowSystemInterface::handleEnterEvent(m_window, windowPoint, screenPoint);
|
m_platformWindow->m_underMouseWindow = m_platformWindow->childWindowAt(windowPoint);
|
||||||
|
QWindowSystemInterface::handleEnterEvent(m_platformWindow->m_underMouseWindow, windowPoint, screenPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)mouseExited:(NSEvent *)theEvent
|
- (void)mouseExited:(NSEvent *)theEvent
|
||||||
{
|
{
|
||||||
if (m_window->flags() & Qt::WindowTransparentForInput)
|
if (m_window->flags() & Qt::WindowTransparentForInput)
|
||||||
return [super mouseExited:theEvent];
|
return [super mouseExited:theEvent];
|
||||||
QWindowSystemInterface::handleLeaveEvent(m_window);
|
Q_UNUSED(theEvent);
|
||||||
|
|
||||||
|
// Top-level windows generate leave events for sub-windows.
|
||||||
|
if (!m_platformWindow->m_nsWindow)
|
||||||
|
return;
|
||||||
|
|
||||||
|
QWindowSystemInterface::handleLeaveEvent(m_platformWindow->m_underMouseWindow);
|
||||||
|
m_platformWindow->m_underMouseWindow = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)rightMouseDown:(NSEvent *)theEvent
|
- (void)rightMouseDown:(NSEvent *)theEvent
|
||||||
|
147
tests/manual/cocoa/nativewidgets/main.cpp
Normal file
147
tests/manual/cocoa/nativewidgets/main.cpp
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||||
|
** Contact: http://www.qt-project.org/legal
|
||||||
|
**
|
||||||
|
** This file is part of the test suite of the Qt Toolkit.
|
||||||
|
**
|
||||||
|
** $QT_BEGIN_LICENSE:LGPL$
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and Digia. For licensing terms and
|
||||||
|
** conditions see http://qt.digia.com/licensing. For further information
|
||||||
|
** use the contact form at http://qt.digia.com/contact-us.
|
||||||
|
**
|
||||||
|
** GNU Lesser General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
|
** General Public License version 2.1 as published by the Free Software
|
||||||
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||||
|
** packaging of this file. Please review the following information to
|
||||||
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||||
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||||
|
**
|
||||||
|
** In addition, as a special exception, Digia gives you certain additional
|
||||||
|
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||||
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3.0 as published by the Free Software
|
||||||
|
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||||
|
** packaging of this file. Please review the following information to
|
||||||
|
** ensure the GNU General Public License version 3.0 requirements will be
|
||||||
|
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||||
|
**
|
||||||
|
**
|
||||||
|
** $QT_END_LICENSE$
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include <QtCore>
|
||||||
|
#include <QtWidgets>
|
||||||
|
|
||||||
|
class ColorWidget : public QWidget
|
||||||
|
{
|
||||||
|
QColor color;
|
||||||
|
int s;
|
||||||
|
int v;
|
||||||
|
|
||||||
|
void changeColor()
|
||||||
|
{
|
||||||
|
color.setHsv((qreal(qrand()) / RAND_MAX) * 50 + 200, s, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
ColorWidget()
|
||||||
|
{
|
||||||
|
s = 150;
|
||||||
|
v = 150;
|
||||||
|
changeColor();
|
||||||
|
setMouseTracking(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mousePressEvent(QMouseEvent *)
|
||||||
|
{
|
||||||
|
changeColor();
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void mouseMoveEvent(QMouseEvent *)
|
||||||
|
{
|
||||||
|
changeColor();
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void enterEvent(QEvent *)
|
||||||
|
{
|
||||||
|
s = 200;
|
||||||
|
v = 200;
|
||||||
|
changeColor();
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void leaveEvent(QEvent *)
|
||||||
|
{
|
||||||
|
s = 75;
|
||||||
|
v = 75;
|
||||||
|
changeColor();
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void paintEvent(QPaintEvent *){
|
||||||
|
QPainter p(this);
|
||||||
|
p.fillRect(QRect(QPoint(0, 0), size()), QBrush(color));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QApplication app(argc, argv);
|
||||||
|
|
||||||
|
ColorWidget window;
|
||||||
|
|
||||||
|
QWidget *w1 = new ColorWidget;
|
||||||
|
QWidget *w2 = new ColorWidget;
|
||||||
|
QWidget *w3 = new ColorWidget;
|
||||||
|
|
||||||
|
QHBoxLayout *layout = new QHBoxLayout;
|
||||||
|
layout->addWidget(w1);
|
||||||
|
layout->addWidget(w2);
|
||||||
|
layout->addWidget(w3);
|
||||||
|
|
||||||
|
QWidget *w3_1 = new ColorWidget;
|
||||||
|
QWidget *w3_2 = new ColorWidget;
|
||||||
|
QWidget *w3_3 = new ColorWidget;
|
||||||
|
|
||||||
|
QVBoxLayout *layout3 = new QVBoxLayout;
|
||||||
|
layout3->setMargin(0);
|
||||||
|
layout3->addWidget(w3_1);
|
||||||
|
layout3->addWidget(w3_2);
|
||||||
|
layout3->addWidget(w3_3);
|
||||||
|
w3->setLayout(layout3);
|
||||||
|
|
||||||
|
window.setLayout(layout);
|
||||||
|
|
||||||
|
bool native = 1;
|
||||||
|
|
||||||
|
if (native) {
|
||||||
|
w1->winId();
|
||||||
|
w2->winId();
|
||||||
|
w3->winId();
|
||||||
|
|
||||||
|
w3_1->winId();
|
||||||
|
w3_2->winId();
|
||||||
|
w3_3->winId();
|
||||||
|
}
|
||||||
|
|
||||||
|
window.resize(640, 480);
|
||||||
|
window.show();
|
||||||
|
|
||||||
|
return app.exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
7
tests/manual/cocoa/nativewidgets/nativewigets.pro
Normal file
7
tests/manual/cocoa/nativewidgets/nativewigets.pro
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
TEMPLATE = app
|
||||||
|
|
||||||
|
HEADERS +=
|
||||||
|
SOURCES += main.cpp
|
||||||
|
|
||||||
|
QT += core widgets
|
||||||
|
DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
|
Loading…
x
Reference in New Issue
Block a user