Commit 31931e3a authored by Wolthera van Hövell's avatar Wolthera van Hövell 🎄

Add visual selector shapes.

These now work fully for the channels. They give nice squares.
I made a base class that only deals with the events and the classes, and added a rectangular subclass. This way we can make our circles and wheels and whatever easily.

Ref T2337
Ref T2438
parent e3f78b79
......@@ -256,6 +256,7 @@ set(kritaui_LIB_SRCS
widgets/kis_elided_label.cpp
widgets/kis_spinbox_color_selector.cpp
widgets/kis_screen_color_picker.cpp
widgets/kis_visual_color_selector.cpp
widgets/KoDualColorButton.cpp
input/kis_input_manager.cpp
input/kis_input_manager_p.cpp
......
......@@ -60,8 +60,10 @@ KisInternalColorSelector::KisInternalColorSelector(QWidget *parent, KoColor colo
m_ui->spinboxselector->slotSetColor(color);
connect(m_ui->spinboxselector, SIGNAL(sigNewColor(KoColor)), this, SLOT(slotColorUpdated(KoColor)));
m_ui->visualSelector->slotSetColor(color);
connect(m_ui->visualSelector, SIGNAL(sigNewColor(KoColor)), this, SLOT(slotColorUpdated(KoColor)));
connect(m_ui->screenColorPicker, SIGNAL(sigNewColorPicked(KoColor)),this, SLOT(slotColorUpdated(KoColor)));
//TODO: Add disable signal as well.
//TODO: Add disable signal as well. Might be not necessary...?
connect(this, SIGNAL(signalForegroundColorChosen(KoColor)), this, SLOT(slotLockSelector()));
m_d->compressColorChanges = new KisSignalCompressor(100 /* ms */, KisSignalCompressor::POSTPONE, this);
......@@ -133,6 +135,9 @@ void KisInternalColorSelector::updateAllElements(QObject *source)
if (source != m_ui->spinboxselector) {
m_ui->spinboxselector->slotSetColor(m_d->currentColor);
}
if (source != m_ui->visualSelector) {
m_ui->visualSelector->slotSetColor(m_d->currentColor);
}
if (source != this->parent()) {
emit(signalForegroundColorChosen(m_d->currentColor));
......
......@@ -14,6 +14,22 @@
<string>Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="KisVisualColorSelector" name="visualSelector" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>400</width>
<height>400</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="KisSpinboxColorSelector" name="spinboxselector" native="true">
<property name="sizePolicy">
......@@ -52,6 +68,12 @@
<header>kis_screen_color_picker.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>KisVisualColorSelector</class>
<extends>QWidget</extends>
<header>kis_visual_color_selector.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections>
......
/*
* Copyright (C) Wolthera van Hovell tot Westerflier <griffinvalley@gmail.com>, (C) 2016
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kis_visual_color_selector.h"
#include <QColor>
#include <QPixmap>
#include <QPainter>
#include <QPainterPath>
#include <QVector>
#include <QVBoxLayout>
#include <QList>
#include "KoColorConversions.h"
struct KisVisualColorSelector::Private
{
KoColor currentcolor;
const KoColorSpace *currentCS;
QList <KisVisualColorSelectorShape*> widgetlist;
//Current coordinates.
QVector <float> currentCoordinates;
};
KisVisualColorSelector::KisVisualColorSelector(QWidget *parent) : QWidget(parent), m_d(new Private)
{
QVBoxLayout *layout = new QVBoxLayout;
this->setLayout(layout);
}
KisVisualColorSelector::~KisVisualColorSelector()
{
}
void KisVisualColorSelector::slotSetColor(KoColor c)
{
m_d->currentcolor = c;
if (m_d->currentCS != c.colorSpace()) {
slotsetColorSpace(c.colorSpace());
}
updateSelectorElements();
}
void KisVisualColorSelector::slotsetColorSpace(const KoColorSpace *cs)
{
if (m_d->currentCS != cs)
{
m_d->currentCS = cs;
if (this->layout()) {
qDeleteAll(this->children());
}
m_d->widgetlist.clear();
QHBoxLayout *layout = new QHBoxLayout;
//redraw all the widgets.
if (m_d->currentCS->colorChannelCount() == 1) {
KisVisualRectangleSelectorShape *bar = new KisVisualRectangleSelectorShape(this, KisVisualRectangleSelectorShape::onedimensional,KisVisualColorSelectorShape::Channel, cs, 0, 0);
bar->setMaximumWidth(width()*0.1);
bar->setMaximumHeight(height());
connect (bar, SIGNAL(sigNewColor(KoColor)), this, SLOT(updateFromWidgets(KoColor)));
layout->addWidget(bar);
m_d->widgetlist.append(bar);
} else if (m_d->currentCS->colorChannelCount() == 3) {
KisVisualRectangleSelectorShape *bar = new KisVisualRectangleSelectorShape(this, KisVisualRectangleSelectorShape::onedimensional,KisVisualColorSelectorShape::Channel, cs, 0, 0);
KisVisualRectangleSelectorShape *block = new KisVisualRectangleSelectorShape(this, KisVisualRectangleSelectorShape::twodimensional,KisVisualColorSelectorShape::Channel, cs, 1, 2);
bar->setMaximumWidth(width()*0.1);
bar->setMaximumHeight(height());
block->setMaximumWidth(width()*0.9);
block->setMaximumHeight(height());
connect (bar, SIGNAL(sigNewColor(KoColor)), block, SLOT(setColor(KoColor)));
connect (block, SIGNAL(sigNewColor(KoColor)), SLOT(updateFromWidgets(KoColor)));
layout->addWidget(bar);
layout->addWidget(block);
m_d->widgetlist.append(bar);
m_d->widgetlist.append(block);
} else if (m_d->currentCS->colorChannelCount() == 4) {
KisVisualRectangleSelectorShape *block = new KisVisualRectangleSelectorShape(this, KisVisualRectangleSelectorShape::twodimensional,KisVisualColorSelectorShape::Channel, cs, 0, 1);
KisVisualRectangleSelectorShape *block2 = new KisVisualRectangleSelectorShape(this, KisVisualRectangleSelectorShape::twodimensional,KisVisualColorSelectorShape::Channel, cs, 2, 3);
block->setMaximumWidth(width()*0.5);
block->setMaximumHeight(height());
block2->setMaximumWidth(width()*0.5);
block2->setMaximumHeight(height());
connect (block, SIGNAL(sigNewColor(KoColor)), block2, SLOT(setColor(KoColor)));
connect (block2, SIGNAL(sigNewColor(KoColor)), SLOT(updateFromWidgets(KoColor)));
layout->addWidget(block);
layout->addWidget(block2);
m_d->widgetlist.append(block);
m_d->widgetlist.append(block2);
}
this->setLayout(layout);
}
}
void KisVisualColorSelector::updateSelectorElements()
{
qDebug()<<"Sending updates: "<<m_d->currentcolor.toQString(m_d->currentcolor);
//first lock all elements from sending updates, then update all elements.
Q_FOREACH (KisVisualColorSelectorShape *shape, m_d->widgetlist) {
shape->blockSignals(true);
}
Q_FOREACH (KisVisualColorSelectorShape *shape, m_d->widgetlist) {
shape->setColor(m_d->currentcolor);
}
Q_FOREACH (KisVisualColorSelectorShape *shape, m_d->widgetlist) {
shape->blockSignals(false);
}
}
void KisVisualColorSelector::updateFromWidgets(KoColor c)
{
m_d->currentcolor = c;
Q_EMIT sigNewColor(c);
}
/*------------Selector shape------------*/
struct KisVisualColorSelectorShape::Private
{
QPixmap gradient;
QPixmap fullSelector;
bool pixmapsNeedUpdate = true;
QPointF currentCoordinates;
Dimensions dimension;
ColorModel model;
const KoColorSpace *cs;
KoColor currentColor;
int channel1;
int channel2;
};
KisVisualColorSelectorShape::KisVisualColorSelectorShape(QWidget *parent,
KisVisualColorSelectorShape::Dimensions dimension,
KisVisualColorSelectorShape::ColorModel model,
const KoColorSpace *cs,
int channel1,
int channel2): QWidget(parent), m_d(new Private)
{
m_d->dimension = dimension;
m_d->model = model;
m_d->cs = cs;
m_d->currentColor = KoColor();
m_d->currentColor.setOpacity(1.0);
m_d->currentColor.convertTo(cs);
int maxchannel = m_d->cs->colorChannelCount()-1;
m_d->channel1 = qBound(0, channel1, maxchannel);
m_d->channel2 = qBound(0, channel2, maxchannel);
this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
//m_d->gradient = QPixmap(size());
//m_d->pixmapsNeedUpdate = true;
//getPixmap();
}
KisVisualColorSelectorShape::~KisVisualColorSelectorShape()
{
}
QPointF KisVisualColorSelectorShape::getShapeCoordinates() {
QPointF point1 = convertKoColorToShapeCoordinate(m_d->currentColor);
if (point1 != m_d->currentCoordinates) {
m_d->currentCoordinates = point1;
}
return point1;
}
void KisVisualColorSelectorShape::setColor(KoColor c)
{
if (c.colorSpace() != m_d->cs) {
c.convertTo(m_d->cs);
}
m_d->currentColor = c;
m_d->pixmapsNeedUpdate = true;
update();
}
void KisVisualColorSelectorShape::slotSetActiveChannels(int channel1, int channel2)
{
int maxchannel = m_d->cs->colorChannelCount()-1;
m_d->channel1 = qBound(0, channel1, maxchannel);
m_d->channel2 = qBound(0, channel2, maxchannel);
m_d->pixmapsNeedUpdate = true;
update();
}
QPixmap KisVisualColorSelectorShape::getPixmap()
{
if (m_d->pixmapsNeedUpdate == true) {
m_d->pixmapsNeedUpdate = false;
m_d->gradient = QPixmap(width(), height());
m_d->gradient.fill(Qt::black);
QImage img(width(), height(), QImage::Format_RGB32);
img.fill(Qt::black);
for (int y = 0; y<img.height(); y++) {
for (int x=0; x<img.width(); x++) {
QPoint widgetPoint(x,y);
QPointF newcoordinate = convertWidgetCoordinateToShapeCoordinate(widgetPoint);
KoColor c = convertShapeCoordinateToKoColor(newcoordinate);
//put displayconverter here
QColor col = c.toQColor();
img.setPixel(widgetPoint, col.rgb());
}
}
m_d->gradient = QPixmap::fromImage(img, Qt::AvoidDither);
}
return m_d->gradient;
}
KoColor KisVisualColorSelectorShape::convertShapeCoordinateToKoColor(QPointF coordinates)
{
KoColor c = m_d->currentColor;
QVector <float> channelValues (c.colorSpace()->channelCount());
channelValues.fill(1.0);
c.colorSpace()->normalisedChannelsValue(c.data(), channelValues);
if (m_d->model == ColorModel::Channel) {
channelValues[m_d->channel1] = coordinates.x();
if (m_d->dimension == Dimensions::twodimensional) {
channelValues[m_d->channel2] = coordinates.y();
}
} else {
if (c.colorSpace()->colorModelId().id() == "RGBA") {
QVector <float> inbetween(3);
if (m_d->model == ColorModel::HSV){
RGBToHSV(channelValues[0],channelValues[1], channelValues[2], &inbetween[0], &inbetween[1], &inbetween[2]);
inbetween[m_d->channel1] = coordinates.x();
if (m_d->dimension == Dimensions::twodimensional) {
inbetween[m_d->channel2] = coordinates.y();
}
HSVToRGB(inbetween[0], inbetween[1], inbetween[2], &channelValues[0], &channelValues[1], &channelValues[2]);
} else /*(m_d->model == KisVisualColorSelectorShape::ColorModel::HSL)*/{
RGBToHSL(channelValues[0],channelValues[1], channelValues[2], &inbetween[0], &inbetween[1], &inbetween[2]);
inbetween[m_d->channel1] = coordinates.x();
if (m_d->dimension == Dimensions::twodimensional) {
inbetween[m_d->channel2] = coordinates.y();
}
HSLToRGB(inbetween[0], inbetween[1], inbetween[2],&channelValues[0],&channelValues[1], &channelValues[2]);
}
}
}
c.colorSpace()->fromNormalisedChannelsValue(c.data(), channelValues);
return c;
}
QPointF KisVisualColorSelectorShape::convertKoColorToShapeCoordinate(KoColor c)
{
if (c.colorSpace() != m_d->cs) {
c.convertTo(m_d->cs);
}
QVector <float> channelValues (m_d->currentColor.colorSpace()->channelCount());
channelValues.fill(1.0);
m_d->cs->normalisedChannelsValue(c.data(), channelValues);
QPointF coordinates(0.0,0.0);
if (m_d->model == ColorModel::Channel) {
coordinates.setX(channelValues[m_d->channel1]);
if (m_d->dimension == Dimensions::twodimensional) {
coordinates.setY(channelValues[m_d->channel2]);
}
} else {
if (c.colorSpace()->colorModelId().id() == "RGBA") {
QVector <float> inbetween(3);
if (m_d->model == ColorModel::HSV){
RGBToHSV(channelValues[0],channelValues[1], channelValues[2], &inbetween[0], &inbetween[1], &inbetween[2]);
coordinates.setX(channelValues[m_d->channel1]);
if (m_d->dimension == Dimensions::twodimensional) {
coordinates.setY(channelValues[m_d->channel2]);
}
} else {
RGBToHSL(channelValues[0],channelValues[1], channelValues[2], &inbetween[0], &inbetween[1], &inbetween[2]);
coordinates.setX(channelValues[m_d->channel1]);
if (m_d->dimension == Dimensions::twodimensional) {
coordinates.setY(channelValues[m_d->channel2]);
}
}
}
}
return coordinates;
}
void KisVisualColorSelectorShape::mousePressEvent(QMouseEvent *e)
{
QPointF coordinates = convertWidgetCoordinateToShapeCoordinate(e->pos());
KoColor col = convertShapeCoordinateToKoColor(coordinates);
setColor(col);
Q_EMIT sigNewColor(col);
}
void KisVisualColorSelectorShape::mouseReleaseEvent(QMouseEvent *)
{
}
void KisVisualColorSelectorShape::paintEvent(QPaintEvent*)
{
QPainter painter(this);
if (m_d->pixmapsNeedUpdate) {
getPixmap();
setMask(getMaskMap());
}
drawCursor();
painter.drawPixmap(0,0,m_d->fullSelector);
}
void KisVisualColorSelectorShape::resizeEvent(QResizeEvent *)
{
m_d->pixmapsNeedUpdate = true;
}
KisVisualColorSelectorShape::Dimensions KisVisualColorSelectorShape::getDimensions()
{
return m_d->dimension;
}
KisVisualColorSelectorShape::ColorModel KisVisualColorSelectorShape::getColorModel()
{
return m_d->model;
}
void KisVisualColorSelectorShape::setFullImage(QPixmap full)
{
m_d->fullSelector = full;
}
KoColor KisVisualColorSelectorShape::getCurrentColor()
{
return m_d->currentColor;
}
/*-----------Rectangle Shape------------*/
KisVisualRectangleSelectorShape::KisVisualRectangleSelectorShape(QWidget *parent,
Dimensions dimension,
ColorModel model,
const KoColorSpace *cs,
int channel1, int channel2,
singelDTypes d)
: KisVisualColorSelectorShape(parent, dimension, model, cs, channel1, channel2)
{
m_type = d;
}
KisVisualRectangleSelectorShape::~KisVisualRectangleSelectorShape()
{
}
QPointF KisVisualRectangleSelectorShape::convertShapeCoordinateToWidgetCoordinate(QPointF coordinate)
{
qreal x = width()/2;
qreal y = height()/2;
KisVisualColorSelectorShape::Dimensions dimension = getDimensions();
if (dimension == KisVisualColorSelectorShape::onedimensional && m_type == KisVisualRectangleSelectorShape::vertical) {
y = coordinate.x()*height();
} else if (dimension == KisVisualColorSelectorShape::onedimensional && m_type == KisVisualRectangleSelectorShape::horizontal) {
x = coordinate.x()*width();
} else {
x = coordinate.x()*width();
y = coordinate.y()*height();
}
return QPointF(x,y);
}
QPointF KisVisualRectangleSelectorShape::convertWidgetCoordinateToShapeCoordinate(QPoint coordinate)
{
//default implementation:
qreal x = 0.5;
qreal y = 0.5;
KisVisualColorSelectorShape::Dimensions dimension = getDimensions();
if (dimension == KisVisualColorSelectorShape::onedimensional && m_type == KisVisualRectangleSelectorShape::vertical) {
x = (qreal)coordinate.y()/(qreal)height();
} else if (dimension == KisVisualColorSelectorShape::onedimensional && m_type == KisVisualRectangleSelectorShape::horizontal) {
x = (qreal)coordinate.x()/(qreal)width();
} else {
x = (qreal)coordinate.x()/(qreal)width();
y = (qreal)coordinate.y()/(qreal)height();
}
return QPointF(x, y);
}
QRegion KisVisualRectangleSelectorShape::getMaskMap()
{
QRegion mask = QRegion(0,0,width(),height());
return mask;
}
void KisVisualRectangleSelectorShape::drawCursor()
{
QPointF cursorPoint = convertShapeCoordinateToWidgetCoordinate(getShapeCoordinates());
QPixmap fullSelector = getPixmap();
QPainter painter;
painter.begin(&fullSelector);
painter.setRenderHint(QPainter::Antialiasing);
//QPainterPath path;
QBrush fill;
painter.setPen(Qt::white);
fill.setStyle(Qt::SolidPattern);
fill.setColor(Qt::white);
painter.setBrush(fill);
painter.drawEllipse(cursorPoint, 5, 5);
//set filter conversion!
fill.setColor(getCurrentColor().toQColor());
painter.setPen(Qt::black);
painter.setBrush(fill);
painter.drawEllipse(cursorPoint, 4, 4);
painter.end();
setFullImage(fullSelector);
}
/*
* Copyright (C) Wolthera van Hovell tot Westerflier <griffinvalley@gmail.com>, (C) 2016
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KISVISUALCOLORSELECTOR_H
#define KISVISUALCOLORSELECTOR_H
#include <QWidget>
#include <QScopedPointer>
#include <QPixmap>
#include <QRegion>
#include <QMouseEvent>
#include <KoColor.h>
#include <KoColorSpace.h>
#include "kritaui_export.h"
/**
* @brief The KisVisualColorSelector class
* this gives a color selector box that draws gradients and everything.
* Unlike other color selectors, this one draws the full gamut of the given colorspace.
*/
class KRITAUI_EXPORT KisVisualColorSelector : public QWidget
{
Q_OBJECT
public:
explicit KisVisualColorSelector(QWidget *parent = 0);
~KisVisualColorSelector();
Q_SIGNALS:
void sigNewColor(KoColor c);
public Q_SLOTS:
void slotSetColor(KoColor c);
void slotsetColorSpace(const KoColorSpace *cs);
private Q_SLOTS:
void updateFromWidgets(KoColor c);
private:
struct Private;
const QScopedPointer<Private> m_d;
void updateSelectorElements();
void drawGradients();
};
//kis_visual_color_selector_shape
class KisVisualColorSelectorShape : public QWidget
{
Q_OBJECT
public:
/**
* @brief The Dimensions enum
* Wether or not the shape is single or two dimensional.
* A 2d widget can represent at maximum 2 coordinates.
*/
enum Dimensions{onedimensional, twodimensional};
enum ColorModel{Channel, HSV, HSL, HSI, HSY, YUV};
explicit KisVisualColorSelectorShape(QWidget *parent,
KisVisualColorSelectorShape::Dimensions dimension,
KisVisualColorSelectorShape::ColorModel model,
const KoColorSpace *cs,
int channel1, int channel2);
~KisVisualColorSelectorShape();
QPointF getShapeCoordinates();
Dimensions getDimensions();
ColorModel getColorModel();
QPixmap getPixmap();
void setFullImage(QPixmap full);
KoColor getCurrentColor();
Q_SIGNALS:
void sigNewColor(KoColor col);
public Q_SLOTS:
void setColor(KoColor c);
void slotSetActiveChannels(int channel1, int channel2);
protected:
void mousePressEvent(QMouseEvent *e);
void mouseReleaseEvent(QMouseEvent *);
void paintEvent(QPaintEvent*);
void resizeEvent(QResizeEvent *);
private:
struct Private;
const QScopedPointer<Private> m_d;
/**
* @brief convertShapeCoordinateToWidgetCoordinate
* @return take the position in the shape and convert it to screen coordinates.
*/
virtual QPointF convertShapeCoordinateToWidgetCoordinate(QPointF) = 0;
/**
* @brief convertWidgetCoordinateToShapeCoordinate
* Convert a coordinate in the widget's height width to a shape coordinate.
* @param coordinate the position your wish to have the shape coordinates of.
*/
virtual QPointF convertWidgetCoordinateToShapeCoordinate(QPoint coordinate) = 0;
QPointF convertKoColorToShapeCoordinate(KoColor c);
KoColor convertShapeCoordinateToKoColor(QPointF coordinates);
/**
* @brief getPixmap
* @return the pixmap of this shape.
*/
virtual QRegion getMaskMap() = 0;
virtual void drawCursor() = 0;