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:
parent
1029049e1d
commit
c24a7377dd
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
@ -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];
|
||||
|
@ -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();
|
||||
|
@ -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),
|
||||
|
@ -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(),
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user