Windows: Fix QColorDialog's live updates while picking outside color

Microsoft's SetCapture() doesn't work on windows owned by other processes,
so instead we use a timer. This is the same approach as used by qttools/src/pixeltool.

The mouse move approach however is more elegant and doesn't hammer the CPU with
QCursor::pos() calls when idle. For this reason the workaround is Q_OS_WIN only.

Task-number: QTBUG-34538
Change-Id: I40a6f7df5bf2a3a29ade8fe4a92f5b5c4ece7efb
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@theqtcompany.com>
This commit is contained in:
Sérgio Martins 2014-12-12 12:33:04 +00:00
parent b699ac070c
commit 38a3158d2f
3 changed files with 48 additions and 5 deletions

View File

@ -57,6 +57,7 @@
#include "qdialogbuttonbox.h" #include "qdialogbuttonbox.h"
#include "qscreen.h" #include "qscreen.h"
#include "qcursor.h" #include "qcursor.h"
#include "qtimer.h"
#include <algorithm> #include <algorithm>
@ -1577,6 +1578,11 @@ void QColorDialogPrivate::_q_pickScreenColor()
#else #else
q->grabMouse(); q->grabMouse();
#endif #endif
#ifdef Q_OS_WIN
// On Windows mouse tracking doesn't work over other processes's windows
updateTimer->start(30);
#endif
q->grabKeyboard(); q->grabKeyboard();
/* With setMouseTracking(true) the desired color can be more precisedly picked up, /* With setMouseTracking(true) the desired color can be more precisedly picked up,
* and continuously pushing the mouse button is not necessary. * and continuously pushing the mouse button is not necessary.
@ -1600,6 +1606,9 @@ void QColorDialogPrivate::releaseColorPicking()
cp->setCrossVisible(true); cp->setCrossVisible(true);
q->removeEventFilter(colorPickingEventFilter); q->removeEventFilter(colorPickingEventFilter);
q->releaseMouse(); q->releaseMouse();
#ifdef Q_OS_WIN
updateTimer->stop();
#endif
q->releaseKeyboard(); q->releaseKeyboard();
q->setMouseTracking(false); q->setMouseTracking(false);
lblScreenColorInfo->setText(QLatin1String("\n")); lblScreenColorInfo->setText(QLatin1String("\n"));
@ -1782,6 +1791,10 @@ void QColorDialogPrivate::initWidgets()
cancel = buttons->addButton(QDialogButtonBox::Cancel); cancel = buttons->addButton(QDialogButtonBox::Cancel);
QObject::connect(cancel, SIGNAL(clicked()), q, SLOT(reject())); QObject::connect(cancel, SIGNAL(clicked()), q, SLOT(reject()));
#ifdef Q_OS_WIN
updateTimer = new QTimer(q);
QObject::connect(updateTimer, SIGNAL(timeout()), q, SLOT(_q_updateColorPicking()));
#endif
retranslateStrings(); retranslateStrings();
} }
@ -2196,18 +2209,37 @@ void QColorDialog::changeEvent(QEvent *e)
QDialog::changeEvent(e); QDialog::changeEvent(e);
} }
bool QColorDialogPrivate::handleColorPickingMouseMove(QMouseEvent *e) void QColorDialogPrivate::_q_updateColorPicking()
{ {
// If the cross is visible the grabbed color will be black most of the times #ifndef QT_NO_CURSOR
cp->setCrossVisible(!cp->geometry().contains(e->pos())); Q_Q(QColorDialog);
static QPoint lastGlobalPos;
QPoint newGlobalPos = QCursor::pos();
if (lastGlobalPos == newGlobalPos)
return;
lastGlobalPos = newGlobalPos;
const QPoint globalPos = e->globalPos(); if (!q->rect().contains(q->mapFromGlobal(newGlobalPos))) // Inside the dialog mouse tracking works, handleColorPickingMouseMove will be called
updateColorPicking(newGlobalPos);
#endif // ! QT_NO_CURSOR
}
void QColorDialogPrivate::updateColorPicking(const QPoint &globalPos)
{
const QColor color = grabScreenColor(globalPos); const QColor color = grabScreenColor(globalPos);
// QTBUG-39792, do not change standard, custom color selectors while moving as // QTBUG-39792, do not change standard, custom color selectors while moving as
// otherwise it is not possible to pre-select a custom cell for assignment. // otherwise it is not possible to pre-select a custom cell for assignment.
setCurrentColor(color, ShowColor); setCurrentColor(color, ShowColor);
lblScreenColorInfo->setText(QColorDialog::tr("Cursor at %1, %2, color: %3\nPress ESC to cancel") lblScreenColorInfo->setText(QColorDialog::tr("Cursor at %1, %2, color: %3\nPress ESC to cancel")
.arg(globalPos.x()).arg(globalPos.y()).arg(color.name())); .arg(globalPos.x()).arg(globalPos.y()).arg(color.name()));
}
bool QColorDialogPrivate::handleColorPickingMouseMove(QMouseEvent *e)
{
// If the cross is visible the grabbed color will be black most of the times
cp->setCrossVisible(!cp->geometry().contains(e->pos()));
updateColorPicking(e->globalPos());
return true; return true;
} }

View File

@ -116,6 +116,7 @@ private:
Q_PRIVATE_SLOT(d_func(), void _q_newCustom(int, int)) Q_PRIVATE_SLOT(d_func(), void _q_newCustom(int, int))
Q_PRIVATE_SLOT(d_func(), void _q_newStandard(int, int)) Q_PRIVATE_SLOT(d_func(), void _q_newStandard(int, int))
Q_PRIVATE_SLOT(d_func(), void _q_pickScreenColor()) Q_PRIVATE_SLOT(d_func(), void _q_pickScreenColor())
Q_PRIVATE_SLOT(d_func(), void _q_updateColorPicking())
friend class QColorShower; friend class QColorShower;
}; };

View File

@ -63,6 +63,7 @@ class QVBoxLayout;
class QPushButton; class QPushButton;
class QWellArray; class QWellArray;
class QColorPickingEventFilter; class QColorPickingEventFilter;
class QTimer;
class QColorDialogPrivate : public QDialogPrivate class QColorDialogPrivate : public QDialogPrivate
{ {
@ -75,7 +76,11 @@ public:
SetColorAll = ShowColor | SelectColor SetColorAll = ShowColor | SelectColor
}; };
QColorDialogPrivate() : options(new QColorDialogOptions) {} QColorDialogPrivate() : options(new QColorDialogOptions)
#ifdef Q_OS_WIN
, updateTimer(0)
#endif
{}
QPlatformColorDialogHelper *platformColorDialogHelper() const QPlatformColorDialogHelper *platformColorDialogHelper() const
{ return static_cast<QPlatformColorDialogHelper *>(platformHelper()); } { return static_cast<QPlatformColorDialogHelper *>(platformHelper()); }
@ -104,6 +109,8 @@ public:
void _q_newCustom(int, int); void _q_newCustom(int, int);
void _q_newStandard(int, int); void _q_newStandard(int, int);
void _q_pickScreenColor(); void _q_pickScreenColor();
void _q_updateColorPicking();
void updateColorPicking(const QPoint &pos);
void releaseColorPicking(); void releaseColorPicking();
bool handleColorPickingMouseMove(QMouseEvent *e); bool handleColorPickingMouseMove(QMouseEvent *e);
bool handleColorPickingMouseButtonRelease(QMouseEvent *e); bool handleColorPickingMouseButtonRelease(QMouseEvent *e);
@ -136,6 +143,9 @@ public:
QPointer<QObject> receiverToDisconnectOnClose; QPointer<QObject> receiverToDisconnectOnClose;
QByteArray memberToDisconnectOnClose; QByteArray memberToDisconnectOnClose;
#ifdef Q_OS_WIN
QTimer *updateTimer;
#endif
#ifdef Q_WS_MAC #ifdef Q_WS_MAC
void openCocoaColorPanel(const QColor &initial, void openCocoaColorPanel(const QColor &initial,