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);
}
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)
{
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) {
QWindow *w = l.at(i);
if (w->handle() && w->type() != Qt::Desktop)
applyCursor(w, w->cursor());
if (w->handle() && w->type() != Qt::Desktop) {
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.
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
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
if (visible)
if (visible && d->hasCursor)
d->applyCursor();
#endif
d->platformWindow->setVisible(visible);
@ -1947,13 +1947,7 @@ void QWindowPrivate::maybeQuitOnLastWindowClosed()
void QWindow::setCursor(const QCursor &cursor)
{
Q_D(QWindow);
d->cursor = 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);
}
d->setCursor(&cursor);
}
/*!
@ -1961,7 +1955,8 @@ void QWindow::setCursor(const QCursor &cursor)
*/
void QWindow::unsetCursor()
{
setCursor(Qt::ArrowCursor);
Q_D(QWindow);
d->setCursor(0);
}
/*!
@ -1975,14 +1970,39 @@ QCursor QWindow::cursor() const
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()
{
Q_Q(QWindow);
if (platformWindow) {
if (QPlatformCursor *platformCursor = screen->handle()->cursor()) {
QCursor *oc = QGuiApplication::overrideCursor();
QCursor c = oc ? *oc : cursor;
platformCursor->changeCursor(&c, q);
QCursor *c = QGuiApplication::overrideCursor();
if (!c && hasCursor)
c = &cursor;
platformCursor->changeCursor(c, q);
}
}
}

View File

@ -98,6 +98,7 @@ public:
, screen(0)
#ifndef QT_NO_CURSOR
, cursor(Qt::ArrowCursor)
, hasCursor(false)
#endif
{
isWindow = true;
@ -109,6 +110,7 @@ public:
void maybeQuitOnLastWindowClosed();
#ifndef QT_NO_CURSOR
void setCursor(const QCursor *c = 0);
void applyCursor();
#endif
@ -151,6 +153,7 @@ public:
#ifndef QT_NO_CURSOR
QCursor cursor;
bool hasCursor;
#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)
{
Q_UNUSED(window);
Qt::CursorShape shape = widgetCursor->shape();
const Qt::CursorShape shape = widgetCursor ? widgetCursor->shape() : Qt::ArrowCursor;
if (shape == Qt::BitmapCursor) {
// application supplied cursor

View File

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

View File

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

View File

@ -196,15 +196,16 @@ void QEglFSCursor::changeCursor(QCursor *cursor, QWindow *window)
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;
if (m_cursor.shape == Qt::BitmapCursor) {
m_cursor.customCursorImage = QImage(); // in case render() never uploaded it
}
m_cursor.shape = cursor->shape();
if (cursor->shape() != Qt::BitmapCursor) { // standard cursor
m_cursor.shape = newShape;
if (newShape != Qt::BitmapCursor) { // standard cursor
const float ws = (float)m_cursorAtlas.cursorWidth / m_cursorAtlas.width,
hs = (float)m_cursorAtlas.cursorHeight / m_cursorAtlas.height;
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)
drmModeMoveCursor(m_screen->device()->fd(), m_screen->crtcId(), 0, 0);
if (widgetCursor->shape() != Qt::BitmapCursor) {
m_cursorImage->set(widgetCursor->shape());
const Qt::CursorShape newShape = widgetCursor ? widgetCursor->shape() : Qt::ArrowCursor;
if (newShape != Qt::BitmapCursor) {
m_cursorImage->set(newShape);
} else {
m_cursorImage->set(widgetCursor->pixmap().toImage(),
widgetCursor->hotSpot().x(),

View File

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

View File

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

View File

@ -739,7 +739,6 @@ QWindowsWindow::QWindowsWindow(QWindow *aWindow, const WindowData &data) :
m_hdc(0),
m_windowState(Qt::WindowNoState),
m_opacity(1.0),
m_cursor(QWindowsScreen::screenOf(aWindow)->windowsCursor()->standardWindowCursor()),
m_dropTarget(0),
m_savedStyle(0),
m_format(aWindow->format()),
@ -1668,18 +1667,40 @@ void QWindowsWindow::getSizeHints(MINMAXINFO *mmi) const
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)
{
if (c.handle() != m_cursor.handle()) {
const bool underMouse = QWindowsContext::instance()->windowUnderMouse() == window();
const bool apply = applyNewCursor(window());
if (QWindowsContext::verboseWindows)
qDebug() << window() << __FUNCTION__ << "Shape=" << c.cursor().shape()
<< " isWUM=" << underMouse;
<< " doApply=" << apply;
m_cursor = c;
if (underMouse)
if (apply)
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
return;
xcb_cursor_t c;
if (cursor->shape() == Qt::BitmapCursor) {
qint64 id = cursor->pixmap().cacheKey();
if (!m_bitmapCursorMap.contains(id))
m_bitmapCursorMap.insert(id, createBitmapCursor(cursor));
c = m_bitmapCursorMap.value(id);
} else {
int id = cursor->shape();
if (!m_shapeCursorMap.contains(id))
m_shapeCursorMap.insert(id, createFontCursor(cursor->shape()));
c = m_shapeCursorMap.value(id);
xcb_cursor_t c = XCB_CURSOR_NONE;
if (cursor) {
if (cursor->shape() == Qt::BitmapCursor) {
qint64 id = cursor->pixmap().cacheKey();
if (!m_bitmapCursorMap.contains(id))
m_bitmapCursorMap.insert(id, createBitmapCursor(cursor));
c = m_bitmapCursorMap.value(id);
} else {
int id = cursor->shape();
if (!m_shapeCursorMap.contains(id))
m_shapeCursorMap.insert(id, createFontCursor(cursor->shape()));
c = m_shapeCursorMap.value(id);
}
}
w->setCursor(c);

View File

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