Skip to content

Commit

Permalink
Add color circle dock
Browse files Browse the repository at this point in the history
Showing something similar to what Krita calls "artistic color selector"
and MyPaint calls "HSV/HCY wheel". Splits the three components into a
slider for the value/lightness/luminance and a circle for hue and
saturation/chroma. Can be configured to restrict the number of steps for
each of these dimensions.
  • Loading branch information
askmeaboutlo0m committed Nov 8, 2024
1 parent dc0afdb commit 2c9747b
Show file tree
Hide file tree
Showing 11 changed files with 1,240 additions and 6 deletions.
1 change: 1 addition & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
Unreleased Version 2.2.2-pre
* Feature: Color circle dock. Similar to Krita's Artistic Color Selector and MyPaint's HSV/HCY Wheel.

2024-11-06 Version 2.2.2-beta.4
* Fix: Solve rendering glitches with selection outlines that happen on some systems. Thanks xxxx for reporting.
Expand Down
6 changes: 6 additions & 0 deletions src/desktop/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ target_sources(drawpile PRIVATE
dialogs/animationexportdialog.h
dialogs/animationimportdialog.cpp
dialogs/animationimportdialog.h
dialogs/artisticcolorwheeldialog.cpp
dialogs/artisticcolorwheeldialog.h
dialogs/avatarimport.cpp
dialogs/avatarimport.h
dialogs/brushexportdialog.cpp
Expand Down Expand Up @@ -187,6 +189,8 @@ target_sources(drawpile PRIVATE
docks/brushpalettedelegate.h
docks/brushpalettedock.cpp
docks/brushpalettedock.h
docks/colorcircle.cpp
docks/colorcircle.h
docks/colorpalette.cpp
docks/colorpalette.h
docks/colorsliders.cpp
Expand Down Expand Up @@ -333,6 +337,8 @@ target_sources(drawpile PRIVATE
view/softwarecanvas.h
view/viewwrapper.cpp
view/viewwrapper.h
widgets/artisticcolorwheel.cpp
widgets/artisticcolorwheel.h
widgets/brushpreview.cpp
widgets/brushpreview.h
widgets/canvasframe.cpp
Expand Down
203 changes: 203 additions & 0 deletions src/desktop/dialogs/artisticcolorwheeldialog.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#include "desktop/dialogs/artisticcolorwheeldialog.h"
#include "desktop/main.h"
#include "desktop/utils/widgetutils.h"
#include "desktop/widgets/artisticcolorwheel.h"
#include "desktop/widgets/kis_slider_spin_box.h"
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QVBoxLayout>

namespace dialogs {

ArtisticColorWheelDialog::ArtisticColorWheelDialog(QWidget *parent)
: QDialog(parent)
{
setWindowTitle(tr("Color Circle Settings"));
setWindowModality(Qt::WindowModal);
resize(350, 500);

QVBoxLayout *layout = new QVBoxLayout(this);
const desktop::settings::Settings &settings = dpApp().settings();

bool hueLimit = settings.colorCircleHueLimit();
m_continuousHueBox = new QCheckBox;
m_continuousHueBox->setChecked(!hueLimit);
layout->addWidget(m_continuousHueBox);
connect(
m_continuousHueBox, COMPAT_CHECKBOX_STATE_CHANGED_SIGNAL(QCheckBox),
this, &ArtisticColorWheelDialog::updateContinuousHue);

int hueCount = settings.colorCircleHueCount();
m_hueStepsSlider = new KisSliderSpinBox;
m_hueStepsSlider->setRange(
widgets::ArtisticColorWheel::HUE_COUNT_MIN,
widgets::ArtisticColorWheel::HUE_COUNT_MAX);
m_hueStepsSlider->setValue(hueCount);
m_hueStepsSlider->setEnabled(hueLimit);
layout->addWidget(m_hueStepsSlider);
connect(
m_hueStepsSlider, QOverload<int>::of(&KisSliderSpinBox::valueChanged),
this, &ArtisticColorWheelDialog::updateHueSteps);

int hueAngle = settings.colorCircleHueAngle();
m_hueAngleSlider = new KisSliderSpinBox;
m_hueAngleSlider->setRange(0, 180);
m_hueAngleSlider->setValue(hueAngle);
m_hueAngleSlider->setEnabled(hueLimit);
layout->addWidget(m_hueAngleSlider);
connect(
m_hueAngleSlider, QOverload<int>::of(&KisSliderSpinBox::valueChanged),
this, &ArtisticColorWheelDialog::updateHueAngle);

bool saturationLimit = settings.colorCircleSaturationLimit();
m_continuousSaturationBox = new QCheckBox;
m_continuousSaturationBox->setChecked(!saturationLimit);
layout->addWidget(m_continuousSaturationBox);
connect(
m_continuousSaturationBox,
COMPAT_CHECKBOX_STATE_CHANGED_SIGNAL(QCheckBox), this,
&ArtisticColorWheelDialog::updateContinuousSaturation);

int saturationCount = settings.colorCircleSaturationCount();
m_saturationStepsSlider = new KisSliderSpinBox;
m_saturationStepsSlider->setRange(
widgets::ArtisticColorWheel::SATURATION_COUNT_MIN,
widgets::ArtisticColorWheel::SATURATION_COUNT_MAX);
m_saturationStepsSlider->setValue(saturationCount);
m_saturationStepsSlider->setEnabled(saturationLimit);
layout->addWidget(m_saturationStepsSlider);
connect(
m_saturationStepsSlider,
QOverload<int>::of(&KisSliderSpinBox::valueChanged), this,
&ArtisticColorWheelDialog::updateSaturationSteps);

bool valueLimit = settings.colorCircleValueLimit();
m_continuousValueBox = new QCheckBox;
m_continuousValueBox->setChecked(!valueLimit);
layout->addWidget(m_continuousValueBox);
connect(
m_continuousValueBox, COMPAT_CHECKBOX_STATE_CHANGED_SIGNAL(QCheckBox),
this, &ArtisticColorWheelDialog::updateContinuousValue);

int valueCount = settings.colorCircleValueCount();
m_valueStepsSlider = new KisSliderSpinBox;
m_valueStepsSlider->setRange(
widgets::ArtisticColorWheel::VALUE_COUNT_MIN,
widgets::ArtisticColorWheel::VALUE_COUNT_MAX);
m_valueStepsSlider->setValue(valueCount);
m_valueStepsSlider->setEnabled(valueLimit);
layout->addWidget(m_valueStepsSlider);
connect(
m_valueStepsSlider, QOverload<int>::of(&KisSliderSpinBox::valueChanged),
this, &ArtisticColorWheelDialog::updateValueSteps);

m_continuousHueBox->setText(tr("Continuous hue"));
m_hueStepsSlider->setPrefix(tr("Hue steps: "));
m_hueAngleSlider->setPrefix(tr("Hue angle: "));
//: Degree symbol. Unless your language uses a different one, keep as-is.
m_hueAngleSlider->setSuffix(tr("°"));
ColorSpace colorSpace = settings.colorWheelSpace();
if(colorSpace == ColorSpace::ColorLCH) {
m_continuousSaturationBox->setText(tr("Continuous chroma"));
m_saturationStepsSlider->setPrefix(tr("Chroma steps: "));
m_continuousValueBox->setText(tr("Continuous luminance"));
m_saturationStepsSlider->setPrefix(tr("Luminance steps: "));
} else {
m_continuousSaturationBox->setText(tr("Continuous saturation"));
m_saturationStepsSlider->setPrefix(tr("Saturation steps: "));
if(colorSpace == ColorSpace::ColorHSL) {
m_continuousValueBox->setText(tr("Continuous lightness"));
m_valueStepsSlider->setPrefix(tr("Lightness steps: "));
} else {
m_continuousValueBox->setText(tr("Continuous value"));
m_valueStepsSlider->setPrefix(tr("Value steps: "));
}
}

m_wheel = new widgets::ArtisticColorWheel;
m_wheel->setHueLimit(hueLimit);
m_wheel->setHueCount(hueCount);
m_wheel->setHueAngle(hueAngle);
m_wheel->setSaturationLimit(saturationLimit);
m_wheel->setSaturationCount(saturationCount);
m_wheel->setValueLimit(valueLimit);
m_wheel->setValueCount(valueCount);
m_wheel->setColorSpace(colorSpace);
m_wheel->setColor(Qt::red);
m_wheel->setMinimumSize(64, 64);
layout->addWidget(m_wheel, 1);

QDialogButtonBox *buttons =
new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
layout->addWidget(buttons);
connect(
buttons, &QDialogButtonBox::accepted, this,
&ArtisticColorWheelDialog::accept);
connect(
buttons, &QDialogButtonBox::rejected, this,
&ArtisticColorWheelDialog::reject);

connect(
this, &ArtisticColorWheelDialog::accepted, this,
&ArtisticColorWheelDialog::saveSettings);
}

void ArtisticColorWheelDialog::updateContinuousHue(compat::CheckBoxState state)
{
bool limit = state == Qt::Unchecked;
m_hueStepsSlider->setEnabled(limit);
m_hueAngleSlider->setEnabled(limit);
m_wheel->setHueLimit(limit);
}

void ArtisticColorWheelDialog::updateHueSteps(int steps)
{
m_wheel->setHueCount(steps);
}

void ArtisticColorWheelDialog::updateHueAngle(int angle)
{
m_wheel->setHueAngle(angle);
}

void ArtisticColorWheelDialog::updateContinuousSaturation(
compat::CheckBoxState state)
{
bool limit = state == Qt::Unchecked;
m_saturationStepsSlider->setEnabled(limit);
m_wheel->setSaturationLimit(limit);
}

void ArtisticColorWheelDialog::updateSaturationSteps(int steps)
{
m_wheel->setSaturationCount(steps);
}

void ArtisticColorWheelDialog::updateContinuousValue(
compat::CheckBoxState state)
{
bool limit = state == Qt::Unchecked;
m_valueStepsSlider->setEnabled(limit);
m_wheel->setValueLimit(limit);
}

void ArtisticColorWheelDialog::updateValueSteps(int steps)
{
m_wheel->setValueCount(steps);
}

void ArtisticColorWheelDialog::saveSettings()
{
desktop::settings::Settings &settings = dpApp().settings();
settings.setColorCircleHueLimit(!m_continuousHueBox->isChecked());
settings.setColorCircleHueCount(m_hueStepsSlider->value());
settings.setColorCircleHueAngle(m_hueAngleSlider->value());
settings.setColorCircleSaturationLimit(
!m_continuousSaturationBox->isChecked());
settings.setColorCircleSaturationCount(m_saturationStepsSlider->value());
settings.setColorCircleValueLimit(!m_continuousValueBox->isChecked());
settings.setColorCircleValueCount(m_valueStepsSlider->value());
}

}
47 changes: 47 additions & 0 deletions src/desktop/dialogs/artisticcolorwheeldialog.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#ifndef DESKTOP_DIALOGS_ARTISTICCOLORWHEELDIALOG
#define DESKTOP_DIALOGS_ARTISTICCOLORWHEELDIALOG
#include "desktop/utils/qtguicompat.h"
#include <QDialog>
#include <QtColorWidgets/ColorWheel>

class KisSliderSpinBox;
class QCheckBox;
class QColor;

namespace widgets {
class ArtisticColorWheel;
}

namespace dialogs {

class ArtisticColorWheelDialog : public QDialog {
Q_OBJECT
public:
using ColorSpace = color_widgets::ColorWheel::ColorSpaceEnum;

explicit ArtisticColorWheelDialog(QWidget *parent = nullptr);

private:
void updateContinuousHue(compat::CheckBoxState state);
void updateHueSteps(int steps);
void updateHueAngle(int angle);
void updateContinuousSaturation(compat::CheckBoxState state);
void updateSaturationSteps(int steps);
void updateContinuousValue(compat::CheckBoxState state);
void updateValueSteps(int steps);
void saveSettings();

QCheckBox *m_continuousHueBox;
KisSliderSpinBox *m_hueStepsSlider;
KisSliderSpinBox *m_hueAngleSlider;
QCheckBox *m_continuousSaturationBox;
KisSliderSpinBox *m_saturationStepsSlider;
QCheckBox *m_continuousValueBox;
KisSliderSpinBox *m_valueStepsSlider;
widgets::ArtisticColorWheel *m_wheel;
};

}

#endif
Loading

0 comments on commit 2c9747b

Please sign in to comment.