From 5719956441160f71b53369100fbcda1ecad2c6f9 Mon Sep 17 00:00:00 2001 From: Adam Honse Date: Mon, 30 Mar 2020 17:54:25 -0500 Subject: [PATCH] Add color wheel color picker to UI --- OpenRGB.pro | 3 + dependencies/ColorWheel/colorwheel.cpp | 517 +++++++++++++++++++++++++ dependencies/ColorWheel/colorwheel.h | 56 +++ qt/OpenRGBDevicePage.cpp | 43 ++ qt/OpenRGBDevicePage.h | 4 + qt/OpenRGBDevicePage.ui | 401 ++++++++++--------- 6 files changed, 834 insertions(+), 190 deletions(-) create mode 100644 dependencies/ColorWheel/colorwheel.cpp create mode 100644 dependencies/ColorWheel/colorwheel.h diff --git a/OpenRGB.pro b/OpenRGB.pro index 3042a4e19..d0331ae40 100644 --- a/OpenRGB.pro +++ b/OpenRGB.pro @@ -22,6 +22,7 @@ DEFINES += \ RC_ICONS = qt/OpenRGB.ico INCLUDEPATH += \ + dependencies/ColorWheel \ dependencies/hidapi \ dependencies/libe131/src/ \ i2c_smbus/ \ @@ -56,6 +57,7 @@ INCLUDEPATH += \ qt/ SOURCES += \ + dependencies/ColorWheel/ColorWheel.cpp \ dependencies/hidapi/hidapi.c \ dependencies/libe131/src/e131.c \ main.cpp \ @@ -153,6 +155,7 @@ SOURCES += \ RGBController/RGBController_ThermaltakeRiing.cpp \ HEADERS += \ + dependencies/ColorWheel/ColorWheel.h \ ProfileManager.h \ qt/OpenRGBDeviceInfoPage.h \ qt/OpenRGBDevicePage.h \ diff --git a/dependencies/ColorWheel/colorwheel.cpp b/dependencies/ColorWheel/colorwheel.cpp new file mode 100644 index 000000000..4b24bb065 --- /dev/null +++ b/dependencies/ColorWheel/colorwheel.cpp @@ -0,0 +1,517 @@ +/*-----------------------------------------------------*\ +| colorwheel.cpp | +| | +| Color wheel selector widget for Qt | +| | +| Original: https://github.com/liuyanghejerry/Qt-Plus | +| | +| Modified by Adam Honse (calcprogrammer1@gmail.com) | +\*-----------------------------------------------------*/ + +#include "colorwheel.h" +#include +#include +#include +#include +#include + +ColorWheel::ColorWheel(QWidget *parent) : + QWidget(parent), + initSize(128,128), + mouseDown(false), + margin(0), + wheelWidth(10), + current(Qt::red), + inWheel(false), + inSquare(false) +{ + current = current.toHsv(); +} + +QColor ColorWheel::color() +{ + return current; +} + +void ColorWheel::setColor(const QColor &color) +{ + if(color == current) return; + if(color.hue() != current.hue()) + { + hueChanged(color.hue()); + } + + if((color.saturation() != current.saturation()) || (color.value() != current.value())) + { + svChanged(color); + } + + update(); + emit colorChanged(color); +} + + +QColor ColorWheel::posColor(const QPoint &point) +{ + /*-----------------------------------------------------*\ + | Subtract offsets from point value | + \*-----------------------------------------------------*/ + int point_x = point.x() - x_offset; + int point_y = point.y() - y_offset; + + /*-----------------------------------------------------*\ + | If point is not within widget, don't process | + \*-----------------------------------------------------*/ + if(!wheel.rect().contains(point)) + { + return QColor(); + } + + /*-----------------------------------------------------*\ + | If within wheel region, update hue from point | + | position | + \*-----------------------------------------------------*/ + if(inWheel) + { + qreal hue = 0; + int r = qMin(width() - x_offset, height() - y_offset) / 2; + if( point_x > r ) + { + if(point_y < r ) + { + //1 + hue = 90 - (qAtan2( (point_x - r) , (r - point_y) ) / 3.14 / 2 * 360); + } + else + { + //4 + hue = 270 + (qAtan2( (point_x - r) , (point_y - r ) ) / 3.14 / 2 * 360); + } + } + else + { + if(point_y < r ) + { + //2 + hue = 90 + (qAtan2( (r - point_x) , (r - point_y) ) / 3.14 / 2 * 360); + } + else + { + //3 + hue = 270 - (qAtan2( (r - point_x) , (point_y - r )) / 3.14 / 2 * 360); + } + } + + /*-----------------------------------------------------*\ + | Restrict hue to range 0-359 | + \*-----------------------------------------------------*/ + hue = (hue > 359) ? 359 : hue; + hue = hue < 0 ? 0 : hue; + + return QColor::fromHsv(hue, + current.saturation(), + current.value()); + } + + /*-----------------------------------------------------*\ + | If within square region, update saturation and value | + | from point position | + \*-----------------------------------------------------*/ + if(inSquare) + { + // region of the widget + int w = qMin(width() - x_offset, height() - y_offset); + + // radius of outer circle + qreal r = w/2 - margin; + + // radius of inner circle + qreal ir = r - wheelWidth; + + // left corner of square + qreal m = w/2.0 - ir/qSqrt(2); + + QPoint p = point - QPoint(x_offset, y_offset) - QPoint(m, m); + qreal SquareWidth = 2*ir/qSqrt(2); + return QColor::fromHsvF( current.hueF(), + p.x()/SquareWidth, + p.y()/SquareWidth); + } + return QColor(); +} + +QSize ColorWheel::sizeHint () const +{ + return QSize(height(),height()); +} + +QSize ColorWheel::minimumSizeHint () const +{ + return initSize; +} + +void ColorWheel::mousePressEvent(QMouseEvent *event) +{ + /*-----------------------------------------------------*\ + | Update last position | + \*-----------------------------------------------------*/ + lastPos = event->pos(); + + /*-----------------------------------------------------*\ + | If mouse is within wheel region, process wheel (hue) | + \*-----------------------------------------------------*/ + if(wheelRegion.contains(lastPos)) + { + inWheel = true; + inSquare = false; + QColor color = posColor(lastPos); + hueChanged(color.hue()); + } + + /*-----------------------------------------------------*\ + | If mouse is within square region, process square | + | (saturation and value) | + \*-----------------------------------------------------*/ + else if(squareRegion.contains(lastPos)) + { + inWheel = false; + inSquare = true; + QColor color = posColor(lastPos); + svChanged(color); + } + + /*-----------------------------------------------------*\ + | Set the mouse down flag | + \*-----------------------------------------------------*/ + mouseDown = true; +} + +void ColorWheel::mouseMoveEvent(QMouseEvent *event) +{ + /*-----------------------------------------------------*\ + | Update last position | + \*-----------------------------------------------------*/ + lastPos = event->pos(); + + /*-----------------------------------------------------*\ + | Don't process if mouse button is not down | + \*-----------------------------------------------------*/ + if(!mouseDown) + { + return; + } + + /*-----------------------------------------------------*\ + | If mouse is within wheel region, process wheel (hue) | + \*-----------------------------------------------------*/ + if(wheelRegion.contains(lastPos) && inWheel) + { + QColor color = posColor(lastPos); + hueChanged(color.hue()); + } + + /*-----------------------------------------------------*\ + | If mouse is within square region, process square | + | (saturation and value) | + \*-----------------------------------------------------*/ + else if(squareRegion.contains(lastPos) && inSquare) + { + QColor color = posColor(lastPos); + svChanged(color); + } + else + { + // TODO: due with cursor out of region after press + // int length = qMin(width(), height()); + // QPoint center(length/2, length/2); + // int R = qSqrt(qPow(qAbs(lastPos.x()), 2) + // + qPow(qAbs(lastPos.y()), 2)); + // if(inWheel){ + // int r = length / 2; + // r += qSqrt(qPow(center.x(), 2) + qPow(center.y(), 2)); + // int x0 = r/R * qAbs(lastPos.x()); + // int y0 = r/R * qAbs(lastPos.y()); + // QColor color = posColor(QPoint(x0, y0)); + // hueChanged(color.hue()); + // }else if(inSquare){ + // // + // } + } +} + +void ColorWheel::mouseReleaseEvent(QMouseEvent *) +{ + /*-----------------------------------------------------*\ + | Clear mouse down and in-region flags | + \*-----------------------------------------------------*/ + mouseDown = false; + inWheel = false; + inSquare = false; +} + +void ColorWheel::resizeEvent(QResizeEvent *event) +{ + unsigned int size = 0; + + if(event->size().width() < event->size().height()) + { + size = event->size().width(); + } + else + { + size = event->size().height(); + } + + wheelWidth = 0.1 * size; + + wheel = QPixmap(event->size()); + wheel.fill(Qt::transparent); + drawWheelImage(event->size()); + drawSquareImage(current.hue()); + update(); +} + +void ColorWheel::paintEvent(QPaintEvent *) +{ + QPainter painter(this); + QStyleOption opt; + opt.initFrom(this); + composeWheel(); + painter.drawPixmap(0,0,wheel); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this); +} + +void ColorWheel::drawWheelImage(const QSize &newSize) +{ + QStyleOption option; + option.initFrom(this); + + QBrush background = option.palette.base(); + + /*-----------------------------------------------------*\ + | Create image canvas | + \*-----------------------------------------------------*/ + wheelImage = QImage(newSize, QImage::Format_ARGB32_Premultiplied); + + /*-----------------------------------------------------*\ + | Paint the background | + \*-----------------------------------------------------*/ + wheelImage.fill(background.color()); + + /*-----------------------------------------------------*\ + | Create rainbow gradient for wheel | + \*-----------------------------------------------------*/ + QConicalGradient conicalGradient(0, 0, 0); + conicalGradient.setColorAt(0.0, Qt::red); + conicalGradient.setColorAt(60.0 / 360.0, Qt::yellow); + conicalGradient.setColorAt(120.0 / 360.0, Qt::green); + conicalGradient.setColorAt(180.0 / 360.0, Qt::cyan); + conicalGradient.setColorAt(240.0 / 360.0, Qt::blue); + conicalGradient.setColorAt(300.0 / 360.0, Qt::magenta); + conicalGradient.setColorAt(1.0, Qt::red); + + /*-----------------------------------------------------*\ + | Set up painter with antialiasing | + \*-----------------------------------------------------*/ + QPainter painter(&wheelImage); + painter.setRenderHint(QPainter::Antialiasing); + + /*-----------------------------------------------------*\ + | Paint the outer circle | + \*-----------------------------------------------------*/ + int size = qMin(newSize.width(), newSize.height()); + x_offset = (newSize.width() - size) / 2; + y_offset = (newSize.height() - size) / 2; + int r = size; + painter.translate(x_offset + (size / 2), y_offset + (size / 2)); + + QBrush brush(conicalGradient); + painter.setPen(Qt::NoPen); + painter.setBrush(brush); + painter.drawEllipse(QPoint(0,0),r/2-margin,r/2-margin); + + /*-----------------------------------------------------*\ + | Paint the inner circle | + \*-----------------------------------------------------*/ + painter.setBrush(background); + painter.drawEllipse(QPoint(0,0),r/2-margin-wheelWidth,r/2-margin-wheelWidth); + + /*-----------------------------------------------------*\ + | Calculate wheel region and subtract out the inner | + | region | + \*-----------------------------------------------------*/ + wheelRegion = QRegion(r/2, r/2, r-2*margin, r-2*margin, QRegion::Ellipse); + wheelRegion.translate(x_offset - (r-2*margin)/2, y_offset - (r-2*margin)/2); + + int tmp = 2*(margin+wheelWidth); + QRegion subRe( r/2, r/2, r-tmp, r-tmp, QRegion::Ellipse ); + subRe.translate( x_offset - (r-tmp)/2, y_offset - (r-tmp)/2); + wheelRegion -= subRe; +} + +void ColorWheel::drawSquareImage(const int &hue) +{ +// QPainter painter(&squarePixmap); +// painter.setRenderHint(QPainter::Antialiasing); + + /*-----------------------------------------------------*\ + | Calculate dimensions | + \*-----------------------------------------------------*/ + int w = qMin(width(), height()); + + // radius of outer circle + qreal r = w/2-margin; + + // radius of inner circle + qreal ir = r-wheelWidth; + + // left corner of square + qreal m = w/2.0-ir/qSqrt(2); + + /*-----------------------------------------------------*\ + | Create image canvas | + \*-----------------------------------------------------*/ + QImage square(255,255, QImage::Format_ARGB32_Premultiplied); + + /*-----------------------------------------------------*\ + | Paint the square. X axis is saturation and Y axis is | + | value | + \*-----------------------------------------------------*/ + QColor color; + QRgb qrgb; + + for(int x = 0; x < 255; x++) + { + for(int y = 0; y < 255; y++) + { + color = QColor::fromHsv(hue, x, y); + + qrgb = qRgb(color.red(),color.green(),color.blue()); + + square.setPixel(x, y, qrgb); + } + } + + /*-----------------------------------------------------*\ + | Copy the fixed-size square image on to the scaled | + | canvas | + \*-----------------------------------------------------*/ + qreal SquareWidth = 2*ir/qSqrt(2); + squareImage = square.scaled(SquareWidth, SquareWidth); + + /*-----------------------------------------------------*\ + | Calculate square region | + \*-----------------------------------------------------*/ + squareRegion = QRegion(x_offset + m, y_offset + m, SquareWidth, SquareWidth); +} + +void ColorWheel::drawIndicator(const int &hue) +{ + QPainter painter(&wheel); + painter.setRenderHint(QPainter::Antialiasing); + if(hue > 20 && hue < 200) + { + painter.setPen(Qt::black); + } + else + { + painter.setPen(Qt::white); + } + painter.setBrush(Qt::NoBrush); + + QPen pen = painter.pen(); + pen.setWidth(3); + painter.setPen(pen); + qreal r = qMin(height(), width()) / 2.0; + painter.translate(x_offset + r, y_offset + r); + painter.rotate( -hue ); + r = qMin(height(), width()) / 2.0 - margin - wheelWidth/2; + painter.drawEllipse(QPointF(r,0.0),5,5); +} + +void ColorWheel::drawPicker(const QColor &color) +{ + QPainter painter(&wheel); + painter.setRenderHint(QPainter::Antialiasing); + QPen pen; + + // region of the widget + int w = qMin(width(), height()); + + // radius of outer circle + qreal r = w/2-margin; + + // radius of inner circle + qreal ir = r-wheelWidth; + + // left corner of square + qreal m = w/2.0-ir/qSqrt(2); + + painter.translate(x_offset + m-5, y_offset + m-5); + + qreal SquareWidth = 2*ir/qSqrt(2); + qreal S = color.saturationF()*SquareWidth; + qreal V = color.valueF()*SquareWidth; + + if(color.saturation() > 30 ||color.value() < 50) + { + pen.setColor(Qt::white); + } + + pen.setWidth(3); + painter.setPen(pen); + painter.drawEllipse(S,V,10,10); +} + +void ColorWheel::composeWheel() +{ + QPainter composePainter(&wheel); + composePainter.drawImage(0, 0, wheelImage); + composePainter.drawImage(squareRegion.boundingRect().topLeft(), squareImage); + composePainter.end(); + drawIndicator(current.hue()); + drawPicker(current); +} + +void ColorWheel::hueChanged(const int &hue) +{ + if((hue < 0) || (hue > 359)) + { + return; + } + + int s = current.saturation(); + int v = current.value(); + current.setHsv(hue, s, v); + + if(!isVisible()) + { + return; + } + + drawSquareImage(hue); + repaint(); + + emit colorChanged(current); +} + +void ColorWheel::svChanged(const QColor &newcolor) +{ + int hue = current.hue(); + + current.setHsv + ( + hue, + newcolor.saturation(), + newcolor.value() + ); + + if(!isVisible()) + { + return; + } + + repaint(); + + emit colorChanged(current); +} diff --git a/dependencies/ColorWheel/colorwheel.h b/dependencies/ColorWheel/colorwheel.h new file mode 100644 index 000000000..9726400c9 --- /dev/null +++ b/dependencies/ColorWheel/colorwheel.h @@ -0,0 +1,56 @@ +#ifndef COLORWHEEL_H +#define COLORWHEEL_H + +#include + +class ColorWheel : public QWidget +{ + Q_OBJECT +public: + explicit ColorWheel(QWidget *parent = 0); + + virtual QSize sizeHint () const; + virtual QSize minimumSizeHint () const; + QColor color(); + +signals: + void colorChanged(const QColor color); + +public slots: + void setColor(const QColor &color); + +protected: + void mousePressEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *); + void resizeEvent(QResizeEvent *event); + void paintEvent(QPaintEvent *); +private: + QSize initSize; + QImage wheelImage; + QImage squareImage; + QPixmap wheel; + bool mouseDown; + QPoint lastPos; + int margin; + int wheelWidth; + QRegion wheelRegion; + QRegion squareRegion; + QColor current; + bool inWheel; + bool inSquare; + int x_offset; + int y_offset; + + QColor posColor(const QPoint &point); + void drawWheelImage(const QSize &newSize); + void drawIndicator(const int &hue); + void drawPicker(const QColor &color); + void drawSquareImage(const int &hue); + void composeWheel(); +private slots: + void hueChanged(const int &hue); + void svChanged(const QColor &newcolor); +}; + +#endif // COLORWHEEL_H diff --git a/qt/OpenRGBDevicePage.cpp b/qt/OpenRGBDevicePage.cpp index 973a92812..0b9bd6e7a 100644 --- a/qt/OpenRGBDevicePage.cpp +++ b/qt/OpenRGBDevicePage.cpp @@ -795,6 +795,7 @@ void Ui::OpenRGBDevicePage::on_ButtonRed_clicked() ui->BlueSpinBox->setValue(0); UpdatingColor = false; updateHSV(); + updateWheel(); } void Ui::OpenRGBDevicePage::on_ButtonYellow_clicked() @@ -805,6 +806,7 @@ void Ui::OpenRGBDevicePage::on_ButtonYellow_clicked() ui->BlueSpinBox->setValue(0); UpdatingColor = false; updateHSV(); + updateWheel(); } void Ui::OpenRGBDevicePage::on_ButtonGreen_clicked() @@ -815,6 +817,7 @@ void Ui::OpenRGBDevicePage::on_ButtonGreen_clicked() ui->BlueSpinBox->setValue(0); UpdatingColor = false; updateHSV(); + updateWheel(); } void Ui::OpenRGBDevicePage::on_ButtonCyan_clicked() @@ -825,6 +828,7 @@ void Ui::OpenRGBDevicePage::on_ButtonCyan_clicked() ui->BlueSpinBox->setValue(255); UpdatingColor = false; updateHSV(); + updateWheel(); } void Ui::OpenRGBDevicePage::on_ButtonBlue_clicked() @@ -835,6 +839,7 @@ void Ui::OpenRGBDevicePage::on_ButtonBlue_clicked() ui->BlueSpinBox->setValue(255); UpdatingColor = false; updateHSV(); + updateWheel(); } void Ui::OpenRGBDevicePage::on_ButtonMagenta_clicked() @@ -845,6 +850,18 @@ void Ui::OpenRGBDevicePage::on_ButtonMagenta_clicked() ui->BlueSpinBox->setValue(255); UpdatingColor = false; updateHSV(); + updateWheel(); +} + +void Ui::OpenRGBDevicePage::on_ColorWheelBox_colorChanged(const QColor color) +{ + UpdatingColor = true; + ui->RedSpinBox->setValue(color.red()); + ui->GreenSpinBox->setValue(color.green()); + ui->BlueSpinBox->setValue(color.blue()); + UpdatingColor = false; + + updateHSV(); } void Ui::OpenRGBDevicePage::updateRGB() @@ -892,34 +909,60 @@ void Ui::OpenRGBDevicePage::updateHSV() UpdatingColor = false; } +void Ui::OpenRGBDevicePage::updateWheel() +{ + if(UpdatingColor) + { + return; + } + + UpdatingColor = true; + + RGBColor qrgb = ToRGBColor + ( + ui->BlueSpinBox->value(), + ui->GreenSpinBox->value(), + ui->RedSpinBox->value()); + + ui->ColorWheelBox->setColor(QColor::fromRgb(qrgb)); + + UpdatingColor = false; +} + void Ui::OpenRGBDevicePage::on_RedSpinBox_valueChanged(int /*arg1*/) { updateHSV(); + updateWheel(); } void Ui::OpenRGBDevicePage::on_HueSpinBox_valueChanged(int /*arg1*/) { updateRGB(); + updateWheel(); } void Ui::OpenRGBDevicePage::on_GreenSpinBox_valueChanged(int /*arg1*/) { updateHSV(); + updateWheel(); } void Ui::OpenRGBDevicePage::on_SatSpinBox_valueChanged(int /*arg1*/) { updateRGB(); + updateWheel(); } void Ui::OpenRGBDevicePage::on_BlueSpinBox_valueChanged(int /*arg1*/) { updateHSV(); + updateWheel(); } void Ui::OpenRGBDevicePage::on_ValSpinBox_valueChanged(int /*arg1*/) { updateRGB(); + updateWheel(); } void Ui::OpenRGBDevicePage::on_SetAllButton_clicked() diff --git a/qt/OpenRGBDevicePage.h b/qt/OpenRGBDevicePage.h index b0b55442a..baca54e3f 100644 --- a/qt/OpenRGBDevicePage.h +++ b/qt/OpenRGBDevicePage.h @@ -58,6 +58,8 @@ private slots: void on_ResizeButton_clicked(); + void on_ColorWheelBox_colorChanged(const QColor color); + private: Ui::OpenRGBDevicePageUi *ui; RGBController *device; @@ -69,6 +71,8 @@ private: void updateHSV(); + void updateWheel(); + signals: void SetAllDevices(unsigned char red, unsigned char green, unsigned char blue); void SaveSizeProfile(); diff --git a/qt/OpenRGBDevicePage.ui b/qt/OpenRGBDevicePage.ui index 86064ffc8..7723cafc4 100644 --- a/qt/OpenRGBDevicePage.ui +++ b/qt/OpenRGBDevicePage.ui @@ -14,51 +14,34 @@ Frame - - + + - Device + B: - - + + + + Qt::Horizontal + + + + + + + Random + + + + + - - - - - - - - - - - Per-LED - - - - - - - 255 - - - - - - - - - - S: - - - @@ -69,180 +52,34 @@ - + - - - - B: - - - - - - - Speed: - - - - - - - Set All Devices - - - - - - - V: - - - - - - - Dir: - - - - - - - - - - R: - - - - - - - LED - - - - - - - G: - - - - - - - - - - - - - - 255 - - - - - - - Qt::Horizontal - - - - - - - H: - - - - - - - - - - - - - - - - - - - - - Set: - - - - - - - LED: - - - - - - - 359 - - - - - - - 255 - - - - - - - Random - - - - - - - 255 - - - - + 255 - - + + - Zone: + S: - - + + - Mode: + R: - - - @@ -250,6 +87,41 @@ + + + + + + + + + + + 255 + + + + + + + Per-LED + + + + + + + 255 + + + + + + + + + + @@ -257,6 +129,51 @@ + + + + LED + + + + + + + Set All Devices + + + + + + + G: + + + + + + + V: + + + + + + + + + + + + + + + + + Dir: + + + @@ -264,8 +181,112 @@ + + + + + + + + + + + 255 + + + + + + + LED: + + + + + + + 359 + + + + + + + Mode: + + + + + + + Set: + + + + + + + Zone: + + + + + + + Speed: + + + + + + + 255 + + + + + + + + + + + + + Device + + + + + + + H: + + + + + + + + 0 + 0 + + + + + + + ColorWheel + QWidget +
ColorWheel.h
+ 1 + + colorChanged(QColor) + +
+