colordialog/picker/wheel: finished color picker UI

Dialog is still unmanaged, but that's the only remaining step.
This commit is contained in:
itsmattkc
2020-04-02 19:26:30 +11:00
parent a2ed1035da
commit 4d0b8f18e2
13 changed files with 366 additions and 236 deletions
+12 -1
View File
@@ -8,10 +8,21 @@ ColorButton::ColorButton(QWidget *parent) :
color_ = Color(1.0f, 1.0f, 1.0f);
connect(this, &ColorButton::clicked, this, &ColorButton::ShowColorDialog);
UpdateColor();
}
void ColorButton::ShowColorDialog()
{
ColorDialog cd(nullptr, color_, this);
cd.exec();
if (cd.exec() == QDialog::Accepted) {
color_ = cd.GetSelectedColor();
UpdateColor();
}
}
void ColorButton::UpdateColor()
{
setStyleSheet(QStringLiteral("ColorButton {background: %1;}").arg(color_.toQColor().name()));
}
+2
View File
@@ -18,6 +18,8 @@ private slots:
void ShowColorDialog();
private:
void UpdateColor();
Color color_;
};
+56 -31
View File
@@ -1,57 +1,82 @@
#include "colorgradientwidget.h"
#include <QPainter>
#include "common/clamp.h"
#include "common/lerp.h"
#include "node/node.h"
ColorGradientGLWidget::ColorGradientGLWidget(Qt::Orientation orientation, QWidget *parent) :
ColorGradientWidget::ColorGradientWidget(Qt::Orientation orientation, QWidget *parent) :
ColorSwatchWidget(parent),
orientation_(orientation)
orientation_(orientation),
val_(1.0)
{
}
void ColorGradientGLWidget::SetColors(const Color &a, const Color &b)
Color ColorGradientWidget::GetColorFromScreenPos(const QPoint &p) const
{
a_ = a;
b_ = b;
update();
if (orientation_ == Qt::Horizontal) {
return LerpColor(start_, end_, p.x(), width());
} else {
return LerpColor(start_, end_, p.y(), height());
}
}
Color ColorGradientGLWidget::GetColorFromCursorPos(const QPoint &p) const
void ColorGradientWidget::paintEvent(QPaintEvent *e)
{
float t;
QWidget::paintEvent(e);
QPainter p(this);
int min;
int max;
if (orientation_ == Qt::Horizontal) {
t = static_cast<float>(p.x()) / static_cast<float>(width());
min = height();
max = width();
} else {
t = static_cast<float>(p.y()) / static_cast<float>(height());
min = width();
max = height();
}
return Color(lerp(a_.red(), b_.red(), t),
lerp(a_.green(), b_.green(), t),
lerp(a_.blue(), b_.blue(), t));
for (int i=0;i<max;i++) {
Color c = LerpColor(start_, end_, i, max);
p.setPen(c.toQColor());
if (orientation_ == Qt::Horizontal) {
p.drawLine(i, 0, i, height());
} else {
p.drawLine(0, i, width(), i);
}
}
// Draw selector
int selector_radius = qMax(2, min / 8);
p.setPen(QPen(GetUISelectorColor(), qMax(1, selector_radius / 2)));
p.setBrush(Qt::NoBrush);
if (orientation_ == Qt::Horizontal) {
p.drawRect(qRound(width() * (1.0 - val_)) - selector_radius, 0, selector_radius * 2, height());
} else {
p.drawRect(0, qRound(height() * (1.0 - val_)) - selector_radius, width(), selector_radius * 2);
}
}
bool ColorGradientGLWidget::PointIsValid(const QPoint &) const
void ColorGradientWidget::SelectedColorChangedEvent(const Color &c)
{
return true;
float hue, sat;
c.toHsv(&hue, &sat, &val_);
start_ = Color::fromHsv(hue, sat, 1.0);
end_ = Color::fromHsv(hue, sat, 0.0);
}
OpenGLShader *ColorGradientGLWidget::CreateShader() const
Color ColorGradientWidget::LerpColor(const Color &a, const Color &b, int i, int max)
{
OpenGLShader* s = new OpenGLShader();
float t = clamp(static_cast<float>(i) / static_cast<float>(max), 0.0f, 1.0f);
s->create();
s->addShaderFromSourceCode(QOpenGLShader::Vertex, OpenGLShader::CodeDefaultVertex());
s->addShaderFromSourceCode(QOpenGLShader::Fragment, Node::ReadFileAsString(QStringLiteral(":/shaders/colorgradient.frag")));
s->link();
return s;
}
void ColorGradientGLWidget::SetShaderUniformValues(OpenGLShader *shader) const
{
shader->setUniformValue("orientation", orientation_);
shader->setUniformValue("color_a", a_.red(), a_.green(), a_.blue());
shader->setUniformValue("color_b", b_.red(), b_.green(), b_.blue());
return Color(lerp(a.red(), b.red(), t),
lerp(a.green(), b.green(), t),
lerp(a.blue(), b.blue(), t));
}
+11 -11
View File
@@ -4,29 +4,29 @@
#include "colorswatchwidget.h"
#include "render/color.h"
class ColorGradientGLWidget : public ColorSwatchWidget
class ColorGradientWidget : public ColorSwatchWidget
{
Q_OBJECT
public:
ColorGradientGLWidget(Qt::Orientation orientation, QWidget* parent = nullptr);
void SetColors(const Color& a, const Color& b);
ColorGradientWidget(Qt::Orientation orientation, QWidget* parent = nullptr);
protected:
virtual Color GetColorFromCursorPos(const QPoint& p) const override;
virtual Color GetColorFromScreenPos(const QPoint& p) const override;
virtual bool PointIsValid(const QPoint& p) const override;
virtual void paintEvent(QPaintEvent* e) override;
virtual OpenGLShader* CreateShader() const override;
virtual void SetShaderUniformValues(OpenGLShader* shader) const override;
virtual void SelectedColorChangedEvent(const Color& c) override;
private:
static Color LerpColor(const Color& a, const Color& b, int i, int max);
Qt::Orientation orientation_;
Color a_;
Color start_;
Color b_;
Color end_;
float val_;
};
+29 -35
View File
@@ -5,57 +5,51 @@
#include "render/backend/opengl/openglrenderfunctions.h"
ColorSwatchWidget::ColorSwatchWidget(QWidget *parent) :
QOpenGLWidget(parent),
shader_(nullptr)
QWidget(parent)
{
// Render with transparent background
setAttribute(Qt::WA_AlwaysStackOnTop);
}
ColorSwatchWidget::~ColorSwatchWidget()
const Color &ColorSwatchWidget::GetSelectedColor() const
{
CleanUp();
return selected_color_;
}
void ColorSwatchWidget::initializeGL()
void ColorSwatchWidget::SetSelectedColor(const Color &c)
{
CleanUp();
shader_ = CreateShader();
connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &ColorSwatchWidget::CleanUp);
}
void ColorSwatchWidget::paintGL()
{
shader_->bind();
shader_->setUniformValue("ove_resolution", width(), height());
SetShaderUniformValues(shader_);
shader_->release();
OpenGLRenderFunctions::Blit(shader_);
}
void ColorSwatchWidget::CleanUp()
{
delete shader_;
shader_ = nullptr;
selected_color_ = c;
SelectedColorChangedEvent(c);
update();
}
void ColorSwatchWidget::mousePressEvent(QMouseEvent *e)
{
QOpenGLWidget::mousePressEvent(e);
QWidget::mousePressEvent(e);
if (PointIsValid(e->pos())) {
emit ColorChanged(GetColorFromCursorPos(e->pos()));
}
SetSelectedColor(GetColorFromScreenPos(e->pos()));
emit SelectedColorChanged(GetSelectedColor());
}
void ColorSwatchWidget::mouseMoveEvent(QMouseEvent *e)
{
QOpenGLWidget::mouseMoveEvent(e);
QWidget::mouseMoveEvent(e);
if (e->buttons() & Qt::LeftButton && PointIsValid(e->pos())) {
emit ColorChanged(GetColorFromCursorPos(e->pos()));
if (e->buttons() & Qt::LeftButton) {
SetSelectedColor(GetColorFromScreenPos(e->pos()));
emit SelectedColorChanged(GetSelectedColor());
}
}
void ColorSwatchWidget::SelectedColorChangedEvent(const Color &)
{
}
Qt::GlobalColor ColorSwatchWidget::GetUISelectorColor() const
{
float rough_color_luma = (GetSelectedColor().red()+GetSelectedColor().red()+GetSelectedColor().blue()+GetSelectedColor().green()+GetSelectedColor().green()+GetSelectedColor().green())/6;
if (rough_color_luma > 0.66) {
return Qt::black;
} else {
return Qt::white;
}
}
+10 -16
View File
@@ -6,39 +6,33 @@
#include "render/backend/opengl/openglshader.h"
#include "render/color.h"
class ColorSwatchWidget : public QOpenGLWidget
class ColorSwatchWidget : public QWidget
{
Q_OBJECT
public:
ColorSwatchWidget(QWidget* parent = nullptr);
virtual ~ColorSwatchWidget() override;
const Color& GetSelectedColor() const;
public slots:
void SetSelectedColor(const Color& c);
signals:
void ColorChanged(const Color& c);
void SelectedColorChanged(const Color& c);
protected:
virtual void initializeGL() override;
virtual void paintGL() override;
virtual void mousePressEvent(QMouseEvent* e) override;
virtual void mouseMoveEvent(QMouseEvent* e) override;
virtual void SetShaderUniformValues(OpenGLShader*) const {}
virtual Color GetColorFromScreenPos(const QPoint& p) const = 0;
virtual Color GetColorFromCursorPos(const QPoint& p) const = 0;
virtual void SelectedColorChangedEvent(const Color& c);
virtual bool PointIsValid(const QPoint& p) const = 0;
virtual OpenGLShader* CreateShader() const = 0;
private slots:
void CleanUp();
Qt::GlobalColor GetUISelectorColor() const;
private:
OpenGLShader* shader_;
Color selected_color_;
};
+115 -75
View File
@@ -1,112 +1,152 @@
#include "colorwheelwidget.h"
#include <QPainter>
#include <QtMath>
#include "node/node.h"
#define M_180_OVER_PI 57.295791433133264917914229473464
#define M_RADIAN_TO_0_1 0.15915497620314795810531730409296
ColorWheelGLWidget::ColorWheelGLWidget(QWidget *parent) :
ColorWheelWidget::ColorWheelWidget(QWidget *parent) :
ColorSwatchWidget(parent),
hsv_value_(1.0f)
val_(1.0f),
force_redraw_(false)
{
}
void ColorWheelGLWidget::SetHSVValue(float val)
Color ColorWheelWidget::GetColorFromScreenPos(const QPoint &p) const
{
hsv_value_ = val;
update();
return GetColorFromTriangle(GetTriangleFromCoords(rect().center(), p));
}
Color ColorWheelGLWidget::GetColorFromCursorPos(const QPoint &p) const
{
QPoint center = rect().center();
qreal opposite = p.y() - center.y();
qreal adjacent = p.x() - center.x();
qreal distance = qSqrt(qPow(adjacent, 2) + qPow(opposite, 2));
qreal hue = qAtan2(opposite, adjacent) * M_180_OVER_PI + 180.0;
qreal sat = (distance / GetRadius());
qreal value = hsv_value_;
return Color::fromHsv(hue, sat, value);
}
bool ColorWheelGLWidget::PointIsValid(const QPoint &p) const
{
QPoint center = rect().center();
qreal distance = qSqrt(qPow(p.x() - center.x(), 2) + qPow(p.y() - center.y(), 2));
return (distance <= GetRadius());
}
OpenGLShader *ColorWheelGLWidget::CreateShader() const
{
OpenGLShader* s = new OpenGLShader();
s->create();
s->addShaderFromSourceCode(QOpenGLShader::Vertex, OpenGLShader::CodeDefaultVertex());
s->addShaderFromSourceCode(QOpenGLShader::Fragment, Node::ReadFileAsString(QStringLiteral(":/shaders/colorwheel.frag")));
s->link();
return s;
}
void ColorWheelGLWidget::SetShaderUniformValues(OpenGLShader *shader) const
{
shader->setUniformValue("hsv_value", hsv_value_);
}
void ColorWheelGLWidget::resizeEvent(QResizeEvent *e)
void ColorWheelWidget::resizeEvent(QResizeEvent *e)
{
ColorSwatchWidget::resizeEvent(e);
emit DiameterChanged(GetDiameter());
}
int ColorWheelGLWidget::GetDiameter() const
void ColorWheelWidget::paintEvent(QPaintEvent *e)
{
ColorSwatchWidget::paintEvent(e);
int diameter = GetDiameter();
// Half diameter (add one to ensure the division rounds up)
int radius = (diameter + 1) / 2;
if (cached_wheel_.width() != diameter || force_redraw_) {
cached_wheel_ = QPixmap(QSize(diameter, diameter));
cached_wheel_.fill(Qt::transparent);
force_redraw_ = false;
QPainter p(&cached_wheel_);
QPoint center(radius, radius);
for (int i=0;i<diameter;i++) {
for (int j=0;j<diameter;j++) {
Triangle tri = GetTriangleFromCoords(center, j, i);
if (tri.hypotenuse <= radius) {
QColor c = GetColorFromTriangle(tri).toQColor();
// Very basic antialiasing around the edges of the wheel
qreal alpha = qMin(1.0, radius - tri.hypotenuse);
c.setAlphaF(alpha);
p.setPen(c);
p.drawPoint(i, j);
}
}
}
}
QPainter p(this);
// Draw wheel pixmap
int x, y;
if (width() == height()) {
x = 0;
y = 0;
} else if (width() > height()) {
x = (width() - height()) / 2;
y = 0;
} else {
x = 0;
y = (height() - width()) / 2;
}
p.drawPixmap(x, y, cached_wheel_);
// Draw selection
// Really rough algorithm for determining whether the selector UI should be white or black
int selector_radius = qMax(1, radius / 32);
p.setPen(QPen(GetUISelectorColor(), qMax(1, selector_radius / 4)));
p.setBrush(Qt::NoBrush);
p.drawEllipse(GetCoordsFromColor(GetSelectedColor()), selector_radius, selector_radius);
}
void ColorWheelWidget::SelectedColorChangedEvent(const Color &c)
{
force_redraw_ = true;
val_ = c.value();
}
int ColorWheelWidget::GetDiameter() const
{
return qMin(width(), height());
}
qreal ColorWheelGLWidget::GetRadius() const
qreal ColorWheelWidget::GetRadius() const
{
return GetDiameter() * 0.5;
}
/* Software/QPainter-based color wheel
void ColorWheelWidget::paintEvent(QPaintEvent *event)
ColorWheelWidget::Triangle ColorWheelWidget::GetTriangleFromCoords(const QPoint &center, const QPoint &p) const
{
QWidget::paintEvent(event);
return GetTriangleFromCoords(center, p.y(), p.x());
}
QPainter p(this);
ColorWheelWidget::Triangle ColorWheelWidget::GetTriangleFromCoords(const QPoint &center, qreal y, qreal x) const
{
qreal opposite = y - center.y();
qreal adjacent = x - center.x();
qreal hypotenuse = qSqrt(qPow(adjacent, 2) + qPow(opposite, 2));
qreal radius = qMin(width(), height()) / 2;
QPoint center = rect().center();
return {opposite, adjacent, hypotenuse};
}
int x_start = center.x() - radius;
int x_end = center.x() + radius;
int y_start = center.y() - radius;
int y_end = center.y() + radius;
Color ColorWheelWidget::GetColorFromTriangle(const ColorWheelWidget::Triangle &tri) const
{
qreal hue = qAtan2(tri.opposite, tri.adjacent) * M_180_OVER_PI + 180.0;
qreal sat = qMin(1.0, (tri.hypotenuse / GetRadius()));
for (int i=x_start;i<x_end;i++) {
for (int j=y_start;j<y_end;j++) {
qreal opposite = j - center.y();
qreal adjacent = i - center.x();
qreal pix_len = qSqrt(qPow(adjacent, 2) + qPow(opposite, 2));
return Color::fromHsv(hue, sat, val_);
}
if (pix_len <= radius) {
qreal hue = qAtan2(opposite, adjacent) * RADIAN_TO_0_1_RANGE + 0.5;
qreal sat = (pix_len / radius);
qreal value = 1.0;
QPoint ColorWheelWidget::GetCoordsFromColor(const Color &c) const
{
float hue, sat, val;
c.toHsv(&hue, &sat, &val);
QColor c;
c.setHsvF(hue, sat, value);
p.setPen(c);
qreal hypotenuse = sat * GetRadius();
p.drawPoint(i, j);
}
}
}
}*/
qreal radian_angle = (hue - 180.0) / M_180_OVER_PI;
qreal opposite = qSin(radian_angle) * hypotenuse;
qreal adjacent = qCos(radian_angle) * hypotenuse;
QPoint pos(qRound(adjacent), qRound(opposite));
pos += rect().center();
return pos;
}
+24 -22
View File
@@ -7,45 +7,47 @@
#include "render/backend/opengl/openglshader.h"
#include "render/color.h"
class ColorWheelGLWidget : public ColorSwatchWidget
class ColorWheelWidget : public ColorSwatchWidget
{
Q_OBJECT
public:
ColorWheelGLWidget(QWidget* parent = nullptr);
void SetHSVValue(float val);
ColorWheelWidget(QWidget* parent = nullptr);
signals:
void DiameterChanged(int radius);
protected:
virtual Color GetColorFromCursorPos(const QPoint& p) const override;
virtual bool PointIsValid(const QPoint& p) const override;
virtual OpenGLShader* CreateShader() const override;
virtual void SetShaderUniformValues(OpenGLShader* shader) const override;
virtual Color GetColorFromScreenPos(const QPoint& p) const override;
virtual void resizeEvent(QResizeEvent* e) override;
virtual void paintEvent(QPaintEvent* e) override;
virtual void SelectedColorChangedEvent(const Color& c) override;
private:
int GetDiameter() const;
qreal GetRadius() const;
float hsv_value_;
struct Triangle {
qreal opposite;
qreal adjacent;
qreal hypotenuse;
};
Triangle GetTriangleFromCoords(const QPoint &center, const QPoint& p) const;
Triangle GetTriangleFromCoords(const QPoint &center, qreal y, qreal x) const;
Color GetColorFromTriangle(const Triangle& tri) const;
QPoint GetCoordsFromColor(const Color& c) const;
QPixmap cached_wheel_;
float val_;
bool force_redraw_;
};
/*class ColorWheelWidget : public QWidget
{
public:
ColorWheelWidget(QWidget* parent = nullptr);
protected:
virtual void paintEvent(QPaintEvent* e) override;
};*/
#endif // COLORWHEELWIDGET_H