qtbase/src/widgets/dialogs/qcolordialog.cpp
Axel Spoerl 3094ac8e55 QColorDialog: React to HSV values during manual edit instead of change
The existing signal connection can cause a loop and undefined behavior
in the interaction between the line edit for a hex rgb color and the
individual rgb values.

=> Change connection to textEdited, to react only on manual editing.

Fixes: QTBUG-115189
Change-Id: Ie3062c575652b905a8ab16edb2f5f77f8d150f36
Reviewed-by: Santhosh Kumar <santhosh.kumar.selvaraj@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
(cherry picked from commit 4a26ae1b270bfeaac5cf60850407fadfbe5db3ab)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
2023-07-25 07:14:59 +00:00

2306 lines
62 KiB
C++

// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qcolordialog.h"
#include "qapplication.h"
#include "qdrawutil.h"
#include "qevent.h"
#include "qimage.h"
#if QT_CONFIG(draganddrop)
#include <qdrag.h>
#endif
#include "qlabel.h"
#include "qlayout.h"
#include "qlineedit.h"
#if QT_CONFIG(menu)
#include "qmenu.h"
#endif
#include "qpainter.h"
#include "qpixmap.h"
#include "qpushbutton.h"
#if QT_CONFIG(regularexpression)
#include <qregularexpression.h>
#endif
#if QT_CONFIG(settings)
#include "qsettings.h"
#endif
#include "qsharedpointer.h"
#include "qstyle.h"
#include "qstyleoption.h"
#include "qvalidator.h"
#include "qmimedata.h"
#include "qspinbox.h"
#include "qdialogbuttonbox.h"
#include "qscreen.h"
#include "qcursor.h"
#include "qtimer.h"
#include "qwindow.h"
#include "private/qdialog_p.h"
#include <qpa/qplatformintegration.h>
#include <qpa/qplatformservices.h>
#include <private/qguiapplication_p.h>
#include <algorithm>
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
namespace QtPrivate {
class QColorLuminancePicker;
class QColorPicker;
class QColorShower;
class QWellArray;
class QColorWell;
class QColorPickingEventFilter;
} // namespace QtPrivate
using QColorLuminancePicker = QtPrivate::QColorLuminancePicker;
using QColorPicker = QtPrivate::QColorPicker;
using QColorShower = QtPrivate::QColorShower;
using QWellArray = QtPrivate::QWellArray;
using QColorWell = QtPrivate::QColorWell;
using QColorPickingEventFilter = QtPrivate::QColorPickingEventFilter;
class QColorDialogPrivate : public QDialogPrivate
{
Q_DECLARE_PUBLIC(QColorDialog)
public:
enum SetColorMode {
ShowColor = 0x1,
SelectColor = 0x2,
SetColorAll = ShowColor | SelectColor
};
QColorDialogPrivate() : options(QColorDialogOptions::create())
#ifdef Q_OS_WIN32
, updateTimer(0)
#endif
{}
QPlatformColorDialogHelper *platformColorDialogHelper() const
{ return static_cast<QPlatformColorDialogHelper *>(platformHelper()); }
void init(const QColor &initial);
void initWidgets();
QRgb currentColor() const;
QColor currentQColor() const;
void setCurrentColor(const QColor &color, SetColorMode setColorMode = SetColorAll);
void setCurrentRgbColor(QRgb rgb);
void setCurrentQColor(const QColor &color);
bool selectColor(const QColor &color);
QColor grabScreenColor(const QPoint &p);
int currentAlpha() const;
void setCurrentAlpha(int a);
void showAlpha(bool b);
bool isAlphaVisible() const;
void retranslateStrings();
bool supportsColorPicking() const;
void _q_addCustom();
void _q_setCustom(int index, QRgb color);
void _q_newHsv(int h, int s, int v);
void _q_newColorTypedIn(QRgb rgb);
void _q_nextCustom(int, int);
void _q_newCustom(int, int);
void _q_newStandard(int, int);
void _q_pickScreenColor();
void _q_updateColorPicking();
void updateColorLabelText(const QPoint &);
void updateColorPicking(const QPoint &pos);
void releaseColorPicking();
bool handleColorPickingMouseMove(QMouseEvent *e);
bool handleColorPickingMouseButtonRelease(QMouseEvent *e);
bool handleColorPickingKeyPress(QKeyEvent *e);
bool canBeNativeDialog() const override;
QWellArray *custom;
QWellArray *standard;
QDialogButtonBox *buttons;
QVBoxLayout *leftLay;
QColorPicker *cp;
QColorLuminancePicker *lp;
QColorShower *cs;
QLabel *lblBasicColors;
QLabel *lblCustomColors;
QLabel *lblScreenColorInfo;
QPushButton *ok;
QPushButton *cancel;
QPushButton *addCusBt;
QPushButton *screenColorPickerButton;
QColor selectedQColor;
int nextCust;
bool smallDisplay;
bool screenColorPicking;
QColorPickingEventFilter *colorPickingEventFilter;
QRgb beforeScreenColorPicking;
QSharedPointer<QColorDialogOptions> options;
QPointer<QObject> receiverToDisconnectOnClose;
QByteArray memberToDisconnectOnClose;
#ifdef Q_OS_WIN32
QTimer *updateTimer;
QWindow dummyTransparentWindow;
#endif
private:
virtual void initHelper(QPlatformDialogHelper *h) override;
virtual void helperPrepareShow(QPlatformDialogHelper *h) override;
};
//////////// QWellArray BEGIN
namespace QtPrivate {
class QWellArray : public QWidget
{
Q_OBJECT
Q_PROPERTY(int selectedColumn READ selectedColumn)
Q_PROPERTY(int selectedRow READ selectedRow)
public:
QWellArray(int rows, int cols, QWidget* parent=nullptr);
~QWellArray() {}
QString cellContent(int row, int col) const;
int selectedColumn() const { return selCol; }
int selectedRow() const { return selRow; }
virtual void setCurrent(int row, int col);
virtual void setSelected(int row, int col);
QSize sizeHint() const override;
inline int cellWidth() const
{ return cellw; }
inline int cellHeight() const
{ return cellh; }
inline int rowAt(int y) const
{ return y / cellh; }
inline int columnAt(int x) const
{ if (isRightToLeft()) return ncols - (x / cellw) - 1; return x / cellw; }
inline int rowY(int row) const
{ return cellh * row; }
inline int columnX(int column) const
{ if (isRightToLeft()) return cellw * (ncols - column - 1); return cellw * column; }
inline int numRows() const
{ return nrows; }
inline int numCols() const
{return ncols; }
inline QRect cellRect() const
{ return QRect(0, 0, cellw, cellh); }
inline QSize gridSize() const
{ return QSize(ncols * cellw, nrows * cellh); }
QRect cellGeometry(int row, int column)
{
QRect r;
if (row >= 0 && row < nrows && column >= 0 && column < ncols)
r.setRect(columnX(column), rowY(row), cellw, cellh);
return r;
}
inline void updateCell(int row, int column) { update(cellGeometry(row, column)); }
signals:
void selected(int row, int col);
void currentChanged(int row, int col);
void colorChanged(int index, QRgb color);
protected:
virtual void paintCell(QPainter *, int row, int col, const QRect&);
virtual void paintCellContents(QPainter *, int row, int col, const QRect&);
void mousePressEvent(QMouseEvent*) override;
void mouseReleaseEvent(QMouseEvent*) override;
void keyPressEvent(QKeyEvent*) override;
void focusInEvent(QFocusEvent*) override;
void focusOutEvent(QFocusEvent*) override;
void paintEvent(QPaintEvent *) override;
private:
Q_DISABLE_COPY(QWellArray)
int nrows;
int ncols;
int cellw;
int cellh;
int curRow;
int curCol;
int selRow;
int selCol;
};
void QWellArray::paintEvent(QPaintEvent *e)
{
QRect r = e->rect();
int cx = r.x();
int cy = r.y();
int ch = r.height();
int cw = r.width();
int colfirst = columnAt(cx);
int collast = columnAt(cx + cw);
int rowfirst = rowAt(cy);
int rowlast = rowAt(cy + ch);
if (isRightToLeft()) {
int t = colfirst;
colfirst = collast;
collast = t;
}
QPainter painter(this);
QPainter *p = &painter;
QRect rect(0, 0, cellWidth(), cellHeight());
if (collast < 0 || collast >= ncols)
collast = ncols-1;
if (rowlast < 0 || rowlast >= nrows)
rowlast = nrows-1;
// Go through the rows
for (int r = rowfirst; r <= rowlast; ++r) {
// get row position and height
int rowp = rowY(r);
// Go through the columns in the row r
// if we know from where to where, go through [colfirst, collast],
// else go through all of them
for (int c = colfirst; c <= collast; ++c) {
// get position and width of column c
int colp = columnX(c);
// Translate painter and draw the cell
rect.translate(colp, rowp);
paintCell(p, r, c, rect);
rect.translate(-colp, -rowp);
}
}
}
QWellArray::QWellArray(int rows, int cols, QWidget *parent)
: QWidget(parent)
,nrows(rows), ncols(cols)
{
setFocusPolicy(Qt::StrongFocus);
cellw = 28;
cellh = 24;
curCol = 0;
curRow = 0;
selCol = -1;
selRow = -1;
}
QSize QWellArray::sizeHint() const
{
ensurePolished();
return gridSize().boundedTo(QSize(640, 480));
}
void QWellArray::paintCell(QPainter* p, int row, int col, const QRect &rect)
{
int b = 3; //margin
const QPalette & g = palette();
QStyleOptionFrame opt;
opt.initFrom(this);
int dfw = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, &opt);
opt.lineWidth = dfw;
opt.midLineWidth = 1;
opt.rect = rect.adjusted(b, b, -b, -b);
opt.palette = g;
opt.state = QStyle::State_Enabled | QStyle::State_Sunken;
style()->drawPrimitive(QStyle::PE_Frame, &opt, p, this);
b += dfw;
if ((row == curRow) && (col == curCol)) {
if (hasFocus()) {
QStyleOptionFocusRect opt;
opt.palette = g;
opt.rect = rect;
opt.state = QStyle::State_None | QStyle::State_KeyboardFocusChange;
style()->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, p, this);
}
}
paintCellContents(p, row, col, opt.rect.adjusted(dfw, dfw, -dfw, -dfw));
}
/*
Reimplement this function to change the contents of the well array.
*/
void QWellArray::paintCellContents(QPainter *p, int row, int col, const QRect &r)
{
Q_UNUSED(row);
Q_UNUSED(col);
p->fillRect(r, Qt::white);
p->setPen(Qt::black);
p->drawLine(r.topLeft(), r.bottomRight());
p->drawLine(r.topRight(), r.bottomLeft());
}
void QWellArray::mousePressEvent(QMouseEvent *e)
{
// The current cell marker is set to the cell the mouse is pressed in
QPoint pos = e->position().toPoint();
setCurrent(rowAt(pos.y()), columnAt(pos.x()));
}
void QWellArray::mouseReleaseEvent(QMouseEvent * /* event */)
{
// The current cell marker is set to the cell the mouse is clicked in
setSelected(curRow, curCol);
}
/*
Sets the cell currently having the focus. This is not necessarily
the same as the currently selected cell.
*/
void QWellArray::setCurrent(int row, int col)
{
if ((curRow == row) && (curCol == col))
return;
if (row < 0 || col < 0)
row = col = -1;
int oldRow = curRow;
int oldCol = curCol;
curRow = row;
curCol = col;
updateCell(oldRow, oldCol);
updateCell(curRow, curCol);
emit currentChanged(curRow, curCol);
}
/*
Sets the currently selected cell to \a row, \a column. If \a row or
\a column are less than zero, the current cell is unselected.
Does not set the position of the focus indicator.
*/
void QWellArray::setSelected(int row, int col)
{
int oldRow = selRow;
int oldCol = selCol;
if (row < 0 || col < 0)
row = col = -1;
selCol = col;
selRow = row;
updateCell(oldRow, oldCol);
updateCell(selRow, selCol);
if (row >= 0)
emit selected(row, col);
#if QT_CONFIG(menu)
if (isVisible() && qobject_cast<QMenu*>(parentWidget()))
parentWidget()->close();
#endif
}
void QWellArray::focusInEvent(QFocusEvent*)
{
updateCell(curRow, curCol);
emit currentChanged(curRow, curCol);
}
void QWellArray::focusOutEvent(QFocusEvent*)
{
updateCell(curRow, curCol);
}
void QWellArray::keyPressEvent(QKeyEvent* e)
{
switch(e->key()) { // Look at the key code
case Qt::Key_Left: // If 'left arrow'-key,
if (curCol > 0) // and cr't not in leftmost col
setCurrent(curRow, curCol - 1); // set cr't to next left column
break;
case Qt::Key_Right: // Correspondingly...
if (curCol < numCols()-1)
setCurrent(curRow, curCol + 1);
break;
case Qt::Key_Up:
if (curRow > 0)
setCurrent(curRow - 1, curCol);
break;
case Qt::Key_Down:
if (curRow < numRows()-1)
setCurrent(curRow + 1, curCol);
break;
#if 0
// bad idea that shouldn't have been implemented; very counterintuitive
case Qt::Key_Return:
case Qt::Key_Enter:
/*
ignore the key, so that the dialog get it, but still select
the current row/col
*/
e->ignore();
// fallthrough intended
#endif
case Qt::Key_Space:
setSelected(curRow, curCol);
break;
default: // If not an interesting key,
e->ignore(); // we don't accept the event
return;
}
} // namespace QtPrivate
//////////// QWellArray END
// Event filter to be installed on the dialog while in color-picking mode.
class QColorPickingEventFilter : public QObject {
public:
explicit QColorPickingEventFilter(QColorDialogPrivate *dp, QObject *parent) : QObject(parent), m_dp(dp) {}
bool eventFilter(QObject *, QEvent *event) override
{
switch (event->type()) {
case QEvent::MouseMove:
return m_dp->handleColorPickingMouseMove(static_cast<QMouseEvent *>(event));
case QEvent::MouseButtonRelease:
return m_dp->handleColorPickingMouseButtonRelease(static_cast<QMouseEvent *>(event));
case QEvent::KeyPress:
return m_dp->handleColorPickingKeyPress(static_cast<QKeyEvent *>(event));
default:
break;
}
return false;
}
private:
QColorDialogPrivate *m_dp;
};
} // unnamed namespace
/*!
Returns the number of custom colors supported by QColorDialog. All
color dialogs share the same custom colors.
*/
int QColorDialog::customCount()
{
return QColorDialogOptions::customColorCount();
}
/*!
\since 4.5
Returns the custom color at the given \a index as a QColor value.
*/
QColor QColorDialog::customColor(int index)
{
return QColor(QColorDialogOptions::customColor(index));
}
/*!
Sets the custom color at \a index to the QColor \a color value.
\note This function does not apply to the Native Color Dialog on the
\macos platform. If you still require this function, use the
QColorDialog::DontUseNativeDialog option.
*/
void QColorDialog::setCustomColor(int index, QColor color)
{
QColorDialogOptions::setCustomColor(index, color.rgba());
}
/*!
\since 5.0
Returns the standard color at the given \a index as a QColor value.
*/
QColor QColorDialog::standardColor(int index)
{
return QColor(QColorDialogOptions::standardColor(index));
}
/*!
Sets the standard color at \a index to the QColor \a color value.
\note This function does not apply to the Native Color Dialog on the
\macos platform. If you still require this function, use the
QColorDialog::DontUseNativeDialog option.
*/
void QColorDialog::setStandardColor(int index, QColor color)
{
QColorDialogOptions::setStandardColor(index, color.rgba());
}
static inline void rgb2hsv(QRgb rgb, int &h, int &s, int &v)
{
QColor c;
c.setRgb(rgb);
c.getHsv(&h, &s, &v);
}
namespace QtPrivate {
class QColorWell : public QWellArray
{
public:
QColorWell(QWidget *parent, int r, int c, const QRgb *vals)
:QWellArray(r, c, parent), values(vals), mousePressed(false), oldCurrent(-1, -1)
{ setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum)); }
protected:
void paintCellContents(QPainter *, int row, int col, const QRect&) override;
void mousePressEvent(QMouseEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
#if QT_CONFIG(draganddrop)
void dragEnterEvent(QDragEnterEvent *e) override;
void dragLeaveEvent(QDragLeaveEvent *e) override;
void dragMoveEvent(QDragMoveEvent *e) override;
void dropEvent(QDropEvent *e) override;
#endif
private:
const QRgb *values;
bool mousePressed;
QPoint pressPos;
QPoint oldCurrent;
};
void QColorWell::paintCellContents(QPainter *p, int row, int col, const QRect &r)
{
int i = row + col*numRows();
p->fillRect(r, QColor(values[i]));
}
void QColorWell::mousePressEvent(QMouseEvent *e)
{
oldCurrent = QPoint(selectedRow(), selectedColumn());
QWellArray::mousePressEvent(e);
mousePressed = true;
pressPos = e->position().toPoint();
}
void QColorWell::mouseMoveEvent(QMouseEvent *e)
{
QWellArray::mouseMoveEvent(e);
#if QT_CONFIG(draganddrop)
if (!mousePressed)
return;
if ((pressPos - e->position().toPoint()).manhattanLength() > QApplication::startDragDistance()) {
setCurrent(oldCurrent.x(), oldCurrent.y());
int i = rowAt(pressPos.y()) + columnAt(pressPos.x()) * numRows();
QColor col(values[i]);
QMimeData *mime = new QMimeData;
mime->setColorData(col);
QPixmap pix(cellWidth(), cellHeight());
pix.fill(col);
QPainter p(&pix);
p.drawRect(0, 0, pix.width() - 1, pix.height() - 1);
p.end();
QDrag *drg = new QDrag(this);
drg->setMimeData(mime);
drg->setPixmap(pix);
mousePressed = false;
drg->exec(Qt::CopyAction);
}
#endif
}
#if QT_CONFIG(draganddrop)
void QColorWell::dragEnterEvent(QDragEnterEvent *e)
{
if (qvariant_cast<QColor>(e->mimeData()->colorData()).isValid())
e->accept();
else
e->ignore();
}
void QColorWell::dragLeaveEvent(QDragLeaveEvent *)
{
if (hasFocus())
parentWidget()->setFocus();
}
void QColorWell::dragMoveEvent(QDragMoveEvent *e)
{
if (qvariant_cast<QColor>(e->mimeData()->colorData()).isValid()) {
setCurrent(rowAt(e->position().toPoint().y()), columnAt(e->position().toPoint().x()));
e->accept();
} else {
e->ignore();
}
}
void QColorWell::dropEvent(QDropEvent *e)
{
QColor col = qvariant_cast<QColor>(e->mimeData()->colorData());
if (col.isValid()) {
int i = rowAt(e->position().toPoint().y()) + columnAt(e->position().toPoint().x()) * numRows();
emit colorChanged(i, col.rgb());
e->accept();
} else {
e->ignore();
}
}
#endif // QT_CONFIG(draganddrop)
void QColorWell::mouseReleaseEvent(QMouseEvent *e)
{
if (!mousePressed)
return;
QWellArray::mouseReleaseEvent(e);
mousePressed = false;
}
class QColorPicker : public QFrame
{
Q_OBJECT
public:
QColorPicker(QWidget* parent);
~QColorPicker();
void setCrossVisible(bool visible);
public slots:
void setCol(int h, int s);
signals:
void newCol(int h, int s);
protected:
QSize sizeHint() const override;
void paintEvent(QPaintEvent*) override;
void mouseMoveEvent(QMouseEvent *) override;
void mousePressEvent(QMouseEvent *) override;
void resizeEvent(QResizeEvent *) override;
private:
int hue;
int sat;
QPoint colPt();
int huePt(const QPoint &pt);
int satPt(const QPoint &pt);
void setCol(const QPoint &pt);
QPixmap pix;
bool crossVisible;
};
} // namespace QtPrivate
static int pWidth = 220;
static int pHeight = 200;
namespace QtPrivate {
class QColorLuminancePicker : public QWidget
{
Q_OBJECT
public:
QColorLuminancePicker(QWidget* parent=nullptr);
~QColorLuminancePicker();
public slots:
void setCol(int h, int s, int v);
void setCol(int h, int s);
signals:
void newHsv(int h, int s, int v);
protected:
void paintEvent(QPaintEvent*) override;
void mouseMoveEvent(QMouseEvent *) override;
void mousePressEvent(QMouseEvent *) override;
private:
enum { foff = 3, coff = 4 }; //frame and contents offset
int val;
int hue;
int sat;
int y2val(int y);
int val2y(int val);
void setVal(int v);
QPixmap *pix;
};
int QColorLuminancePicker::y2val(int y)
{
int d = height() - 2*coff - 1;
return 255 - (y - coff)*255/d;
}
int QColorLuminancePicker::val2y(int v)
{
int d = height() - 2*coff - 1;
return coff + (255-v)*d/255;
}
QColorLuminancePicker::QColorLuminancePicker(QWidget* parent)
:QWidget(parent)
{
hue = 100; val = 100; sat = 100;
pix = nullptr;
// setAttribute(WA_NoErase, true);
}
QColorLuminancePicker::~QColorLuminancePicker()
{
delete pix;
}
void QColorLuminancePicker::mouseMoveEvent(QMouseEvent *m)
{
setVal(y2val(m->position().toPoint().y()));
}
void QColorLuminancePicker::mousePressEvent(QMouseEvent *m)
{
setVal(y2val(m->position().toPoint().y()));
}
void QColorLuminancePicker::setVal(int v)
{
if (val == v)
return;
val = qMax(0, qMin(v,255));
delete pix; pix=nullptr;
repaint();
emit newHsv(hue, sat, val);
}
//receives from a hue,sat chooser and relays.
void QColorLuminancePicker::setCol(int h, int s)
{
setCol(h, s, val);
emit newHsv(h, s, val);
}
void QColorLuminancePicker::paintEvent(QPaintEvent *)
{
int w = width() - 5;
QRect r(0, foff, w, height() - 2*foff);
int wi = r.width() - 2;
int hi = r.height() - 2;
if (!pix || pix->height() != hi || pix->width() != wi) {
delete pix;
QImage img(wi, hi, QImage::Format_RGB32);
int y;
uint *pixel = (uint *) img.scanLine(0);
for (y = 0; y < hi; y++) {
uint *end = pixel + wi;
std::fill(pixel, end, QColor::fromHsv(hue, sat, y2val(y + coff)).rgb());
pixel = end;
}
pix = new QPixmap(QPixmap::fromImage(img));
}
QPainter p(this);
p.drawPixmap(1, coff, *pix);
const QPalette &g = palette();
qDrawShadePanel(&p, r, g, true);
p.setPen(g.windowText().color());
p.setBrush(g.windowText());
QPolygon a;
int y = val2y(val);
a.setPoints(3, w, y, w+5, y+5, w+5, y-5);
p.eraseRect(w, 0, 5, height());
p.drawPolygon(a);
}
void QColorLuminancePicker::setCol(int h, int s , int v)
{
val = v;
hue = h;
sat = s;
delete pix; pix=nullptr;
repaint();
}
QPoint QColorPicker::colPt()
{
QRect r = contentsRect();
return QPoint((360 - hue) * (r.width() - 1) / 360, (255 - sat) * (r.height() - 1) / 255);
}
int QColorPicker::huePt(const QPoint &pt)
{
QRect r = contentsRect();
return 360 - pt.x() * 360 / (r.width() - 1);
}
int QColorPicker::satPt(const QPoint &pt)
{
QRect r = contentsRect();
return 255 - pt.y() * 255 / (r.height() - 1);
}
void QColorPicker::setCol(const QPoint &pt)
{
setCol(huePt(pt), satPt(pt));
}
QColorPicker::QColorPicker(QWidget* parent)
: QFrame(parent)
, crossVisible(true)
{
hue = 0; sat = 0;
setCol(150, 255);
setAttribute(Qt::WA_NoSystemBackground);
setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed) );
}
QColorPicker::~QColorPicker()
{
}
void QColorPicker::setCrossVisible(bool visible)
{
if (crossVisible != visible) {
crossVisible = visible;
update();
}
}
QSize QColorPicker::sizeHint() const
{
return QSize(pWidth + 2*frameWidth(), pHeight + 2*frameWidth());
}
void QColorPicker::setCol(int h, int s)
{
int nhue = qMin(qMax(0,h), 359);
int nsat = qMin(qMax(0,s), 255);
if (nhue == hue && nsat == sat)
return;
QRect r(colPt(), QSize(20,20));
hue = nhue; sat = nsat;
r = r.united(QRect(colPt(), QSize(20,20)));
r.translate(contentsRect().x()-9, contentsRect().y()-9);
// update(r);
repaint(r);
}
void QColorPicker::mouseMoveEvent(QMouseEvent *m)
{
QPoint p = m->position().toPoint() - contentsRect().topLeft();
setCol(p);
emit newCol(hue, sat);
}
void QColorPicker::mousePressEvent(QMouseEvent *m)
{
QPoint p = m->position().toPoint() - contentsRect().topLeft();
setCol(p);
emit newCol(hue, sat);
}
void QColorPicker::paintEvent(QPaintEvent* )
{
QPainter p(this);
drawFrame(&p);
QRect r = contentsRect();
p.drawPixmap(r.topLeft(), pix);
if (crossVisible) {
QPoint pt = colPt() + r.topLeft();
p.setPen(Qt::black);
p.fillRect(pt.x()-9, pt.y(), 20, 2, Qt::black);
p.fillRect(pt.x(), pt.y()-9, 2, 20, Qt::black);
}
}
void QColorPicker::resizeEvent(QResizeEvent *ev)
{
QFrame::resizeEvent(ev);
int w = width() - frameWidth() * 2;
int h = height() - frameWidth() * 2;
QImage img(w, h, QImage::Format_RGB32);
int x, y;
uint *pixel = (uint *) img.scanLine(0);
for (y = 0; y < h; y++) {
const uint *end = pixel + w;
x = 0;
while (pixel < end) {
QPoint p(x, y);
QColor c;
c.setHsv(huePt(p), satPt(p), 200);
*pixel = c.rgb();
++pixel;
++x;
}
}
pix = QPixmap::fromImage(img);
}
class QColSpinBox : public QSpinBox
{
public:
QColSpinBox(QWidget *parent)
: QSpinBox(parent) { setRange(0, 255); }
void setValue(int i) {
const QSignalBlocker blocker(this);
QSpinBox::setValue(i);
}
};
class QColorShowLabel;
class QColorShower : public QWidget
{
Q_OBJECT
public:
QColorShower(QColorDialog *parent);
//things that don't emit signals
void setHsv(int h, int s, int v);
int currentAlpha() const
{ return (colorDialog->options() & QColorDialog::ShowAlphaChannel) ? alphaEd->value() : 255; }
void setCurrentAlpha(int a) { alphaEd->setValue(a); rgbEd(); }
void showAlpha(bool b);
bool isAlphaVisible() const;
QRgb currentColor() const { return curCol; }
QColor currentQColor() const { return curQColor; }
void retranslateStrings();
void updateQColor();
public slots:
void setRgb(QRgb rgb);
signals:
void newCol(QRgb rgb);
void currentColorChanged(const QColor &color);
private slots:
void rgbEd();
void hsvEd();
void htmlEd();
private:
void showCurrentColor();
int hue, sat, val;
QRgb curCol;
QColor curQColor;
QLabel *lblHue;
QLabel *lblSat;
QLabel *lblVal;
QLabel *lblRed;
QLabel *lblGreen;
QLabel *lblBlue;
QLabel *lblHtml;
QColSpinBox *hEd;
QColSpinBox *sEd;
QColSpinBox *vEd;
QColSpinBox *rEd;
QColSpinBox *gEd;
QColSpinBox *bEd;
QColSpinBox *alphaEd;
QLabel *alphaLab;
QLineEdit *htEd;
QColorShowLabel *lab;
bool rgbOriginal;
QColorDialog *colorDialog;
QGridLayout *gl;
friend class QT_PREPEND_NAMESPACE(QColorDialog);
friend class QT_PREPEND_NAMESPACE(QColorDialogPrivate);
};
class QColorShowLabel : public QFrame
{
Q_OBJECT
public:
QColorShowLabel(QWidget *parent) : QFrame(parent) {
setFrameStyle(QFrame::Panel|QFrame::Sunken);
setAcceptDrops(true);
mousePressed = false;
}
void setColor(QColor c) { col = c; }
signals:
void colorDropped(QRgb);
protected:
void paintEvent(QPaintEvent *) override;
void mousePressEvent(QMouseEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
#if QT_CONFIG(draganddrop)
void dragEnterEvent(QDragEnterEvent *e) override;
void dragLeaveEvent(QDragLeaveEvent *e) override;
void dropEvent(QDropEvent *e) override;
#endif
private:
QColor col;
bool mousePressed;
QPoint pressPos;
};
void QColorShowLabel::paintEvent(QPaintEvent *e)
{
QPainter p(this);
drawFrame(&p);
p.fillRect(contentsRect()&e->rect(), col);
}
void QColorShower::showAlpha(bool b)
{
alphaLab->setVisible(b);
alphaEd->setVisible(b);
}
inline bool QColorShower::isAlphaVisible() const
{
return alphaLab->isVisible();
}
void QColorShowLabel::mousePressEvent(QMouseEvent *e)
{
mousePressed = true;
pressPos = e->position().toPoint();
}
void QColorShowLabel::mouseMoveEvent(QMouseEvent *e)
{
#if !QT_CONFIG(draganddrop)
Q_UNUSED(e);
#else
if (!mousePressed)
return;
if ((pressPos - e->position().toPoint()).manhattanLength() > QApplication::startDragDistance()) {
QMimeData *mime = new QMimeData;
mime->setColorData(col);
QPixmap pix(30, 20);
pix.fill(col);
QPainter p(&pix);
p.drawRect(0, 0, pix.width() - 1, pix.height() - 1);
p.end();
QDrag *drg = new QDrag(this);
drg->setMimeData(mime);
drg->setPixmap(pix);
mousePressed = false;
drg->exec(Qt::CopyAction);
}
#endif
}
#if QT_CONFIG(draganddrop)
void QColorShowLabel::dragEnterEvent(QDragEnterEvent *e)
{
if (qvariant_cast<QColor>(e->mimeData()->colorData()).isValid())
e->accept();
else
e->ignore();
}
void QColorShowLabel::dragLeaveEvent(QDragLeaveEvent *)
{
}
void QColorShowLabel::dropEvent(QDropEvent *e)
{
QColor color = qvariant_cast<QColor>(e->mimeData()->colorData());
if (color.isValid()) {
col = color;
repaint();
emit colorDropped(col.rgb());
e->accept();
} else {
e->ignore();
}
}
#endif // QT_CONFIG(draganddrop)
void QColorShowLabel::mouseReleaseEvent(QMouseEvent *)
{
if (!mousePressed)
return;
mousePressed = false;
}
QColorShower::QColorShower(QColorDialog *parent)
: QWidget(parent)
{
colorDialog = parent;
curCol = qRgb(255, 255, 255);
curQColor = Qt::white;
gl = new QGridLayout(this);
const int s = gl->spacing();
gl->setContentsMargins(s, s, s, s);
lab = new QColorShowLabel(this);
#ifdef QT_SMALL_COLORDIALOG
lab->setMinimumHeight(60);
#endif
lab->setMinimumWidth(60);
// For QVGA screens only the comboboxes and color label are visible.
// For nHD screens only color and luminence pickers and color label are visible.
#if !defined(QT_SMALL_COLORDIALOG)
gl->addWidget(lab, 0, 0, -1, 1);
#else
gl->addWidget(lab, 0, 0, 1, -1);
#endif
connect(lab, &QColorShowLabel::colorDropped, this, &QColorShower::newCol);
connect(lab, &QColorShowLabel::colorDropped, this, &QColorShower::setRgb);
hEd = new QColSpinBox(this);
hEd->setRange(0, 359);
lblHue = new QLabel(this);
#ifndef QT_NO_SHORTCUT
lblHue->setBuddy(hEd);
#endif
lblHue->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
#if !defined(QT_SMALL_COLORDIALOG)
gl->addWidget(lblHue, 0, 1);
gl->addWidget(hEd, 0, 2);
#else
gl->addWidget(lblHue, 1, 0);
gl->addWidget(hEd, 2, 0);
#endif
sEd = new QColSpinBox(this);
lblSat = new QLabel(this);
#ifndef QT_NO_SHORTCUT
lblSat->setBuddy(sEd);
#endif
lblSat->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
#if !defined(QT_SMALL_COLORDIALOG)
gl->addWidget(lblSat, 1, 1);
gl->addWidget(sEd, 1, 2);
#else
gl->addWidget(lblSat, 1, 1);
gl->addWidget(sEd, 2, 1);
#endif
vEd = new QColSpinBox(this);
lblVal = new QLabel(this);
#ifndef QT_NO_SHORTCUT
lblVal->setBuddy(vEd);
#endif
lblVal->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
#if !defined(QT_SMALL_COLORDIALOG)
gl->addWidget(lblVal, 2, 1);
gl->addWidget(vEd, 2, 2);
#else
gl->addWidget(lblVal, 1, 2);
gl->addWidget(vEd, 2, 2);
#endif
rEd = new QColSpinBox(this);
lblRed = new QLabel(this);
#ifndef QT_NO_SHORTCUT
lblRed->setBuddy(rEd);
#endif
lblRed->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
#if !defined(QT_SMALL_COLORDIALOG)
gl->addWidget(lblRed, 0, 3);
gl->addWidget(rEd, 0, 4);
#else
gl->addWidget(lblRed, 3, 0);
gl->addWidget(rEd, 4, 0);
#endif
gEd = new QColSpinBox(this);
lblGreen = new QLabel(this);
#ifndef QT_NO_SHORTCUT
lblGreen->setBuddy(gEd);
#endif
lblGreen->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
#if !defined(QT_SMALL_COLORDIALOG)
gl->addWidget(lblGreen, 1, 3);
gl->addWidget(gEd, 1, 4);
#else
gl->addWidget(lblGreen, 3, 1);
gl->addWidget(gEd, 4, 1);
#endif
bEd = new QColSpinBox(this);
lblBlue = new QLabel(this);
#ifndef QT_NO_SHORTCUT
lblBlue->setBuddy(bEd);
#endif
lblBlue->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
#if !defined(QT_SMALL_COLORDIALOG)
gl->addWidget(lblBlue, 2, 3);
gl->addWidget(bEd, 2, 4);
#else
gl->addWidget(lblBlue, 3, 2);
gl->addWidget(bEd, 4, 2);
#endif
alphaEd = new QColSpinBox(this);
alphaLab = new QLabel(this);
#ifndef QT_NO_SHORTCUT
alphaLab->setBuddy(alphaEd);
#endif
alphaLab->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
#if !defined(QT_SMALL_COLORDIALOG)
gl->addWidget(alphaLab, 3, 1, 1, 3);
gl->addWidget(alphaEd, 3, 4);
#else
gl->addWidget(alphaLab, 1, 3, 3, 1);
gl->addWidget(alphaEd, 4, 3);
#endif
alphaEd->hide();
alphaLab->hide();
lblHtml = new QLabel(this);
htEd = new QLineEdit(this);
htEd->setObjectName("qt_colorname_lineedit");
#ifndef QT_NO_SHORTCUT
lblHtml->setBuddy(htEd);
#endif
#if QT_CONFIG(regularexpression)
QRegularExpression regExp(QStringLiteral("#?([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})"));
QRegularExpressionValidator *validator = new QRegularExpressionValidator(regExp, this);
htEd->setValidator(validator);
#else
htEd->setReadOnly(true);
#endif
htEd->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);
lblHtml->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
#if defined(QT_SMALL_COLORDIALOG)
gl->addWidget(lblHtml, 5, 0);
gl->addWidget(htEd, 5, 1, 1, /*colspan=*/ 2);
#else
gl->addWidget(lblHtml, 5, 1);
gl->addWidget(htEd, 5, 2, 1, /*colspan=*/ 3);
#endif
connect(hEd, &QSpinBox::valueChanged, this, &QColorShower::hsvEd);
connect(sEd, &QSpinBox::valueChanged, this, &QColorShower::hsvEd);
connect(vEd, &QSpinBox::valueChanged, this, &QColorShower::hsvEd);
connect(rEd, &QSpinBox::valueChanged, this, &QColorShower::rgbEd);
connect(gEd, &QSpinBox::valueChanged, this, &QColorShower::rgbEd);
connect(bEd, &QSpinBox::valueChanged, this, &QColorShower::rgbEd);
connect(alphaEd, &QSpinBox::valueChanged, this, &QColorShower::rgbEd);
connect(htEd, &QLineEdit::textEdited, this, &QColorShower::htmlEd);
retranslateStrings();
}
} // namespace QtPrivate
inline QRgb QColorDialogPrivate::currentColor() const { return cs->currentColor(); }
inline int QColorDialogPrivate::currentAlpha() const { return cs->currentAlpha(); }
inline void QColorDialogPrivate::setCurrentAlpha(int a) { cs->setCurrentAlpha(a); }
inline void QColorDialogPrivate::showAlpha(bool b) { cs->showAlpha(b); }
inline bool QColorDialogPrivate::isAlphaVisible() const { return cs->isAlphaVisible(); }
QColor QColorDialogPrivate::currentQColor() const
{
if (nativeDialogInUse)
return platformColorDialogHelper()->currentColor();
return cs->currentQColor();
}
void QColorShower::showCurrentColor()
{
lab->setColor(currentColor());
lab->repaint();
}
void QColorShower::rgbEd()
{
rgbOriginal = true;
curCol = qRgba(rEd->value(), gEd->value(), bEd->value(), currentAlpha());
rgb2hsv(currentColor(), hue, sat, val);
hEd->setValue(hue);
sEd->setValue(sat);
vEd->setValue(val);
htEd->setText(QColor(curCol).name());
showCurrentColor();
emit newCol(currentColor());
updateQColor();
}
void QColorShower::hsvEd()
{
rgbOriginal = false;
hue = hEd->value();
sat = sEd->value();
val = vEd->value();
QColor c;
c.setHsv(hue, sat, val);
curCol = c.rgb();
rEd->setValue(qRed(currentColor()));
gEd->setValue(qGreen(currentColor()));
bEd->setValue(qBlue(currentColor()));
htEd->setText(c.name());
showCurrentColor();
emit newCol(currentColor());
updateQColor();
}
void QColorShower::htmlEd()
{
QString t = htEd->text();
if (t.isEmpty())
return;
if (!t.startsWith(QStringLiteral("#"))) {
t = QStringLiteral("#") + t;
QSignalBlocker blocker(htEd);
htEd->setText(t);
}
QColor c = QColor::fromString(t);
if (!c.isValid())
return;
curCol = qRgba(c.red(), c.green(), c.blue(), currentAlpha());
rgb2hsv(curCol, hue, sat, val);
hEd->setValue(hue);
sEd->setValue(sat);
vEd->setValue(val);
rEd->setValue(qRed(currentColor()));
gEd->setValue(qGreen(currentColor()));
bEd->setValue(qBlue(currentColor()));
showCurrentColor();
emit newCol(currentColor());
updateQColor();
}
void QColorShower::setRgb(QRgb rgb)
{
rgbOriginal = true;
curCol = rgb;
rgb2hsv(currentColor(), hue, sat, val);
hEd->setValue(hue);
sEd->setValue(sat);
vEd->setValue(val);
rEd->setValue(qRed(currentColor()));
gEd->setValue(qGreen(currentColor()));
bEd->setValue(qBlue(currentColor()));
htEd->setText(QColor(rgb).name());
showCurrentColor();
updateQColor();
}
void QColorShower::setHsv(int h, int s, int v)
{
if (h < -1 || (uint)s > 255 || (uint)v > 255)
return;
rgbOriginal = false;
hue = h; val = v; sat = s;
QColor c;
c.setHsv(hue, sat, val);
curCol = c.rgb();
hEd->setValue(hue);
sEd->setValue(sat);
vEd->setValue(val);
rEd->setValue(qRed(currentColor()));
gEd->setValue(qGreen(currentColor()));
bEd->setValue(qBlue(currentColor()));
htEd->setText(c.name());
showCurrentColor();
updateQColor();
}
void QColorShower::retranslateStrings()
{
lblHue->setText(QColorDialog::tr("Hu&e:"));
lblSat->setText(QColorDialog::tr("&Sat:"));
lblVal->setText(QColorDialog::tr("&Val:"));
lblRed->setText(QColorDialog::tr("&Red:"));
lblGreen->setText(QColorDialog::tr("&Green:"));
lblBlue->setText(QColorDialog::tr("Bl&ue:"));
alphaLab->setText(QColorDialog::tr("A&lpha channel:"));
lblHtml->setText(QColorDialog::tr("&HTML:"));
}
void QColorShower::updateQColor()
{
QColor oldQColor(curQColor);
curQColor.setRgba(qRgba(qRed(curCol), qGreen(curCol), qBlue(curCol), currentAlpha()));
if (curQColor != oldQColor)
emit currentColorChanged(curQColor);
}
//sets all widgets to display h,s,v
void QColorDialogPrivate::_q_newHsv(int h, int s, int v)
{
if (!nativeDialogInUse) {
cs->setHsv(h, s, v);
cp->setCol(h, s);
lp->setCol(h, s, v);
}
}
//sets all widgets to display rgb
void QColorDialogPrivate::setCurrentRgbColor(QRgb rgb)
{
if (!nativeDialogInUse) {
cs->setRgb(rgb);
_q_newColorTypedIn(rgb);
}
}
// hack; doesn't keep curCol in sync, so use with care
void QColorDialogPrivate::setCurrentQColor(const QColor &color)
{
Q_Q(QColorDialog);
if (cs->curQColor != color) {
cs->curQColor = color;
emit q->currentColorChanged(color);
}
}
// size of standard and custom color selector
enum {
colorColumns = 8,
standardColorRows = 6,
customColorRows = 2
};
bool QColorDialogPrivate::selectColor(const QColor &col)
{
QRgb color = col.rgb();
// Check standard colors
if (standard) {
const QRgb *standardColors = QColorDialogOptions::standardColors();
const QRgb *standardColorsEnd = standardColors + standardColorRows * colorColumns;
const QRgb *match = std::find(standardColors, standardColorsEnd, color);
if (match != standardColorsEnd) {
const int index = int(match - standardColors);
const int column = index / standardColorRows;
const int row = index % standardColorRows;
_q_newStandard(row, column);
standard->setCurrent(row, column);
standard->setSelected(row, column);
standard->setFocus();
return true;
}
}
// Check custom colors
if (custom) {
const QRgb *customColors = QColorDialogOptions::customColors();
const QRgb *customColorsEnd = customColors + customColorRows * colorColumns;
const QRgb *match = std::find(customColors, customColorsEnd, color);
if (match != customColorsEnd) {
const int index = int(match - customColors);
const int column = index / customColorRows;
const int row = index % customColorRows;
_q_newCustom(row, column);
custom->setCurrent(row, column);
custom->setSelected(row, column);
custom->setFocus();
return true;
}
}
return false;
}
QColor QColorDialogPrivate::grabScreenColor(const QPoint &p)
{
QScreen *screen = QGuiApplication::screenAt(p);
if (!screen)
screen = QGuiApplication::primaryScreen();
const QRect screenRect = screen->geometry();
const QPixmap pixmap =
screen->grabWindow(0, p.x() - screenRect.x(), p.y() - screenRect.y(), 1, 1);
const QImage i = pixmap.toImage();
return i.pixel(0, 0);
}
//sets all widgets except cs to display rgb
void QColorDialogPrivate::_q_newColorTypedIn(QRgb rgb)
{
if (!nativeDialogInUse) {
int h, s, v;
rgb2hsv(rgb, h, s, v);
cp->setCol(h, s);
lp->setCol(h, s, v);
}
}
void QColorDialogPrivate::_q_nextCustom(int r, int c)
{
nextCust = r + customColorRows * c;
}
void QColorDialogPrivate::_q_newCustom(int r, int c)
{
const int i = r + customColorRows * c;
setCurrentRgbColor(QColorDialogOptions::customColor(i));
if (standard)
standard->setSelected(-1,-1);
}
void QColorDialogPrivate::_q_newStandard(int r, int c)
{
setCurrentRgbColor(QColorDialogOptions::standardColor(r + c * 6));
if (custom)
custom->setSelected(-1,-1);
}
void QColorDialogPrivate::_q_pickScreenColor()
{
Q_Q(QColorDialog);
auto *platformServices = QGuiApplicationPrivate::platformIntegration()->services();
if (platformServices->hasCapability(QPlatformServices::Capability::ColorPicking)) {
if (auto *colorPicker = platformServices->colorPicker(q->windowHandle())) {
q->connect(colorPicker, &QPlatformServiceColorPicker::colorPicked, q,
[q, colorPicker](const QColor &color) {
colorPicker->deleteLater();
q->setCurrentColor(color);
});
colorPicker->pickColor();
return;
}
}
if (!colorPickingEventFilter)
colorPickingEventFilter = new QColorPickingEventFilter(this, q);
q->installEventFilter(colorPickingEventFilter);
// If user pushes Escape, the last color before picking will be restored.
beforeScreenColorPicking = cs->currentColor();
#ifndef QT_NO_CURSOR
q->grabMouse(Qt::CrossCursor);
#else
q->grabMouse();
#endif
#ifdef Q_OS_WIN32
// On Windows mouse tracking doesn't work over other processes's windows
updateTimer->start(30);
// HACK: Because mouse grabbing doesn't work across processes, we have to have a dummy,
// invisible window to catch the mouse click, otherwise we will click whatever we clicked
// and loose focus.
dummyTransparentWindow.show();
#endif
q->grabKeyboard();
/* With setMouseTracking(true) the desired color can be more precisely picked up,
* and continuously pushing the mouse button is not necessary.
*/
q->setMouseTracking(true);
addCusBt->setDisabled(true);
buttons->setDisabled(true);
if (screenColorPickerButton) {
screenColorPickerButton->setDisabled(true);
const QPoint globalPos = QCursor::pos();
q->setCurrentColor(grabScreenColor(globalPos));
updateColorLabelText(globalPos);
}
}
void QColorDialogPrivate::updateColorLabelText(const QPoint &globalPos)
{
if (lblScreenColorInfo)
lblScreenColorInfo->setText(QColorDialog::tr("Cursor at %1, %2\nPress ESC to cancel")
.arg(globalPos.x())
.arg(globalPos.y()));
}
void QColorDialogPrivate::releaseColorPicking()
{
Q_Q(QColorDialog);
cp->setCrossVisible(true);
q->removeEventFilter(colorPickingEventFilter);
q->releaseMouse();
#ifdef Q_OS_WIN32
updateTimer->stop();
dummyTransparentWindow.setVisible(false);
#endif
q->releaseKeyboard();
q->setMouseTracking(false);
lblScreenColorInfo->setText("\n"_L1);
addCusBt->setDisabled(false);
buttons->setDisabled(false);
screenColorPickerButton->setDisabled(false);
}
void QColorDialogPrivate::init(const QColor &initial)
{
Q_Q(QColorDialog);
q->setSizeGripEnabled(false);
q->setWindowTitle(QColorDialog::tr("Select Color"));
// default: use the native dialog if possible. Can be overridden in setOptions()
nativeDialogInUse = (platformColorDialogHelper() != nullptr);
colorPickingEventFilter = nullptr;
nextCust = 0;
if (!nativeDialogInUse)
initWidgets();
#ifdef Q_OS_WIN32
dummyTransparentWindow.resize(1, 1);
dummyTransparentWindow.setFlags(Qt::Tool | Qt::FramelessWindowHint);
#endif
q->setCurrentColor(initial);
}
void QColorDialogPrivate::initWidgets()
{
Q_Q(QColorDialog);
QVBoxLayout *mainLay = new QVBoxLayout(q);
// there's nothing in this dialog that benefits from sizing up
mainLay->setSizeConstraint(QLayout::SetFixedSize);
QHBoxLayout *topLay = new QHBoxLayout();
mainLay->addLayout(topLay);
leftLay = nullptr;
#if defined(QT_SMALL_COLORDIALOG)
smallDisplay = true;
const int lumSpace = 20;
#else
// small displays (e.g. PDAs) cannot fit the full color dialog,
// so just use the color picker.
smallDisplay = (QGuiApplication::primaryScreen()->virtualGeometry().width() < 480 || QGuiApplication::primaryScreen()->virtualGeometry().height() < 350);
const int lumSpace = topLay->spacing() / 2;
#endif
if (!smallDisplay) {
leftLay = new QVBoxLayout;
topLay->addLayout(leftLay);
standard = new QColorWell(q, standardColorRows, colorColumns, QColorDialogOptions::standardColors());
lblBasicColors = new QLabel(q);
#ifndef QT_NO_SHORTCUT
lblBasicColors->setBuddy(standard);
#endif
q->connect(standard, SIGNAL(selected(int,int)), SLOT(_q_newStandard(int,int)));
leftLay->addWidget(lblBasicColors);
leftLay->addWidget(standard);
#if !defined(QT_SMALL_COLORDIALOG)
if (supportsColorPicking()) {
screenColorPickerButton = new QPushButton();
leftLay->addWidget(screenColorPickerButton);
lblScreenColorInfo = new QLabel("\n"_L1);
leftLay->addWidget(lblScreenColorInfo);
q->connect(screenColorPickerButton, SIGNAL(clicked()), SLOT(_q_pickScreenColor()));
} else {
screenColorPickerButton = nullptr;
lblScreenColorInfo = nullptr;
}
#endif
leftLay->addStretch();
custom = new QColorWell(q, customColorRows, colorColumns, QColorDialogOptions::customColors());
custom->setAcceptDrops(true);
q->connect(custom, SIGNAL(selected(int,int)), SLOT(_q_newCustom(int,int)));
q->connect(custom, SIGNAL(currentChanged(int,int)), SLOT(_q_nextCustom(int,int)));
q->connect(custom, &QWellArray::colorChanged, [this] (int index, QRgb color) {
QColorDialogOptions::setCustomColor(index, color);
if (custom)
custom->update();
});
lblCustomColors = new QLabel(q);
#ifndef QT_NO_SHORTCUT
lblCustomColors->setBuddy(custom);
#endif
leftLay->addWidget(lblCustomColors);
leftLay->addWidget(custom);
addCusBt = new QPushButton(q);
QObject::connect(addCusBt, SIGNAL(clicked()), q, SLOT(_q_addCustom()));
leftLay->addWidget(addCusBt);
} else {
// better color picker size for small displays
#if defined(QT_SMALL_COLORDIALOG)
QSize screenSize = QGuiApplication::screenAt(QCursor::pos())->availableGeometry().size();
pWidth = pHeight = qMin(screenSize.width(), screenSize.height());
pHeight -= 20;
if (screenSize.height() > screenSize.width())
pWidth -= 20;
#else
pWidth = 150;
pHeight = 100;
#endif
custom = nullptr;
standard = nullptr;
}
QVBoxLayout *rightLay = new QVBoxLayout;
topLay->addLayout(rightLay);
QHBoxLayout *pickLay = new QHBoxLayout;
rightLay->addLayout(pickLay);
QVBoxLayout *cLay = new QVBoxLayout;
pickLay->addLayout(cLay);
cp = new QColorPicker(q);
cp->setFrameStyle(QFrame::Panel | QFrame::Sunken);
#if defined(QT_SMALL_COLORDIALOG)
cp->hide();
#else
cLay->addSpacing(lumSpace);
cLay->addWidget(cp);
#endif
cLay->addSpacing(lumSpace);
lp = new QColorLuminancePicker(q);
#if defined(QT_SMALL_COLORDIALOG)
lp->hide();
#else
lp->setFixedWidth(20);
pickLay->addSpacing(10);
pickLay->addWidget(lp);
pickLay->addStretch();
#endif
QObject::connect(cp, SIGNAL(newCol(int,int)), lp, SLOT(setCol(int,int)));
QObject::connect(lp, SIGNAL(newHsv(int,int,int)), q, SLOT(_q_newHsv(int,int,int)));
rightLay->addStretch();
cs = new QColorShower(q);
pickLay->setContentsMargins(cs->gl->contentsMargins());
QObject::connect(cs, SIGNAL(newCol(QRgb)), q, SLOT(_q_newColorTypedIn(QRgb)));
QObject::connect(cs, SIGNAL(currentColorChanged(QColor)),
q, SIGNAL(currentColorChanged(QColor)));
#if defined(QT_SMALL_COLORDIALOG)
topLay->addWidget(cs);
#else
rightLay->addWidget(cs);
if (leftLay)
leftLay->addSpacing(cs->gl->contentsMargins().right());
#endif
buttons = new QDialogButtonBox(q);
mainLay->addWidget(buttons);
ok = buttons->addButton(QDialogButtonBox::Ok);
QObject::connect(ok, SIGNAL(clicked()), q, SLOT(accept()));
ok->setDefault(true);
cancel = buttons->addButton(QDialogButtonBox::Cancel);
QObject::connect(cancel, SIGNAL(clicked()), q, SLOT(reject()));
#ifdef Q_OS_WIN32
updateTimer = new QTimer(q);
QObject::connect(updateTimer, SIGNAL(timeout()), q, SLOT(_q_updateColorPicking()));
#endif
retranslateStrings();
}
void QColorDialogPrivate::initHelper(QPlatformDialogHelper *h)
{
QColorDialog *d = q_func();
QObject::connect(h, SIGNAL(currentColorChanged(QColor)), d, SIGNAL(currentColorChanged(QColor)));
QObject::connect(h, SIGNAL(colorSelected(QColor)), d, SIGNAL(colorSelected(QColor)));
static_cast<QPlatformColorDialogHelper *>(h)->setOptions(options);
}
void QColorDialogPrivate::helperPrepareShow(QPlatformDialogHelper *)
{
options->setWindowTitle(q_func()->windowTitle());
}
void QColorDialogPrivate::_q_addCustom()
{
QColorDialogOptions::setCustomColor(nextCust, cs->currentColor());
if (custom)
custom->update();
nextCust = (nextCust+1) % QColorDialogOptions::customColorCount();
}
void QColorDialogPrivate::retranslateStrings()
{
if (nativeDialogInUse)
return;
if (!smallDisplay) {
lblBasicColors->setText(QColorDialog::tr("&Basic colors"));
lblCustomColors->setText(QColorDialog::tr("&Custom colors"));
addCusBt->setText(QColorDialog::tr("&Add to Custom Colors"));
#if !defined(QT_SMALL_COLORDIALOG)
if (screenColorPickerButton)
screenColorPickerButton->setText(QColorDialog::tr("&Pick Screen Color"));
#endif
}
cs->retranslateStrings();
}
bool QColorDialogPrivate::supportsColorPicking() const
{
const auto integration = QGuiApplicationPrivate::platformIntegration();
return integration->hasCapability(QPlatformIntegration::ScreenWindowGrabbing)
|| integration->services()->hasCapability(QPlatformServices::Capability::ColorPicking);
}
bool QColorDialogPrivate::canBeNativeDialog() const
{
// Don't use Q_Q here! This function is called from ~QDialog,
// so Q_Q calling q_func() invokes undefined behavior (invalid cast in q_func()).
const QDialog * const q = static_cast<const QDialog*>(q_ptr);
if (nativeDialogInUse)
return true;
if (QCoreApplication::testAttribute(Qt::AA_DontUseNativeDialogs)
|| q->testAttribute(Qt::WA_DontShowOnScreen)
|| (options->options() & QColorDialog::DontUseNativeDialog)) {
return false;
}
return strcmp(QColorDialog::staticMetaObject.className(), q->metaObject()->className()) == 0;
}
static const Qt::WindowFlags qcd_DefaultWindowFlags =
Qt::Dialog | Qt::WindowTitleHint
| Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint;
/*!
\class QColorDialog
\brief The QColorDialog class provides a dialog widget for specifying colors.
\ingroup standard-dialogs
\inmodule QtWidgets
The color dialog's function is to allow users to choose colors.
For example, you might use this in a drawing program to allow the
user to set the brush color.
The static functions provide modal color dialogs.
\omit
If you require a modeless dialog, use the QColorDialog constructor.
\endomit
The static getColor() function shows the dialog, and allows the user to
specify a color. This function can also be used to let users choose a
color with a level of transparency: pass the ShowAlphaChannel option as
an additional argument.
The user can store customCount() different custom colors. The
custom colors are shared by all color dialogs, and remembered
during the execution of the program. Use setCustomColor() to set
the custom colors, and use customColor() to get them.
When pressing the "Pick Screen Color" button, the cursor changes to a haircross
and the colors on the screen are scanned. The user can pick up one by clicking
the mouse or the Enter button. Pressing Escape restores the last color selected
before entering this mode.
The \l{dialogs/standarddialogs}{Standard Dialogs} example shows
how to use QColorDialog as well as other built-in Qt dialogs.
\image fusion-colordialog.png A color dialog in the Fusion widget style.
\sa QColor, QFileDialog, QFontDialog, {Standard Dialogs Example}
*/
/*!
\since 4.5
Constructs a color dialog with the given \a parent.
*/
QColorDialog::QColorDialog(QWidget *parent)
: QColorDialog(QColor(Qt::white), parent)
{
}
/*!
\since 4.5
Constructs a color dialog with the given \a parent and specified
\a initial color.
*/
QColorDialog::QColorDialog(const QColor &initial, QWidget *parent)
: QDialog(*new QColorDialogPrivate, parent, qcd_DefaultWindowFlags)
{
Q_D(QColorDialog);
d->init(initial);
}
void QColorDialogPrivate::setCurrentColor(const QColor &color, SetColorMode setColorMode)
{
if (nativeDialogInUse) {
platformColorDialogHelper()->setCurrentColor(color);
return;
}
if (setColorMode & ShowColor) {
setCurrentRgbColor(color.rgb());
setCurrentAlpha(color.alpha());
}
if (setColorMode & SelectColor)
selectColor(color);
}
/*!
\property QColorDialog::currentColor
\brief the currently selected color in the dialog
*/
void QColorDialog::setCurrentColor(const QColor &color)
{
Q_D(QColorDialog);
d->setCurrentColor(color);
}
QColor QColorDialog::currentColor() const
{
Q_D(const QColorDialog);
return d->currentQColor();
}
/*!
Returns the color that the user selected by clicking the \uicontrol{OK}
or equivalent button.
\note This color is not always the same as the color held by the
\l currentColor property since the user can choose different colors
before finally selecting the one to use.
*/
QColor QColorDialog::selectedColor() const
{
Q_D(const QColorDialog);
return d->selectedQColor;
}
/*!
Sets the given \a option to be enabled if \a on is true;
otherwise, clears the given \a option.
\sa options, testOption()
*/
void QColorDialog::setOption(ColorDialogOption option, bool on)
{
const QColorDialog::ColorDialogOptions previousOptions = options();
if (!(previousOptions & option) != !on)
setOptions(previousOptions ^ option);
}
/*!
\since 4.5
Returns \c true if the given \a option is enabled; otherwise, returns
false.
\sa options, setOption()
*/
bool QColorDialog::testOption(ColorDialogOption option) const
{
Q_D(const QColorDialog);
return d->options->testOption(static_cast<QColorDialogOptions::ColorDialogOption>(option));
}
/*!
\property QColorDialog::options
\brief the various options that affect the look and feel of the dialog
By default, all options are disabled.
Options should be set before showing the dialog. Setting them while the
dialog is visible is not guaranteed to have an immediate effect on the
dialog (depending on the option and on the platform).
\sa setOption(), testOption()
*/
void QColorDialog::setOptions(ColorDialogOptions options)
{
Q_D(QColorDialog);
if (QColorDialog::options() == options)
return;
d->options->setOptions(QColorDialogOptions::ColorDialogOptions(int(options)));
if ((options & DontUseNativeDialog) && d->nativeDialogInUse) {
d->nativeDialogInUse = false;
d->initWidgets();
}
if (!d->nativeDialogInUse) {
d->buttons->setVisible(!(options & NoButtons));
d->showAlpha(options & ShowAlphaChannel);
}
}
QColorDialog::ColorDialogOptions QColorDialog::options() const
{
Q_D(const QColorDialog);
return QColorDialog::ColorDialogOptions(int(d->options->options()));
}
/*!
\enum QColorDialog::ColorDialogOption
\since 4.5
This enum specifies various options that affect the look and feel
of a color dialog.
\value ShowAlphaChannel Allow the user to select the alpha component of a color.
\value NoButtons Don't display \uicontrol{OK} and \uicontrol{Cancel} buttons. (Useful for "live dialogs".)
\value DontUseNativeDialog Use Qt's standard color dialog instead of the operating system
native color dialog.
\sa options, setOption(), testOption(), windowModality()
*/
/*!
\fn void QColorDialog::currentColorChanged(const QColor &color)
This signal is emitted whenever the current color changes in the dialog.
The current color is specified by \a color.
\sa color, colorSelected()
*/
/*!
\fn void QColorDialog::colorSelected(const QColor &color);
This signal is emitted just after the user has clicked \uicontrol{OK} to
select a color to use. The chosen color is specified by \a color.
\sa color, currentColorChanged()
*/
/*!
Changes the visibility of the dialog. If \a visible is true, the dialog
is shown; otherwise, it is hidden.
*/
void QColorDialog::setVisible(bool visible)
{
Q_D(QColorDialog);
if (visible){
if (testAttribute(Qt::WA_WState_ExplicitShowHide) && !testAttribute(Qt::WA_WState_Hidden))
return;
} else if (testAttribute(Qt::WA_WState_ExplicitShowHide) && testAttribute(Qt::WA_WState_Hidden))
return;
if (visible)
d->selectedQColor = QColor();
if (d->nativeDialogInUse) {
if (d->setNativeDialogVisible(visible)) {
// Set WA_DontShowOnScreen so that QDialog::setVisible(visible) below
// updates the state correctly, but skips showing the non-native version:
setAttribute(Qt::WA_DontShowOnScreen);
} else {
d->initWidgets();
}
} else {
setAttribute(Qt::WA_DontShowOnScreen, false);
}
QDialog::setVisible(visible);
}
/*!
\since 4.5
Opens the dialog and connects its colorSelected() signal to the slot specified
by \a receiver and \a member.
The signal will be disconnected from the slot when the dialog is closed.
*/
void QColorDialog::open(QObject *receiver, const char *member)
{
Q_D(QColorDialog);
connect(this, SIGNAL(colorSelected(QColor)), receiver, member);
d->receiverToDisconnectOnClose = receiver;
d->memberToDisconnectOnClose = member;
QDialog::open();
}
/*!
\since 4.5
Pops up a modal color dialog with the given window \a title (or "Select Color" if none is
specified), lets the user choose a color, and returns that color. The color is initially set
to \a initial. The dialog is a child of \a parent. It returns an invalid (see
QColor::isValid()) color if the user cancels the dialog.
The \a options argument allows you to customize the dialog.
*/
QColor QColorDialog::getColor(const QColor &initial, QWidget *parent, const QString &title,
ColorDialogOptions options)
{
QColorDialog dlg(parent);
if (!title.isEmpty())
dlg.setWindowTitle(title);
dlg.setOptions(options);
dlg.setCurrentColor(initial);
dlg.exec();
return dlg.selectedColor();
}
/*!
Destroys the color dialog.
*/
QColorDialog::~QColorDialog()
{
}
/*!
\reimp
*/
void QColorDialog::changeEvent(QEvent *e)
{
Q_D(QColorDialog);
if (e->type() == QEvent::LanguageChange)
d->retranslateStrings();
QDialog::changeEvent(e);
}
void QColorDialogPrivate::_q_updateColorPicking()
{
#ifndef QT_NO_CURSOR
Q_Q(QColorDialog);
static QPoint lastGlobalPos;
QPoint newGlobalPos = QCursor::pos();
if (lastGlobalPos == newGlobalPos)
return;
lastGlobalPos = newGlobalPos;
if (!q->rect().contains(q->mapFromGlobal(newGlobalPos))) { // Inside the dialog mouse tracking works, handleColorPickingMouseMove will be called
updateColorPicking(newGlobalPos);
#ifdef Q_OS_WIN32
dummyTransparentWindow.setPosition(newGlobalPos);
#endif
}
#endif // ! QT_NO_CURSOR
}
void QColorDialogPrivate::updateColorPicking(const QPoint &globalPos)
{
const QColor color = grabScreenColor(globalPos);
// 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.
setCurrentColor(color, ShowColor);
updateColorLabelText(globalPos);
}
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->position().toPoint()));
updateColorPicking(e->globalPosition().toPoint());
return true;
}
bool QColorDialogPrivate::handleColorPickingMouseButtonRelease(QMouseEvent *e)
{
setCurrentColor(grabScreenColor(e->globalPosition().toPoint()), SetColorAll);
releaseColorPicking();
return true;
}
bool QColorDialogPrivate::handleColorPickingKeyPress(QKeyEvent *e)
{
Q_Q(QColorDialog);
#if QT_CONFIG(shortcut)
if (e->matches(QKeySequence::Cancel)) {
releaseColorPicking();
q->setCurrentColor(beforeScreenColorPicking);
} else
#endif
if (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) {
q->setCurrentColor(grabScreenColor(QCursor::pos()));
releaseColorPicking();
}
e->accept();
return true;
}
/*!
Closes the dialog and sets its result code to \a result. If this dialog
is shown with exec(), done() causes the local event loop to finish,
and exec() to return \a result.
\sa QDialog::done()
*/
void QColorDialog::done(int result)
{
Q_D(QColorDialog);
if (result == Accepted) {
d->selectedQColor = d->currentQColor();
emit colorSelected(d->selectedQColor);
} else {
d->selectedQColor = QColor();
}
QDialog::done(result);
if (d->receiverToDisconnectOnClose) {
disconnect(this, SIGNAL(colorSelected(QColor)),
d->receiverToDisconnectOnClose, d->memberToDisconnectOnClose);
d->receiverToDisconnectOnClose = nullptr;
}
d->memberToDisconnectOnClose.clear();
}
QT_END_NAMESPACE
#include "qcolordialog.moc"
#include "moc_qcolordialog.cpp"