Regression: Fix setting of custom cursors for native widgets.

Currently, there is no concept of not having a cursor set on
a Window. Qt::ArrowCursor is always set instead. This causes
bugs when native child widgets are involved, for example setting
a cursor on the native widget's parent no longer works since
the child's Qt::ArrowCursor applies.

Introduce QWindowPrivate::hasCursor tracking whether a cursor
has been explicitly set and clear in QWindow::unsetCursor().

Handle 0 in QPlatformCursor::changeCursor() to mean
"unsetCursor()":
- Windows: Introduce default constructor for QWindowsWindowCursor
  meaning "0". Search for applicable parent cursor in applyCursor.
- XCB: No big changes required, set XCB_CURSOR_NONE for no cursor.
- Other platforms: Assume Qt::ArrowCursor when cursor = 0 is
  passed for now.

Task-number: QTBUG-28879
Change-Id: Id82722592f3cd5fe577a5b64dcc600c85cfea484
Reviewed-by: Jonathan Liu <net147@gmail.com>
Reviewed-by: Morten Johan Sørvig <morten.sorvig@digia.com>
Reviewed-by: Samuel Rødal <samuel.rodal@digia.com>
This commit is contained in:
Friedemann Kleint 2013-01-14 10:00:12 +01:00 committed by The Qt Project
parent 1029049e1d
commit c24a7377dd
14 changed files with 135 additions and 45 deletions

View File

@ -2629,6 +2629,13 @@ static inline void applyCursor(QWindow *w, QCursor c)
cursor->changeCursor(&c, w); cursor->changeCursor(&c, w);
} }
static inline void unsetCursor(QWindow *w)
{
if (const QScreen *screen = w->screen())
if (QPlatformCursor *cursor = screen->handle()->cursor())
cursor->changeCursor(0, w);
}
static inline void applyCursor(const QList<QWindow *> &l, const QCursor &c) static inline void applyCursor(const QList<QWindow *> &l, const QCursor &c)
{ {
for (int i = 0; i < l.size(); ++i) { for (int i = 0; i < l.size(); ++i) {
@ -2642,8 +2649,13 @@ static inline void applyWindowCursor(const QList<QWindow *> &l)
{ {
for (int i = 0; i < l.size(); ++i) { for (int i = 0; i < l.size(); ++i) {
QWindow *w = l.at(i); QWindow *w = l.at(i);
if (w->handle() && w->type() != Qt::Desktop) if (w->handle() && w->type() != Qt::Desktop) {
applyCursor(w, w->cursor()); if (qt_window_private(w)->hasCursor) {
applyCursor(w, w->cursor());
} else {
unsetCursor(w);
}
}
} }
} }

View File

@ -95,6 +95,10 @@ QList<QPlatformCursor *> QPlatformCursorPrivate::getInstances()
\a windowCursor is a pointer to the QCursor that should be displayed. \a windowCursor is a pointer to the QCursor that should be displayed.
To unset the cursor of \a window, 0 is passed. This means \a window does not have
a cursor set and the cursor of a the first parent window which has a cursor explicitly
set or the system default cursor should take effect.
\a window is a pointer to the window currently displayed at QCursor::pos(). Note \a window is a pointer to the window currently displayed at QCursor::pos(). Note
that this may be 0 if the current position is not occupied by a displayed widget. that this may be 0 if the current position is not occupied by a displayed widget.

View File

@ -284,7 +284,7 @@ void QWindow::setVisible(bool visible)
} }
#ifndef QT_NO_CURSOR #ifndef QT_NO_CURSOR
if (visible) if (visible && d->hasCursor)
d->applyCursor(); d->applyCursor();
#endif #endif
d->platformWindow->setVisible(visible); d->platformWindow->setVisible(visible);
@ -1947,13 +1947,7 @@ void QWindowPrivate::maybeQuitOnLastWindowClosed()
void QWindow::setCursor(const QCursor &cursor) void QWindow::setCursor(const QCursor &cursor)
{ {
Q_D(QWindow); Q_D(QWindow);
d->cursor = cursor; d->setCursor(&cursor);
// Only attempt to set cursor and emit signal if there is an actual platform cursor
if (d->screen->handle()->cursor()) {
d->applyCursor();
QEvent event(QEvent::CursorChange);
QGuiApplication::sendEvent(this, &event);
}
} }
/*! /*!
@ -1961,7 +1955,8 @@ void QWindow::setCursor(const QCursor &cursor)
*/ */
void QWindow::unsetCursor() void QWindow::unsetCursor()
{ {
setCursor(Qt::ArrowCursor); Q_D(QWindow);
d->setCursor(0);
} }
/*! /*!
@ -1975,14 +1970,39 @@ QCursor QWindow::cursor() const
return d->cursor; return d->cursor;
} }
void QWindowPrivate::setCursor(const QCursor *newCursor)
{
Q_Q(QWindow);
if (newCursor) {
const Qt::CursorShape newShape = newCursor->shape();
if (newShape <= Qt::LastCursor && hasCursor && newShape == cursor.shape())
return; // Unchanged and no bitmap/custom cursor.
cursor = *newCursor;
hasCursor = true;
} else {
if (!hasCursor)
return;
cursor = QCursor(Qt::ArrowCursor);
hasCursor = false;
}
// Only attempt to set cursor and emit signal if there is an actual platform cursor
if (screen->handle()->cursor()) {
applyCursor();
QEvent event(QEvent::CursorChange);
QGuiApplication::sendEvent(q, &event);
}
}
void QWindowPrivate::applyCursor() void QWindowPrivate::applyCursor()
{ {
Q_Q(QWindow); Q_Q(QWindow);
if (platformWindow) { if (platformWindow) {
if (QPlatformCursor *platformCursor = screen->handle()->cursor()) { if (QPlatformCursor *platformCursor = screen->handle()->cursor()) {
QCursor *oc = QGuiApplication::overrideCursor(); QCursor *c = QGuiApplication::overrideCursor();
QCursor c = oc ? *oc : cursor; if (!c && hasCursor)
platformCursor->changeCursor(&c, q); c = &cursor;
platformCursor->changeCursor(c, q);
} }
} }
} }

View File

@ -98,6 +98,7 @@ public:
, screen(0) , screen(0)
#ifndef QT_NO_CURSOR #ifndef QT_NO_CURSOR
, cursor(Qt::ArrowCursor) , cursor(Qt::ArrowCursor)
, hasCursor(false)
#endif #endif
{ {
isWindow = true; isWindow = true;
@ -109,6 +110,7 @@ public:
void maybeQuitOnLastWindowClosed(); void maybeQuitOnLastWindowClosed();
#ifndef QT_NO_CURSOR #ifndef QT_NO_CURSOR
void setCursor(const QCursor *c = 0);
void applyCursor(); void applyCursor();
#endif #endif
@ -151,6 +153,7 @@ public:
#ifndef QT_NO_CURSOR #ifndef QT_NO_CURSOR
QCursor cursor; QCursor cursor;
bool hasCursor;
#endif #endif
}; };

View File

@ -120,7 +120,7 @@ void QFbCursor::setCursor(const uchar *data, const uchar *mask, int width, int h
void QFbCursor::changeCursor(QCursor * widgetCursor, QWindow *window) void QFbCursor::changeCursor(QCursor * widgetCursor, QWindow *window)
{ {
Q_UNUSED(window); Q_UNUSED(window);
Qt::CursorShape shape = widgetCursor->shape(); const Qt::CursorShape shape = widgetCursor ? widgetCursor->shape() : Qt::ArrowCursor;
if (shape == Qt::BitmapCursor) { if (shape == Qt::BitmapCursor) {
// application supplied cursor // application supplied cursor

View File

@ -61,8 +61,9 @@ void QCocoaCursor::changeCursor(QCursor *cursor, QWindow *window)
{ {
Q_UNUSED(window); Q_UNUSED(window);
const Qt::CursorShape newShape = cursor ? cursor->shape() : Qt::ArrowCursor;
// Check for a suitable built-in NSCursor first: // Check for a suitable built-in NSCursor first:
switch (cursor->shape()) { switch (newShape) {
case Qt::ArrowCursor: case Qt::ArrowCursor:
[[NSCursor arrowCursor] set]; [[NSCursor arrowCursor] set];
break; break;
@ -99,14 +100,14 @@ void QCocoaCursor::changeCursor(QCursor *cursor, QWindow *window)
default : { default : {
// No suitable OS cursor exist, use cursors provided // No suitable OS cursor exist, use cursors provided
// by Qt for the rest. Check for a cached cursor: // by Qt for the rest. Check for a cached cursor:
NSCursor *cocoaCursor = m_cursors.value(cursor->shape()); NSCursor *cocoaCursor = m_cursors.value(newShape);
if (cocoaCursor == 0) { if (cocoaCursor == 0) {
cocoaCursor = createCursorData(cursor); cocoaCursor = createCursorData(cursor);
if (cocoaCursor == 0) { if (cocoaCursor == 0) {
[[NSCursor arrowCursor] set]; [[NSCursor arrowCursor] set];
return; return;
} }
m_cursors.insert(cursor->shape(), cocoaCursor); m_cursors.insert(newShape, cocoaCursor);
} }
[cocoaCursor set]; [cocoaCursor set];

View File

@ -59,8 +59,9 @@ void QDirectFBCursor::changeCursor(QCursor *cursor, QWindow *)
int ySpot; int ySpot;
QPixmap map; QPixmap map;
if (cursor->shape() != Qt::BitmapCursor) { const Qt::CursorShape newShape = cursor ? cursor->shape() : Qt::ArrowCursor;
m_image->set(cursor->shape()); if (newShape != Qt::BitmapCursor) {
m_image->set(newShape);
xSpot = m_image->hotspot().x(); xSpot = m_image->hotspot().x();
ySpot = m_image->hotspot().y(); ySpot = m_image->hotspot().y();
QImage *i = m_image->image(); QImage *i = m_image->image();

View File

@ -196,15 +196,16 @@ void QEglFSCursor::changeCursor(QCursor *cursor, QWindow *window)
bool QEglFSCursor::setCurrentCursor(QCursor *cursor) bool QEglFSCursor::setCurrentCursor(QCursor *cursor)
{ {
if (m_cursor.shape == cursor->shape() && cursor->shape() != Qt::BitmapCursor) const Qt::CursorShape newShape = cursor ? cursor->shape() : Qt::ArrowCursor;
if (m_cursor.shape == newShape && newShape != Qt::BitmapCursor)
return false; return false;
if (m_cursor.shape == Qt::BitmapCursor) { if (m_cursor.shape == Qt::BitmapCursor) {
m_cursor.customCursorImage = QImage(); // in case render() never uploaded it m_cursor.customCursorImage = QImage(); // in case render() never uploaded it
} }
m_cursor.shape = cursor->shape(); m_cursor.shape = newShape;
if (cursor->shape() != Qt::BitmapCursor) { // standard cursor if (newShape != Qt::BitmapCursor) { // standard cursor
const float ws = (float)m_cursorAtlas.cursorWidth / m_cursorAtlas.width, const float ws = (float)m_cursorAtlas.cursorWidth / m_cursorAtlas.width,
hs = (float)m_cursorAtlas.cursorHeight / m_cursorAtlas.height; hs = (float)m_cursorAtlas.cursorHeight / m_cursorAtlas.height;
m_cursor.textureRect = QRectF(ws * (m_cursor.shape % m_cursorAtlas.cursorsPerRow), m_cursor.textureRect = QRectF(ws * (m_cursor.shape % m_cursorAtlas.cursorsPerRow),

View File

@ -85,8 +85,9 @@ void QKmsCursor::changeCursor(QCursor *widgetCursor, QWindow *window)
if (!m_moved) if (!m_moved)
drmModeMoveCursor(m_screen->device()->fd(), m_screen->crtcId(), 0, 0); drmModeMoveCursor(m_screen->device()->fd(), m_screen->crtcId(), 0, 0);
if (widgetCursor->shape() != Qt::BitmapCursor) { const Qt::CursorShape newShape = widgetCursor ? widgetCursor->shape() : Qt::ArrowCursor;
m_cursorImage->set(widgetCursor->shape()); if (newShape != Qt::BitmapCursor) {
m_cursorImage->set(newShape);
} else { } else {
m_cursorImage->set(widgetCursor->pixmap().toImage(), m_cursorImage->set(widgetCursor->pixmap().toImage(),
widgetCursor->hotSpot().x(), widgetCursor->hotSpot().x(),

View File

@ -405,8 +405,12 @@ void QWindowsCursor::changeCursor(QCursor *cursorIn, QWindow *window)
if (QWindowsContext::verboseWindows > 1) if (QWindowsContext::verboseWindows > 1)
qDebug() << __FUNCTION__ << cursorIn << window; qDebug() << __FUNCTION__ << cursorIn << window;
if (!cursorIn || !window) if (!window)
return; return;
if (!cursorIn) {
QWindowsWindow::baseWindowOf(window)->setCursor(QWindowsWindowCursor());
return;
}
const QWindowsWindowCursor wcursor = const QWindowsWindowCursor wcursor =
cursorIn->shape() == Qt::BitmapCursor ? cursorIn->shape() == Qt::BitmapCursor ?
QWindowsWindowCursor(*cursorIn) : standardWindowCursor(cursorIn->shape()); QWindowsWindowCursor(*cursorIn) : standardWindowCursor(cursorIn->shape());
@ -448,6 +452,7 @@ void QWindowsCursor::setPos(const QPoint &pos)
class QWindowsWindowCursorData : public QSharedData class QWindowsWindowCursorData : public QSharedData
{ {
public: public:
QWindowsWindowCursorData() : m_cursor(Qt::ArrowCursor), m_handle(0) {}
explicit QWindowsWindowCursorData(const QCursor &c); explicit QWindowsWindowCursorData(const QCursor &c);
~QWindowsWindowCursorData(); ~QWindowsWindowCursorData();
@ -463,7 +468,13 @@ QWindowsWindowCursorData::QWindowsWindowCursorData(const QCursor &c) :
QWindowsWindowCursorData::~QWindowsWindowCursorData() QWindowsWindowCursorData::~QWindowsWindowCursorData()
{ {
DestroyCursor(m_handle); if (m_handle)
DestroyCursor(m_handle);
}
QWindowsWindowCursor::QWindowsWindowCursor() :
m_data(new QWindowsWindowCursorData)
{
} }
QWindowsWindowCursor::QWindowsWindowCursor(const QCursor &c) : QWindowsWindowCursor::QWindowsWindowCursor(const QCursor &c) :
@ -487,6 +498,11 @@ QWindowsWindowCursor & QWindowsWindowCursor::operator =(const QWindowsWindowCurs
return *this; return *this;
} }
bool QWindowsWindowCursor::isNull() const
{
return m_data->m_handle == 0;
}
QCursor QWindowsWindowCursor::cursor() const QCursor QWindowsWindowCursor::cursor() const
{ {
return m_data->m_cursor; return m_data->m_cursor;

View File

@ -55,11 +55,13 @@ class QWindowsWindowCursorData;
class QWindowsWindowCursor class QWindowsWindowCursor
{ {
public: public:
QWindowsWindowCursor();
explicit QWindowsWindowCursor(const QCursor &c); explicit QWindowsWindowCursor(const QCursor &c);
~QWindowsWindowCursor(); ~QWindowsWindowCursor();
QWindowsWindowCursor(const QWindowsWindowCursor &c); QWindowsWindowCursor(const QWindowsWindowCursor &c);
QWindowsWindowCursor &operator=(const QWindowsWindowCursor &c); QWindowsWindowCursor &operator=(const QWindowsWindowCursor &c);
bool isNull() const;
QCursor cursor() const; QCursor cursor() const;
HCURSOR handle() const; HCURSOR handle() const;

View File

@ -739,7 +739,6 @@ QWindowsWindow::QWindowsWindow(QWindow *aWindow, const WindowData &data) :
m_hdc(0), m_hdc(0),
m_windowState(Qt::WindowNoState), m_windowState(Qt::WindowNoState),
m_opacity(1.0), m_opacity(1.0),
m_cursor(QWindowsScreen::screenOf(aWindow)->windowsCursor()->standardWindowCursor()),
m_dropTarget(0), m_dropTarget(0),
m_savedStyle(0), m_savedStyle(0),
m_format(aWindow->format()), m_format(aWindow->format()),
@ -1668,18 +1667,40 @@ void QWindowsWindow::getSizeHints(MINMAXINFO *mmi) const
void QWindowsWindow::applyCursor() void QWindowsWindow::applyCursor()
{ {
SetCursor(m_cursor.handle()); if (m_cursor.isNull()) { // Recurse up to parent with non-null cursor.
if (const QWindow *p = window()->parent())
QWindowsWindow::baseWindowOf(p)->applyCursor();
} else {
SetCursor(m_cursor.handle());
}
}
// Check whether to apply a new cursor. Either the window in question is
// currently under mouse, or it is the parent of the window under mouse and
// there is no other window with an explicitly set cursor in-between.
static inline bool applyNewCursor(const QWindow *w)
{
const QWindow *underMouse = QWindowsContext::instance()->windowUnderMouse();
if (underMouse == w)
return true;
for (const QWindow *p = underMouse; p ; p = p->parent()) {
if (p == w)
return true;
if (!QWindowsWindow::baseWindowOf(p)->cursor().isNull())
return false;
}
return false;
} }
void QWindowsWindow::setCursor(const QWindowsWindowCursor &c) void QWindowsWindow::setCursor(const QWindowsWindowCursor &c)
{ {
if (c.handle() != m_cursor.handle()) { if (c.handle() != m_cursor.handle()) {
const bool underMouse = QWindowsContext::instance()->windowUnderMouse() == window(); const bool apply = applyNewCursor(window());
if (QWindowsContext::verboseWindows) if (QWindowsContext::verboseWindows)
qDebug() << window() << __FUNCTION__ << "Shape=" << c.cursor().shape() qDebug() << window() << __FUNCTION__ << "Shape=" << c.cursor().shape()
<< " isWUM=" << underMouse; << " doApply=" << apply;
m_cursor = c; m_cursor = c;
if (underMouse) if (apply)
applyCursor(); applyCursor();
} }
} }

View File

@ -301,17 +301,19 @@ void QXcbCursor::changeCursor(QCursor *cursor, QWindow *widget)
// No X11 cursor control when there is no widget under the cursor // No X11 cursor control when there is no widget under the cursor
return; return;
xcb_cursor_t c; xcb_cursor_t c = XCB_CURSOR_NONE;
if (cursor->shape() == Qt::BitmapCursor) { if (cursor) {
qint64 id = cursor->pixmap().cacheKey(); if (cursor->shape() == Qt::BitmapCursor) {
if (!m_bitmapCursorMap.contains(id)) qint64 id = cursor->pixmap().cacheKey();
m_bitmapCursorMap.insert(id, createBitmapCursor(cursor)); if (!m_bitmapCursorMap.contains(id))
c = m_bitmapCursorMap.value(id); m_bitmapCursorMap.insert(id, createBitmapCursor(cursor));
} else { c = m_bitmapCursorMap.value(id);
int id = cursor->shape(); } else {
if (!m_shapeCursorMap.contains(id)) int id = cursor->shape();
m_shapeCursorMap.insert(id, createFontCursor(cursor->shape())); if (!m_shapeCursorMap.contains(id))
c = m_shapeCursorMap.value(id); m_shapeCursorMap.insert(id, createFontCursor(cursor->shape()));
c = m_shapeCursorMap.value(id);
}
} }
w->setCursor(c); w->setCursor(c);

View File

@ -999,6 +999,12 @@ static inline void applyCursor(QWidget *w, QCursor c)
window->setCursor(c); window->setCursor(c);
} }
static inline void unsetCursor(QWidget *w)
{
if (QWindow *window = w->windowHandle())
window->unsetCursor();
}
void qt_qpa_set_cursor(QWidget *w, bool force) void qt_qpa_set_cursor(QWidget *w, bool force)
{ {
if (!w->testAttribute(Qt::WA_WState_Created)) if (!w->testAttribute(Qt::WA_WState_Created))
@ -1032,9 +1038,9 @@ void qt_qpa_set_cursor(QWidget *w, bool force)
else else
// Enforce the windows behavior of clearing the cursor on // Enforce the windows behavior of clearing the cursor on
// disabled widgets. // disabled widgets.
applyCursor(nativeParent, Qt::ArrowCursor); unsetCursor(nativeParent);
} else { } else {
applyCursor(nativeParent, Qt::ArrowCursor); unsetCursor(nativeParent);
} }
} }
#endif //QT_NO_CURSOR #endif //QT_NO_CURSOR