1270 lines
31 KiB
C++
1270 lines
31 KiB
C++
/****************************************************************************
|
|
**
|
|
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
|
** All rights reserved.
|
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
|
**
|
|
** This file is part of a Qt Solutions component.
|
|
**
|
|
** Commercial Usage
|
|
** Licensees holding valid Qt Commercial licenses may use this file in
|
|
** accordance with the Qt Solutions Commercial License Agreement provided
|
|
** with the Software or, alternatively, in accordance with the terms
|
|
** contained in a written agreement between you and Nokia.
|
|
**
|
|
** GNU Lesser General Public License Usage
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
|
** General Public License version 2.1 as published by the Free Software
|
|
** Foundation and appearing in the file LICENSE.LGPL included in the
|
|
** packaging of this file. Please review the following information to
|
|
** ensure the GNU Lesser General Public License version 2.1 requirements
|
|
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
**
|
|
** In addition, as a special exception, Nokia gives you certain
|
|
** additional rights. These rights are described in the Nokia Qt LGPL
|
|
** Exception version 1.1, included in the file LGPL_EXCEPTION.txt in this
|
|
** package.
|
|
**
|
|
** GNU General Public License Usage
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
** General Public License version 3.0 as published by the Free Software
|
|
** Foundation and appearing in the file LICENSE.GPL included in the
|
|
** packaging of this file. Please review the following information to
|
|
** ensure the GNU General Public License version 3.0 requirements will be
|
|
** met: http://www.gnu.org/copyleft/gpl.html.
|
|
**
|
|
** Please note Third Party Software included with Qt Solutions may impose
|
|
** additional restrictions and it is the user's responsibility to ensure
|
|
** that they have met the licensing requirements of the GPL, LGPL, or Qt
|
|
** Solutions Commercial license and the relevant license of the Third
|
|
** Party Software they are using.
|
|
**
|
|
** If you are unsure which license is appropriate for your use, please
|
|
** contact Nokia at qt-info@nokia.com.
|
|
**
|
|
****************************************************************************/
|
|
|
|
#include <QApplication>
|
|
#include <QColorDialog>
|
|
#include <QFocusEvent>
|
|
#include <QGridLayout>
|
|
#include <QHideEvent>
|
|
#include <QKeyEvent>
|
|
#include <QLabel>
|
|
#include <QLayout>
|
|
#include <QMouseEvent>
|
|
#include <QPaintEvent>
|
|
#include <QPainter>
|
|
#include <QPixmap>
|
|
#include <QPushButton>
|
|
#include <QShowEvent>
|
|
#include <QStyle>
|
|
#include <QToolTip>
|
|
#include <QtCore/QMap>
|
|
#include <cmath>
|
|
|
|
#include "../vmisc/defglobal.h"
|
|
#include "qtcolorpicker.h"
|
|
|
|
/*! \class QtColorPicker
|
|
|
|
\brief The QtColorPicker class provides a widget for selecting
|
|
colors from a popup color grid.
|
|
|
|
Users can invoke the color picker by clicking on it, or by
|
|
navigating to it and pressing Space. They can use the mouse or
|
|
arrow keys to navigate between colors on the grid, and select a
|
|
color by clicking or by pressing Enter or Space. The
|
|
colorChanged() signal is emitted whenever the color picker's color
|
|
changes.
|
|
|
|
The widget also supports negative selection: Users can click and
|
|
hold the mouse button on the QtColorPicker widget, then move the
|
|
mouse over the color grid and release the mouse button over the
|
|
color they wish to select.
|
|
|
|
The color grid shows a customized selection of colors. An optional
|
|
ellipsis "..." button (signifying "more") can be added at the
|
|
bottom of the grid; if the user presses this, a QColorDialog pops
|
|
up and lets them choose any color they like. This button is made
|
|
available by using setColorDialogEnabled().
|
|
|
|
When a color is selected, the QtColorPicker widget shows the color
|
|
and its name. If the name cannot be determined, the translatable
|
|
name "Custom" is used.
|
|
|
|
The QtColorPicker object is optionally initialized with the number
|
|
of columns in the color grid. Colors are then added left to right,
|
|
top to bottom using insertColor(). If the number of columns is not
|
|
set, QtColorPicker calculates the number of columns and rows that
|
|
will make the grid as square as possible.
|
|
|
|
\code
|
|
DrawWidget::DrawWidget(QWidget *parent, const char *name)
|
|
{
|
|
QtColorPicker *picker = new QtColorPicker(this);
|
|
picker->insertColor(red, "Red"));
|
|
picker->insertColor(QColor("green"), "Green"));
|
|
picker->insertColor(QColor(0, 0, 255), "Blue"));
|
|
picker->insertColor(white);
|
|
|
|
connect(colors, SIGNAL(colorChanged(const QColor &)), SLOT(setCurrentColor(const QColor &)));
|
|
}
|
|
\endcode
|
|
|
|
An alternative to adding colors manually is to initialize the grid
|
|
with QColorDialog's standard colors using setStandardColors().
|
|
|
|
QtColorPicker also provides a the static function getColor(),
|
|
which pops up the grid of standard colors at any given point.
|
|
|
|
\img colorpicker1.png
|
|
\img colorpicker2.png
|
|
|
|
\sa QColorDialog
|
|
*/
|
|
|
|
/*! \fn QtColorPicker::colorChanged(const QColor &color)
|
|
|
|
This signal is emitted when the QtColorPicker's color is changed.
|
|
\a color is the new color.
|
|
|
|
To obtain the color's name, use text().
|
|
*/
|
|
|
|
/*
|
|
A class that acts very much like a QPushButton. It's not styled,
|
|
so we can expect the exact same look, feel and geometry
|
|
everywhere. Also, this button always emits clicked on
|
|
mouseRelease, even if the mouse button was not pressed inside the
|
|
widget.
|
|
*/
|
|
class ColorPickerButton : public QFrame
|
|
{
|
|
Q_OBJECT // NOLINT
|
|
|
|
public:
|
|
explicit ColorPickerButton(QWidget *parent);
|
|
|
|
signals:
|
|
void clicked();
|
|
|
|
protected:
|
|
void mousePressEvent(QMouseEvent *e) override;
|
|
void mouseMoveEvent(QMouseEvent *e) override;
|
|
void mouseReleaseEvent(QMouseEvent *e) override;
|
|
void keyPressEvent(QKeyEvent *e) override;
|
|
void keyReleaseEvent(QKeyEvent *e) override;
|
|
void paintEvent(QPaintEvent *e) override;
|
|
void focusInEvent(QFocusEvent *e) override;
|
|
void focusOutEvent(QFocusEvent *e) override;
|
|
};
|
|
|
|
/*
|
|
This class represents each "color" or item in the color grid.
|
|
*/
|
|
class ColorPickerItem : public QFrame
|
|
{
|
|
Q_OBJECT // NOLINT
|
|
|
|
public:
|
|
ColorPickerItem(const QColor &color = Qt::white, const QString &text = QString(), QWidget *parent = nullptr);
|
|
~ColorPickerItem();
|
|
|
|
auto color() const -> QColor;
|
|
auto text() const -> QString;
|
|
|
|
void setSelected(bool);
|
|
auto isSelected() const -> bool;
|
|
signals:
|
|
void clicked();
|
|
void selected();
|
|
|
|
public slots:
|
|
void setColor(const QColor &color, const QString &text = QString());
|
|
|
|
protected:
|
|
void mousePressEvent(QMouseEvent *e);
|
|
void mouseReleaseEvent(QMouseEvent *e);
|
|
void mouseMoveEvent(QMouseEvent *e);
|
|
void paintEvent(QPaintEvent *e);
|
|
|
|
private:
|
|
QColor c;
|
|
QString t;
|
|
bool sel;
|
|
};
|
|
|
|
/*
|
|
|
|
*/
|
|
class ColorPickerPopup : public QFrame
|
|
{
|
|
Q_OBJECT // NOLINT
|
|
|
|
public:
|
|
ColorPickerPopup(int width, bool withColorDialog, QWidget *parent = nullptr);
|
|
~ColorPickerPopup();
|
|
|
|
void insertColor(const QColor &col, const QString &text, vsizetype index);
|
|
void exec();
|
|
|
|
void setExecFlag();
|
|
|
|
auto lastSelected() const -> QColor;
|
|
|
|
auto find(const QColor &col) const -> ColorPickerItem *;
|
|
auto color(int index) const -> QColor;
|
|
|
|
auto CustomItems() const -> QVector<ColorPickerItem *>;
|
|
|
|
signals:
|
|
void selected(const QColor &);
|
|
void hid();
|
|
|
|
public slots:
|
|
void getColorFromDialog();
|
|
|
|
protected slots:
|
|
void updateSelected();
|
|
|
|
protected:
|
|
void keyPressEvent(QKeyEvent *e);
|
|
void showEvent(QShowEvent *e);
|
|
void hideEvent(QHideEvent *e);
|
|
void mouseReleaseEvent(QMouseEvent *e);
|
|
|
|
void regenerateGrid();
|
|
|
|
private:
|
|
Q_DISABLE_COPY_MOVE(ColorPickerPopup) // NOLINT
|
|
QMap<int, QMap<int, QWidget *>> widgetAt{};
|
|
QList<ColorPickerItem *> items{};
|
|
QGridLayout *grid{nullptr};
|
|
ColorPickerButton *moreButton{nullptr};
|
|
QEventLoop *eventLoop{nullptr};
|
|
|
|
int lastPos{0};
|
|
int cols;
|
|
QColor lastSel{};
|
|
};
|
|
|
|
/*!
|
|
Constructs a QtColorPicker widget. The popup will display a grid
|
|
with \a cols columns, or if \a cols is -1, the number of columns
|
|
will be calculated automatically.
|
|
|
|
If \a enableColorDialog is true, the popup will also have a "More"
|
|
button (signified by an ellipsis "...") that presents a
|
|
QColorDialog when clicked.
|
|
|
|
After constructing a QtColorPicker, call insertColor() to add
|
|
individual colors to the popup grid, or call setStandardColors()
|
|
to add all the standard colors in one go.
|
|
|
|
The \a parent argument is passed to QFrame's constructor.
|
|
|
|
\sa QFrame
|
|
*/
|
|
QtColorPicker::QtColorPicker(QWidget *parent, int columns, bool enableColorDialog)
|
|
: QPushButton(parent),
|
|
withColorDialog(enableColorDialog)
|
|
{
|
|
setFocusPolicy(Qt::StrongFocus);
|
|
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
|
|
setAutoDefault(false);
|
|
setAutoFillBackground(true);
|
|
setCheckable(true);
|
|
|
|
// Set text
|
|
setText(tr("Black"));
|
|
firstInserted = false;
|
|
|
|
// Create and set icon
|
|
col = Qt::black;
|
|
dirty = true;
|
|
|
|
// Create color grid popup and connect to it.
|
|
popup = new ColorPickerPopup(columns, withColorDialog, this);
|
|
connect(popup, SIGNAL(selected(const QColor &)), SLOT(setCurrentColor(const QColor &)));
|
|
connect(popup, SIGNAL(hid()), SLOT(popupClosed()));
|
|
|
|
// Connect this push button's pressed() signal.
|
|
connect(this, SIGNAL(toggled(bool)), SLOT(buttonPressed(bool)));
|
|
}
|
|
|
|
/*!
|
|
Destructs the QtColorPicker.
|
|
*/
|
|
QtColorPicker::~QtColorPicker()
|
|
{
|
|
}
|
|
|
|
/*! \internal
|
|
|
|
Pops up the color grid, and makes sure the status of
|
|
QtColorPicker's button is right.
|
|
*/
|
|
void QtColorPicker::buttonPressed(bool toggled)
|
|
{
|
|
if (!toggled)
|
|
return;
|
|
|
|
const QRect desktop = this->parentWidget()->geometry();
|
|
// Make sure the popup is inside the desktop.
|
|
QPoint pos = rect().bottomLeft();
|
|
if (pos.x() < desktop.left())
|
|
pos.setX(desktop.left());
|
|
if (pos.y() < desktop.top())
|
|
pos.setY(desktop.top());
|
|
|
|
if ((pos.x() + popup->sizeHint().width()) > desktop.width())
|
|
pos.setX(desktop.width() - popup->sizeHint().width());
|
|
if ((pos.y() + popup->sizeHint().height()) > desktop.bottom())
|
|
pos.setY(desktop.bottom() - popup->sizeHint().height());
|
|
popup->move(mapToGlobal(pos));
|
|
|
|
if (ColorPickerItem *item = popup->find(col))
|
|
item->setSelected(true);
|
|
|
|
// Remove focus from this widget, preventing the focus rect
|
|
// from showing when the popup is shown. Order an update to
|
|
// make sure the focus rect is cleared.
|
|
clearFocus();
|
|
update();
|
|
|
|
// Allow keyboard navigation as soon as the popup shows.
|
|
popup->setFocus();
|
|
|
|
// Execute the popup. The popup will enter the event loop.
|
|
popup->show();
|
|
}
|
|
|
|
/*!
|
|
\internal
|
|
*/
|
|
void QtColorPicker::paintEvent(QPaintEvent *e)
|
|
{
|
|
if (dirty)
|
|
{
|
|
int const iconSize = style()->pixelMetric(QStyle::PM_SmallIconSize);
|
|
QPixmap pix(iconSize, iconSize);
|
|
pix.fill(palette().button().color());
|
|
|
|
QPainter p(&pix);
|
|
|
|
int const w = pix.width(); // width of cell in pixels
|
|
int const h = pix.height(); // height of cell in pixels
|
|
p.setPen(QPen(Qt::gray));
|
|
p.setBrush(col);
|
|
p.drawRect(2, 2, w - 5, h - 5);
|
|
setIcon(QIcon(pix));
|
|
|
|
dirty = false;
|
|
}
|
|
QPushButton::paintEvent(e);
|
|
}
|
|
|
|
/*! \internal
|
|
|
|
Makes sure the button isn't pressed when the popup hides.
|
|
*/
|
|
void QtColorPicker::popupClosed()
|
|
{
|
|
setChecked(false);
|
|
setFocus();
|
|
}
|
|
|
|
/*!
|
|
Returns the currently selected color.
|
|
|
|
\sa text()
|
|
*/
|
|
auto QtColorPicker::currentColor() const -> QColor
|
|
{
|
|
return col;
|
|
}
|
|
|
|
/*!
|
|
Returns the color at position \a index.
|
|
*/
|
|
auto QtColorPicker::color(int index) const -> QColor
|
|
{
|
|
return popup->color(index);
|
|
}
|
|
|
|
/*!
|
|
Adds the 17 predefined colors from the Qt namespace.
|
|
|
|
(The names given to the colors, "Black", "White", "Red", etc., are
|
|
all translatable.)
|
|
|
|
\sa insertColor()
|
|
*/
|
|
void QtColorPicker::setStandardColors()
|
|
{
|
|
insertColor(Qt::black, tr("Black"));
|
|
insertColor(Qt::white, tr("White"));
|
|
insertColor(Qt::red, tr("Red"));
|
|
insertColor(Qt::darkRed, tr("Dark red"));
|
|
insertColor(Qt::green, tr("Green"));
|
|
insertColor(Qt::darkGreen, tr("Dark green"));
|
|
insertColor(Qt::blue, tr("Blue"));
|
|
insertColor(Qt::darkBlue, tr("Dark blue"));
|
|
insertColor(Qt::cyan, tr("Cyan"));
|
|
insertColor(Qt::darkCyan, tr("Dark cyan"));
|
|
insertColor(Qt::magenta, tr("Magenta"));
|
|
insertColor(Qt::darkMagenta, tr("Dark magenta"));
|
|
insertColor(Qt::yellow, tr("Yellow"));
|
|
insertColor(Qt::darkYellow, tr("Dark yellow"));
|
|
insertColor(Qt::gray, tr("Gray"));
|
|
insertColor(Qt::darkGray, tr("Dark gray"));
|
|
insertColor(Qt::lightGray, tr("Light gray"));
|
|
}
|
|
|
|
auto QtColorPicker::CustomColors() const -> QVector<QColor>
|
|
{
|
|
QVector<QColor> customColor;
|
|
if (popup != nullptr)
|
|
{
|
|
QVector<ColorPickerItem *> const items = popup->CustomItems();
|
|
customColor.reserve(items.size());
|
|
|
|
for (auto *item : items)
|
|
{
|
|
if (item != nullptr)
|
|
{
|
|
customColor.append(item->color());
|
|
}
|
|
}
|
|
}
|
|
return customColor;
|
|
}
|
|
|
|
/*!
|
|
Makes \a color current. If \a color is not already in the color grid, it
|
|
is inserted with the text "Custom".
|
|
|
|
This function emits the colorChanged() signal if the new color is
|
|
valid, and different from the old one.
|
|
*/
|
|
void QtColorPicker::setCurrentColor(const QColor &color)
|
|
{
|
|
if (col == color || !color.isValid())
|
|
return;
|
|
|
|
ColorPickerItem *item = popup->find(color);
|
|
if (!item)
|
|
{
|
|
insertColor(color, tr("Custom"));
|
|
item = popup->find(color);
|
|
}
|
|
|
|
col = color;
|
|
setText(item->text());
|
|
|
|
dirty = true;
|
|
|
|
popup->hide();
|
|
repaint();
|
|
|
|
item->setSelected(true);
|
|
emit colorChanged(color);
|
|
}
|
|
|
|
/*!
|
|
Adds the color \a color with the name \a text to the color grid,
|
|
at position \a index. If index is -1, the color is assigned
|
|
automatically assigned a position, starting from left to right,
|
|
top to bottom.
|
|
*/
|
|
void QtColorPicker::insertColor(const QColor &color, const QString &text, int index)
|
|
{
|
|
popup->insertColor(color, text, index);
|
|
if (!firstInserted)
|
|
{
|
|
col = color;
|
|
setText(text);
|
|
firstInserted = true;
|
|
}
|
|
}
|
|
|
|
/*! \property QtColorPicker::colorDialog
|
|
\brief Whether the ellipsis "..." (more) button is available.
|
|
|
|
If this property is set to TRUE, the color grid popup will include
|
|
a "More" button (signified by an ellipsis, "...") which pops up a
|
|
QColorDialog when clicked. The user will then be able to select
|
|
any custom color they like.
|
|
*/
|
|
void QtColorPicker::setColorDialogEnabled(bool enabled)
|
|
{
|
|
withColorDialog = enabled;
|
|
}
|
|
auto QtColorPicker::colorDialogEnabled() const -> bool
|
|
{
|
|
return withColorDialog;
|
|
}
|
|
|
|
/*!
|
|
Pops up a color grid with Qt default colors at \a point, using
|
|
global coordinates. If \a allowCustomColors is true, there will
|
|
also be a button on the popup that invokes QColorDialog.
|
|
|
|
For example:
|
|
|
|
\code
|
|
void Drawer::mouseReleaseEvent(QMouseEvent *e)
|
|
{
|
|
if (e->button() & RightButton) {
|
|
QColor color = QtColorPicker::getColor(mapToGlobal(e->pos()));
|
|
}
|
|
}
|
|
\endcode
|
|
*/
|
|
auto QtColorPicker::getColor(const QPoint &point, bool allowCustomColors) -> QColor
|
|
{
|
|
ColorPickerPopup popup(-1, allowCustomColors);
|
|
|
|
popup.insertColor(Qt::black, tr("Black"), 0);
|
|
popup.insertColor(Qt::white, tr("White"), 1);
|
|
popup.insertColor(Qt::red, tr("Red"), 2);
|
|
popup.insertColor(Qt::darkRed, tr("Dark red"), 3);
|
|
popup.insertColor(Qt::green, tr("Green"), 4);
|
|
popup.insertColor(Qt::darkGreen, tr("Dark green"), 5);
|
|
popup.insertColor(Qt::blue, tr("Blue"), 6);
|
|
popup.insertColor(Qt::darkBlue, tr("Dark blue"), 7);
|
|
popup.insertColor(Qt::cyan, tr("Cyan"), 8);
|
|
popup.insertColor(Qt::darkCyan, tr("Dark cyan"), 9);
|
|
popup.insertColor(Qt::magenta, tr("Magenta"), 10);
|
|
popup.insertColor(Qt::darkMagenta, tr("Dark magenta"), 11);
|
|
popup.insertColor(Qt::yellow, tr("Yellow"), 12);
|
|
popup.insertColor(Qt::darkYellow, tr("Dark yellow"), 13);
|
|
popup.insertColor(Qt::gray, tr("Gray"), 14);
|
|
popup.insertColor(Qt::darkGray, tr("Dark gray"), 15);
|
|
popup.insertColor(Qt::lightGray, tr("Light gray"), 16);
|
|
|
|
popup.move(point);
|
|
popup.exec();
|
|
return popup.lastSelected();
|
|
}
|
|
|
|
/*! \internal
|
|
|
|
Constructs the popup widget.
|
|
*/
|
|
ColorPickerPopup::ColorPickerPopup(int width, bool withColorDialog, QWidget *parent)
|
|
: QFrame(parent, Qt::Popup),
|
|
cols(width)
|
|
{
|
|
setFrameStyle(QFrame::StyledPanel);
|
|
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
|
|
|
setFocusPolicy(Qt::StrongFocus);
|
|
setMouseTracking(true);
|
|
|
|
if (withColorDialog)
|
|
{
|
|
moreButton = new ColorPickerButton(this);
|
|
moreButton->setFixedWidth(24);
|
|
moreButton->setFixedHeight(21);
|
|
moreButton->setFrameRect(QRect(2, 2, 20, 17));
|
|
connect(moreButton, SIGNAL(clicked()), SLOT(getColorFromDialog()));
|
|
}
|
|
else
|
|
{
|
|
moreButton = nullptr;
|
|
}
|
|
|
|
eventLoop = nullptr;
|
|
grid = nullptr;
|
|
regenerateGrid();
|
|
}
|
|
|
|
/*! \internal
|
|
|
|
Destructs the popup widget.
|
|
*/
|
|
ColorPickerPopup::~ColorPickerPopup()
|
|
{
|
|
if (eventLoop != nullptr)
|
|
{
|
|
eventLoop->exit();
|
|
}
|
|
}
|
|
|
|
/*! \internal
|
|
|
|
If there is an item whole color is equal to \a col, returns a
|
|
pointer to this item; otherwise returns 0.
|
|
*/
|
|
auto ColorPickerPopup::find(const QColor &col) const -> ColorPickerItem *
|
|
{
|
|
auto item =
|
|
std::find_if(items.begin(), items.end(), [col](ColorPickerItem *item) { return item && item->color() == col; });
|
|
|
|
if (item != items.end())
|
|
{
|
|
return *item;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
/*! \internal
|
|
|
|
Adds \a item to the grid. The items are added from top-left to
|
|
bottom-right.
|
|
*/
|
|
void ColorPickerPopup::insertColor(const QColor &col, const QString &text, vsizetype index)
|
|
{
|
|
// Don't add colors that we have already.
|
|
ColorPickerItem *existingItem = find(col);
|
|
ColorPickerItem *lastSelectedItem = find(lastSelected());
|
|
|
|
if (existingItem != nullptr)
|
|
{
|
|
if (lastSelectedItem && existingItem != lastSelectedItem)
|
|
{
|
|
lastSelectedItem->setSelected(false);
|
|
}
|
|
existingItem->setFocus();
|
|
existingItem->setSelected(true);
|
|
return;
|
|
}
|
|
|
|
auto *item = new ColorPickerItem(col, text, this);
|
|
|
|
if (lastSelectedItem)
|
|
{
|
|
lastSelectedItem->setSelected(false);
|
|
}
|
|
else
|
|
{
|
|
item->setSelected(true);
|
|
lastSel = col;
|
|
}
|
|
item->setFocus();
|
|
|
|
connect(item, SIGNAL(selected()), SLOT(updateSelected()));
|
|
|
|
if (index == -1)
|
|
{
|
|
index = items.count();
|
|
}
|
|
|
|
items.insert(index, item);
|
|
regenerateGrid();
|
|
|
|
update();
|
|
}
|
|
|
|
/*! \internal
|
|
|
|
*/
|
|
auto ColorPickerPopup::color(int index) const -> QColor
|
|
{
|
|
if (index < 0 || index > static_cast<int>(items.count() - 1))
|
|
{
|
|
return QColor();
|
|
}
|
|
|
|
auto *that = const_cast<ColorPickerPopup *>(this);
|
|
return that->items.at(index)->color();
|
|
}
|
|
|
|
auto ColorPickerPopup::CustomItems() const -> QVector<ColorPickerItem *>
|
|
{
|
|
QVector<ColorPickerItem *> customItems;
|
|
customItems.reserve(items.size());
|
|
|
|
for (auto *item : items)
|
|
{
|
|
if (item != nullptr && item->text() == QCoreApplication::translate("ColorPickerPopup", "Custom"))
|
|
{
|
|
customItems.append(item);
|
|
}
|
|
}
|
|
|
|
return customItems;
|
|
}
|
|
|
|
/*! \internal
|
|
|
|
*/
|
|
void ColorPickerPopup::exec()
|
|
{
|
|
show();
|
|
|
|
QEventLoop e;
|
|
eventLoop = &e;
|
|
(void)e.exec();
|
|
eventLoop = nullptr;
|
|
}
|
|
|
|
/*! \internal
|
|
|
|
*/
|
|
void ColorPickerPopup::updateSelected()
|
|
{
|
|
QLayoutItem *layoutItem;
|
|
int i = 0;
|
|
while ((layoutItem = grid->itemAt(i)) != nullptr)
|
|
{
|
|
QWidget *w = layoutItem->widget();
|
|
if (w && w->inherits("ColorPickerItem"))
|
|
{
|
|
auto *litem = reinterpret_cast<ColorPickerItem *>(layoutItem->widget());
|
|
if (litem != sender())
|
|
{
|
|
litem->setSelected(false);
|
|
}
|
|
}
|
|
++i;
|
|
}
|
|
|
|
if (sender() && sender()->inherits("ColorPickerItem"))
|
|
{
|
|
auto *item = static_cast<ColorPickerItem *>(sender());
|
|
lastSel = item->color();
|
|
emit selected(item->color());
|
|
}
|
|
|
|
hide();
|
|
}
|
|
|
|
/*! \internal
|
|
|
|
*/
|
|
void ColorPickerPopup::mouseReleaseEvent(QMouseEvent *e)
|
|
{
|
|
if (!rect().contains(e->pos()))
|
|
{
|
|
hide();
|
|
}
|
|
}
|
|
|
|
/*! \internal
|
|
|
|
Controls keyboard navigation and selection on the color grid.
|
|
*/
|
|
void ColorPickerPopup::keyPressEvent(QKeyEvent *e)
|
|
{
|
|
int curRow = 0;
|
|
int curCol = 0;
|
|
|
|
bool foundFocus = false;
|
|
for (int j = 0; !foundFocus && j < grid->rowCount(); ++j)
|
|
{
|
|
for (int i = 0; !foundFocus && i < grid->columnCount(); ++i)
|
|
{
|
|
if ((widgetAt[j][i] != nullptr) && widgetAt[j][i]->hasFocus())
|
|
{
|
|
curRow = j;
|
|
curCol = i;
|
|
foundFocus = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
switch (e->key())
|
|
{
|
|
case Qt::Key_Left:
|
|
if (curCol > 0)
|
|
{
|
|
--curCol;
|
|
}
|
|
else if (curRow > 0)
|
|
{
|
|
--curRow;
|
|
curCol = grid->columnCount() - 1;
|
|
}
|
|
break;
|
|
case Qt::Key_Right:
|
|
if (curCol < grid->columnCount() - 1 && widgetAt[curRow][curCol + 1])
|
|
{
|
|
++curCol;
|
|
}
|
|
else if (curRow < grid->rowCount() - 1)
|
|
{
|
|
++curRow;
|
|
curCol = 0;
|
|
}
|
|
break;
|
|
case Qt::Key_Up:
|
|
if (curRow > 0)
|
|
{
|
|
--curRow;
|
|
}
|
|
else
|
|
curCol = 0;
|
|
break;
|
|
case Qt::Key_Down:
|
|
if (curRow < grid->rowCount() - 1)
|
|
{
|
|
QWidget *w = widgetAt[curRow + 1][curCol];
|
|
if (w)
|
|
{
|
|
++curRow;
|
|
}
|
|
else
|
|
for (int i = 1; i < grid->columnCount(); ++i)
|
|
{
|
|
if (!widgetAt[curRow + 1][i])
|
|
{
|
|
curCol = i - 1;
|
|
++curRow;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case Qt::Key_Space:
|
|
case Qt::Key_Return:
|
|
case Qt::Key_Enter:
|
|
{
|
|
QWidget *w = widgetAt[curRow][curCol];
|
|
if ((w != nullptr) && w->inherits("ColorPickerItem"))
|
|
{
|
|
auto *wi = reinterpret_cast<ColorPickerItem *>(w);
|
|
wi->setSelected(true);
|
|
|
|
QLayoutItem *layoutItem;
|
|
int i = 0;
|
|
while ((layoutItem = grid->itemAt(i)) != nullptr)
|
|
{
|
|
QWidget *wl = layoutItem->widget();
|
|
if (wl && wl->inherits("ColorPickerItem"))
|
|
{
|
|
auto *litem = reinterpret_cast<ColorPickerItem *>(layoutItem->widget());
|
|
if (litem != wi)
|
|
{
|
|
litem->setSelected(false);
|
|
}
|
|
}
|
|
++i;
|
|
}
|
|
|
|
lastSel = wi->color();
|
|
emit selected(wi->color());
|
|
hide();
|
|
}
|
|
else if (w && w->inherits("QPushButton"))
|
|
{
|
|
auto *wi = reinterpret_cast<ColorPickerItem *>(w);
|
|
wi->setSelected(true);
|
|
|
|
QLayoutItem *layoutItem;
|
|
int i = 0;
|
|
while ((layoutItem = grid->itemAt(i)) != nullptr)
|
|
{
|
|
QWidget *wl = layoutItem->widget();
|
|
if (wl && wl->inherits("ColorPickerItem"))
|
|
{
|
|
auto *litem = reinterpret_cast<ColorPickerItem *>(layoutItem->widget());
|
|
if (litem != wi)
|
|
{
|
|
litem->setSelected(false);
|
|
}
|
|
}
|
|
++i;
|
|
}
|
|
|
|
lastSel = wi->color();
|
|
emit selected(wi->color());
|
|
hide();
|
|
}
|
|
break;
|
|
}
|
|
case Qt::Key_Escape:
|
|
hide();
|
|
break;
|
|
default:
|
|
e->ignore();
|
|
break;
|
|
}
|
|
|
|
widgetAt[curRow][curCol]->setFocus();
|
|
}
|
|
|
|
/*! \internal
|
|
|
|
*/
|
|
void ColorPickerPopup::hideEvent(QHideEvent *e)
|
|
{
|
|
if (eventLoop)
|
|
{
|
|
eventLoop->exit();
|
|
}
|
|
|
|
setFocus();
|
|
|
|
emit hid();
|
|
QFrame::hideEvent(e);
|
|
}
|
|
|
|
/*! \internal
|
|
|
|
*/
|
|
auto ColorPickerPopup::lastSelected() const -> QColor
|
|
{
|
|
return lastSel;
|
|
}
|
|
|
|
/*! \internal
|
|
|
|
Sets focus on the popup to enable keyboard navigation. Draws
|
|
focusRect and selection rect.
|
|
*/
|
|
void ColorPickerPopup::showEvent(QShowEvent *)
|
|
{
|
|
bool foundSelected = false;
|
|
for (int i = 0; i < grid->columnCount(); ++i)
|
|
{
|
|
for (int j = 0; j < grid->rowCount(); ++j)
|
|
{
|
|
QWidget *w = widgetAt[j][i];
|
|
if (w && w->inherits("ColorPickerItem"))
|
|
{
|
|
if (static_cast<ColorPickerItem *>(w)->isSelected())
|
|
{
|
|
w->setFocus();
|
|
foundSelected = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!foundSelected)
|
|
{
|
|
if (items.isEmpty())
|
|
{
|
|
setFocus();
|
|
}
|
|
else
|
|
{
|
|
widgetAt[0][0]->setFocus();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*!
|
|
|
|
*/
|
|
void ColorPickerPopup::regenerateGrid()
|
|
{
|
|
widgetAt.clear();
|
|
|
|
int columns = cols;
|
|
if (columns == -1)
|
|
{
|
|
columns = static_cast<int>(ceil(sqrt(static_cast<float>(items.count()))));
|
|
}
|
|
|
|
// When the number of columns grows, the number of rows will
|
|
// fall. There's no way to shrink a grid, so we create a new
|
|
// one.
|
|
if (grid)
|
|
delete grid;
|
|
grid = new QGridLayout(this);
|
|
grid->setContentsMargins(1, 1, 1, 1);
|
|
grid->setSpacing(0);
|
|
|
|
int ccol = 0, crow = 0;
|
|
for (int i = 0; i < items.size(); ++i)
|
|
{
|
|
if (items.at(i))
|
|
{
|
|
widgetAt[crow][ccol] = items.at(i);
|
|
grid->addWidget(items.at(i), crow, ccol++);
|
|
if (ccol == columns)
|
|
{
|
|
++crow;
|
|
ccol = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (moreButton)
|
|
{
|
|
grid->addWidget(moreButton, crow, ccol);
|
|
widgetAt[crow][ccol] = moreButton;
|
|
}
|
|
updateGeometry();
|
|
}
|
|
|
|
/*! \internal
|
|
|
|
Copies the color dialog's currently selected item and emits
|
|
itemSelected().
|
|
*/
|
|
void ColorPickerPopup::getColorFromDialog()
|
|
{
|
|
QColor const col = QColorDialog::getColor(lastSel, parentWidget());
|
|
if (!col.isValid())
|
|
{
|
|
return;
|
|
}
|
|
|
|
insertColor(col, tr("Custom"), -1);
|
|
lastSel = col;
|
|
emit selected(col);
|
|
}
|
|
|
|
/*!
|
|
Constructs a ColorPickerItem whose color is set to \a color, and
|
|
whose name is set to \a text.
|
|
*/
|
|
ColorPickerItem::ColorPickerItem(const QColor &color, const QString &text, QWidget *parent)
|
|
: QFrame(parent),
|
|
c(color),
|
|
t(text),
|
|
sel(false)
|
|
{
|
|
setToolTip(t);
|
|
setFixedWidth(24);
|
|
setFixedHeight(21);
|
|
}
|
|
|
|
/*!
|
|
Destructs a ColorPickerItem.
|
|
*/
|
|
ColorPickerItem::~ColorPickerItem()
|
|
{
|
|
}
|
|
|
|
/*!
|
|
Returns the item's color.
|
|
|
|
\sa text()
|
|
*/
|
|
auto ColorPickerItem::color() const -> QColor
|
|
{
|
|
return c;
|
|
}
|
|
|
|
/*!
|
|
Returns the item's text.
|
|
|
|
\sa color()
|
|
*/
|
|
auto ColorPickerItem::text() const -> QString
|
|
{
|
|
return t;
|
|
}
|
|
|
|
/*!
|
|
|
|
*/
|
|
auto ColorPickerItem::isSelected() const -> bool
|
|
{
|
|
return sel;
|
|
}
|
|
|
|
/*!
|
|
|
|
*/
|
|
void ColorPickerItem::setSelected(bool selected)
|
|
{
|
|
sel = selected;
|
|
update();
|
|
}
|
|
|
|
/*!
|
|
Sets the item's color to \a color, and its name to \a text.
|
|
*/
|
|
void ColorPickerItem::setColor(const QColor &color, const QString &text)
|
|
{
|
|
c = color;
|
|
t = text;
|
|
setToolTip(t);
|
|
update();
|
|
}
|
|
|
|
/*!
|
|
|
|
*/
|
|
void ColorPickerItem::mouseMoveEvent(QMouseEvent *)
|
|
{
|
|
setFocus();
|
|
update();
|
|
}
|
|
|
|
/*!
|
|
|
|
*/
|
|
void ColorPickerItem::mouseReleaseEvent(QMouseEvent *)
|
|
{
|
|
sel = true;
|
|
emit selected();
|
|
}
|
|
|
|
/*!
|
|
|
|
*/
|
|
void ColorPickerItem::mousePressEvent(QMouseEvent *)
|
|
{
|
|
setFocus();
|
|
update();
|
|
}
|
|
|
|
/*!
|
|
|
|
*/
|
|
void ColorPickerItem::paintEvent(QPaintEvent *)
|
|
{
|
|
QPainter p(this);
|
|
int const w = width(); // width of cell in pixels
|
|
int const h = height(); // height of cell in pixels
|
|
|
|
p.setPen(QPen(Qt::gray, 0, Qt::SolidLine));
|
|
|
|
if (sel)
|
|
{
|
|
p.drawRect(1, 1, w - 3, h - 3);
|
|
}
|
|
|
|
p.setPen(QPen(Qt::black, 0, Qt::SolidLine));
|
|
p.drawRect(3, 3, w - 7, h - 7);
|
|
p.fillRect(QRect(4, 4, w - 8, h - 8), QBrush(c));
|
|
|
|
if (hasFocus())
|
|
{
|
|
p.drawRect(0, 0, w - 1, h - 1);
|
|
}
|
|
}
|
|
|
|
/*!
|
|
|
|
*/
|
|
ColorPickerButton::ColorPickerButton(QWidget *parent)
|
|
: QFrame(parent)
|
|
{
|
|
setFrameStyle(StyledPanel);
|
|
}
|
|
|
|
/*!
|
|
|
|
*/
|
|
void ColorPickerButton::mousePressEvent(QMouseEvent *e)
|
|
{
|
|
Q_UNUSED(e)
|
|
setFrameShadow(Sunken);
|
|
update();
|
|
}
|
|
|
|
/*!
|
|
|
|
*/
|
|
void ColorPickerButton::mouseMoveEvent(QMouseEvent *e)
|
|
{
|
|
Q_UNUSED(e)
|
|
setFocus();
|
|
update();
|
|
}
|
|
|
|
/*!
|
|
|
|
*/
|
|
void ColorPickerButton::mouseReleaseEvent(QMouseEvent *e)
|
|
{
|
|
Q_UNUSED(e)
|
|
setFrameShadow(Raised);
|
|
repaint();
|
|
emit clicked();
|
|
}
|
|
|
|
/*!
|
|
|
|
*/
|
|
void ColorPickerButton::keyPressEvent(QKeyEvent *e)
|
|
{
|
|
if (e->key() == Qt::Key_Up || e->key() == Qt::Key_Down || e->key() == Qt::Key_Left || e->key() == Qt::Key_Right)
|
|
{
|
|
QCoreApplication::sendEvent(parent(), e);
|
|
}
|
|
else if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Space || e->key() == Qt::Key_Return)
|
|
{
|
|
setFrameShadow(Sunken);
|
|
update();
|
|
}
|
|
else
|
|
{
|
|
QFrame::keyPressEvent(e);
|
|
}
|
|
}
|
|
|
|
/*!
|
|
|
|
*/
|
|
void ColorPickerButton::keyReleaseEvent(QKeyEvent *e)
|
|
{
|
|
if (e->key() == Qt::Key_Up || e->key() == Qt::Key_Down || e->key() == Qt::Key_Left || e->key() == Qt::Key_Right)
|
|
{
|
|
QCoreApplication::sendEvent(parent(), e);
|
|
}
|
|
else if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Space || e->key() == Qt::Key_Return)
|
|
{
|
|
setFrameShadow(Raised);
|
|
repaint();
|
|
emit clicked();
|
|
}
|
|
else
|
|
{
|
|
QFrame::keyReleaseEvent(e);
|
|
}
|
|
}
|
|
|
|
/*!
|
|
|
|
*/
|
|
void ColorPickerButton::focusInEvent(QFocusEvent *e)
|
|
{
|
|
setFrameShadow(Raised);
|
|
update();
|
|
QFrame::focusInEvent(e);
|
|
}
|
|
|
|
/*!
|
|
|
|
*/
|
|
void ColorPickerButton::focusOutEvent(QFocusEvent *e)
|
|
{
|
|
setFrameShadow(Raised);
|
|
update();
|
|
QFrame::focusOutEvent(e);
|
|
}
|
|
|
|
/*!
|
|
|
|
*/
|
|
void ColorPickerButton::paintEvent(QPaintEvent *e)
|
|
{
|
|
QFrame::paintEvent(e);
|
|
|
|
QPainter p(this);
|
|
p.fillRect(contentsRect(), palette().button());
|
|
|
|
QRect const r = rect();
|
|
|
|
int const offset = frameShadow() == Sunken ? 1 : 0;
|
|
|
|
QPen const pen(palette().buttonText(), 1);
|
|
p.setPen(pen);
|
|
|
|
p.drawRect(r.center().x() + offset - 4, r.center().y() + offset, 1, 1);
|
|
p.drawRect(r.center().x() + offset, r.center().y() + offset, 1, 1);
|
|
p.drawRect(r.center().x() + offset + 4, r.center().y() + offset, 1, 1);
|
|
if (hasFocus())
|
|
{
|
|
p.setPen(QPen(Qt::black, 0, Qt::SolidLine));
|
|
p.drawRect(0, 0, width() - 1, height() - 1);
|
|
}
|
|
|
|
p.end();
|
|
}
|
|
|
|
#include "qtcolorpicker.moc"
|