Add HSL color picker box for theming.

This commit is contained in:
John Preston 2019-08-26 21:35:51 +03:00
parent a3e993253c
commit 56a82600f8
7 changed files with 313 additions and 114 deletions

View File

@ -389,6 +389,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_theme_classic" = "Classic";
"lng_settings_theme_midnight" = "Midnight";
"lng_settings_theme_matrix" = "Matrix";
"lng_settings_theme_accent_title" = "Choose accent color";
"lng_settings_data_storage" = "Data and storage";
"lng_settings_information" = "Edit profile";
"lng_settings_passcode_title" = "Local passcode";

View File

@ -16,7 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class EditColorBox::Picker : public TWidget {
public:
Picker(QWidget *parent, QColor color);
Picker(QWidget *parent, Mode mode, QColor color);
float64 valueX() const {
return _x;
@ -28,7 +28,7 @@ public:
base::Observable<void> &changed() {
return _changed;
}
void setHSV(int hue, int saturation, int brightness);
void setHSB(HSB hsb);
void setRGB(int red, int green, int blue);
protected:
@ -43,8 +43,11 @@ private:
QCursor generateCursor();
void preparePalette();
void preparePaletteRGBA();
void preparePaletteHSL();
void updateCurrentPoint(QPoint localPosition);
Mode _mode;
QColor _topleft;
QColor _topright;
QColor _bottomleft;
@ -84,7 +87,9 @@ QCursor EditColorBox::Picker::generateCursor() {
return QCursor(QPixmap::fromImage(cursor));
}
EditColorBox::Picker::Picker(QWidget *parent, QColor color) : TWidget(parent) {
EditColorBox::Picker::Picker(QWidget *parent, Mode mode, QColor color)
: TWidget(parent)
, _mode(mode) {
setCursor(generateCursor());
auto size = QSize(st::colorPickerSize, st::colorPickerSize);
@ -137,18 +142,26 @@ void EditColorBox::Picker::preparePalette() {
if (!_paletteInvalidated) return;
_paletteInvalidated = false;
auto size = _palette.width();
if (_mode == Mode::RGBA) {
preparePaletteRGBA();
} else {
preparePaletteHSL();
}
}
void EditColorBox::Picker::preparePaletteRGBA() {
const auto size = _palette.width();
auto ints = reinterpret_cast<uint32*>(_palette.bits());
auto intsAddPerLine = (_palette.bytesPerLine() - size * sizeof(uint32)) / sizeof(uint32);
const auto intsAddPerLine = (_palette.bytesPerLine() - size * sizeof(uint32)) / sizeof(uint32);
constexpr auto Large = 1024 * 1024;
constexpr auto LargeBit = 20; // n / Large == (n >> LargeBit)
auto part = Large / size;
const auto part = Large / size;
auto topleft = anim::shifted(_topleft);
auto topright = anim::shifted(_topright);
auto bottomleft = anim::shifted(_bottomleft);
auto bottomright = anim::shifted(_bottomright);
const auto topleft = anim::shifted(_topleft);
const auto topright = anim::shifted(_topright);
const auto bottomleft = anim::shifted(_bottomleft);
const auto bottomright = anim::shifted(_bottomright);
auto y_accumulated = 0;
for (auto y = 0; y != size; ++y, y_accumulated += part) {
@ -156,11 +169,11 @@ void EditColorBox::Picker::preparePalette() {
// 0 <= y_accumulated < Large
// 0 <= y_ratio < 256
auto top_ratio = 255 - y_ratio;
auto bottom_ratio = y_ratio;
const auto top_ratio = 255 - y_ratio;
const auto bottom_ratio = y_ratio;
auto left = anim::reshifted(bottomleft * bottom_ratio + topleft * top_ratio);
auto right = anim::reshifted(bottomright * bottom_ratio + topright * top_ratio);
const auto left = anim::reshifted(bottomleft * bottom_ratio + topleft * top_ratio);
const auto right = anim::reshifted(bottomright * bottom_ratio + topright * top_ratio);
auto x_accumulated = 0;
for (auto x = 0; x != size; ++x, x_accumulated += part) {
@ -177,6 +190,41 @@ void EditColorBox::Picker::preparePalette() {
}
}
void EditColorBox::Picker::preparePaletteHSL() {
const auto size = _palette.width();
const auto intsAddPerLine = (_palette.bytesPerLine() - size * sizeof(uint32)) / sizeof(uint32);
auto ints = reinterpret_cast<uint32*>(_palette.bits());
constexpr auto Large = 1024 * 1024;
constexpr auto LargeBit = 20; // n / Large == (n >> LargeBit)
const auto part = Large / size;
const auto lightness = _topleft.lightness();
const auto right = anim::shifted(_bottomright);
for (auto y = 0; y != size; ++y) {
const auto hue = y * 360 / size;
const auto color = QColor::fromHsl(hue, 255, lightness).toRgb();
const auto left = anim::shifted(anim::getPremultiplied(color));
auto x_accumulated = 0;
for (auto x = 0; x != size; ++x, x_accumulated += part) {
auto x_ratio = x_accumulated >> (LargeBit - 8); // (x_accumulated * 256) / Large;
// 0 <= x_accumulated < Large
// 0 <= x_ratio < 256
auto left_ratio = 255 - x_ratio;
auto right_ratio = x_ratio;
*ints++ = anim::unshifted(left * left_ratio + right * right_ratio);
}
ints += intsAddPerLine;
}
_palette = std::move(_palette).transformed(
QTransform(0, 1, 1, 0, 0, 0));
}
void EditColorBox::Picker::updateCurrentPoint(QPoint localPosition) {
auto x = snap(localPosition.x(), 0, width()) / float64(width());
auto y = snap(localPosition.y(), 0, height()) / float64(height());
@ -188,17 +236,25 @@ void EditColorBox::Picker::updateCurrentPoint(QPoint localPosition) {
}
}
void EditColorBox::Picker::setHSV(int hue, int saturation, int brightness) {
_topleft = QColor(255, 255, 255);
_topright.setHsv(qMax(0, hue), 255, 255);
_topright = _topright.toRgb();
_bottomleft = _bottomright = QColor(0, 0, 0);
void EditColorBox::Picker::setHSB(HSB hsb) {
if (_mode == Mode::RGBA) {
_topleft = QColor(255, 255, 255);
_topright.setHsv(qMax(0, hsb.hue), 255, 255);
_topright = _topright.toRgb();
_bottomleft = _bottomright = QColor(0, 0, 0);
_x = snap(hsb.saturation / 255., 0., 1.);
_y = 1. - snap(hsb.brightness / 255., 0., 1.);
} else {
_topleft = _topright = QColor::fromHsl(0, 255, hsb.brightness);
_bottomleft = _bottomright = QColor::fromHsl(0, 0, hsb.brightness);
_x = snap(hsb.hue / 360., 0., 1.);
_y = 1. - snap(hsb.saturation / 255., 0., 1.);
}
_paletteInvalidated = true;
update();
_x = snap(saturation / 255., 0., 1.);
_y = 1. - snap(brightness / 255., 0., 1.);
}
void EditColorBox::Picker::setRGB(int red, int green, int blue) {
@ -206,7 +262,11 @@ void EditColorBox::Picker::setRGB(int red, int green, int blue) {
}
void EditColorBox::Picker::setFromColor(QColor color) {
setHSV(color.hsvHue(), color.hsvSaturation(), color.value());
if (_mode == Mode::RGBA) {
setHSB({ color.hsvHue(), color.hsvSaturation(), color.value() });
} else {
setHSB({ color.hslHue(), color.hslSaturation(), color.lightness() });
}
}
class EditColorBox::Slider : public TWidget {
@ -218,6 +278,7 @@ public:
enum class Type {
Hue,
Opacity,
Lightness
};
Slider(QWidget *parent, Direction direction, Type type, QColor color);
@ -231,7 +292,7 @@ public:
_value = snap(value, 0., 1.);
update();
}
void setHSV(int hue, int saturation, int brightness);
void setHSB(HSB hsb);
void setRGB(int red, int green, int blue);
void setAlpha(int alpha);
@ -275,7 +336,7 @@ EditColorBox::Slider::Slider(QWidget *parent, Direction direction, Type type, QC
, _type(type)
, _color(color.red(), color.green(), color.blue())
, _value(valueFromColor(color))
, _transparent((_type == Type::Hue) ? QBrush() : style::transparentPlaceholderBrush()) {
, _transparent((_type == Type::Opacity) ? style::transparentPlaceholderBrush() : QBrush()) {
prepareMinSize();
}
@ -336,10 +397,9 @@ void EditColorBox::Slider::generatePixmap() {
auto part = Large / size;
if (_type == Type::Hue) {
QColor color;
for (auto x = 0; x != size; ++x) {
color.setHsv(x * 360 / size, 255, 255);
auto value = anim::getPremultiplied(color.toRgb());
const auto color = QColor::fromHsv(x * 360 / size, 255, 255);
const auto value = anim::getPremultiplied(color.toRgb());
for (auto y = 0; y != cIntRetinaFactor(); ++y) {
ints[y * intsPerLine] = value;
}
@ -349,7 +409,7 @@ void EditColorBox::Slider::generatePixmap() {
image = std::move(image).transformed(QTransform(0, -1, 1, 0, 0, 0));
}
_pixmap = App::pixmapFromImageInPlace(std::move(image));
} else {
} else if (_type == Type::Opacity) {
auto color = anim::shifted(QColor(255, 255, 255, 255));
auto transparent = anim::shifted(QColor(255, 255, 255, 0));
for (auto y = 0; y != cIntRetinaFactor(); ++y) {
@ -368,16 +428,33 @@ void EditColorBox::Slider::generatePixmap() {
}
_mask = std::move(image);
updatePixmapFromMask();
} else {
QColor color;
for (auto x = 0; x != size; ++x) {
color.setHsl(_color.hslHue(), _color.hslSaturation(), x * 255 / size);
auto value = anim::getPremultiplied(color.toRgb());
for (auto y = 0; y != cIntRetinaFactor(); ++y) {
ints[y * intsPerLine] = value;
}
++ints;
}
if (!isHorizontal()) {
image = std::move(image).transformed(QTransform(0, -1, 1, 0, 0, 0));
}
_pixmap = App::pixmapFromImageInPlace(std::move(image));
}
}
void EditColorBox::Slider::setHSV(int hue, int saturation, int brightness) {
void EditColorBox::Slider::setHSB(HSB hsb) {
if (_type == Type::Hue) {
// hue == 360 converts to 0 if done in general way
_value = valueFromHue(hue);
_value = valueFromHue(hsb.hue);
update();
} else if (_type == Type::Opacity) {
_color.setHsv(hsb.hue, hsb.saturation, hsb.brightness);
colorUpdated();
} else {
_color.setHsv(hue, saturation, brightness);
_color.setHsl(hsb.hue, hsb.saturation, hsb.brightness);
colorUpdated();
}
}
@ -392,12 +469,18 @@ void EditColorBox::Slider::colorUpdated() {
_value = valueFromColor(_color);
} else if (!_mask.isNull()) {
updatePixmapFromMask();
} else {
generatePixmap();
}
update();
}
float64 EditColorBox::Slider::valueFromColor(QColor color) const {
return (_type == Type::Hue) ? valueFromHue(color.hsvHue()) : color.alphaF();
return (_type == Type::Hue)
? valueFromHue(color.hsvHue())
: (_type == Type::Opacity)
? color.alphaF()
: color.lightnessF();
}
float64 EditColorBox::Slider::valueFromHue(int hue) const {
@ -613,14 +696,18 @@ void EditColorBox::ResultField::paintAdditionalPlaceholder(Painter &p) {
p.drawText(QRect(_st.textMargins.right(), _st.textMargins.top(), width(), height() - _st.textMargins.top() - _st.textMargins.bottom()), "#", style::al_topleft);
}
EditColorBox::EditColorBox(QWidget*, const QString &title, QColor current) : BoxContent()
EditColorBox::EditColorBox(
QWidget*,
const QString &title,
Mode mode,
QColor current)
: BoxContent()
, _title(title)
, _picker(this, current)
, _hueSlider(this, Slider::Direction::Vertical, Slider::Type::Hue, current)
, _opacitySlider(this, Slider::Direction::Horizontal, Slider::Type::Opacity, current)
, _mode(mode)
, _picker(this, mode, current)
, _hueField(this, st::colorValueInput, "H", 360, QString() + QChar(176)) // degree character
, _saturationField(this, st::colorValueInput, "S", 100, "%")
, _brightnessField(this, st::colorValueInput, "B", 100, "%")
, _brightnessField(this, st::colorValueInput, (mode == Mode::RGBA) ? "B" : "L", 100, "%")
, _redField(this, st::colorValueInput, "R", 255)
, _greenField(this, st::colorValueInput, "G", 255)
, _blueField(this, st::colorValueInput, "B", 255)
@ -628,16 +715,38 @@ EditColorBox::EditColorBox(QWidget*, const QString &title, QColor current) : Box
, _transparent(style::transparentPlaceholderBrush())
, _current(current)
, _new(current) {
if (_mode == Mode::RGBA) {
_hueSlider.create(
this,
Slider::Direction::Vertical,
Slider::Type::Hue,
current);
_opacitySlider.create(
this,
Slider::Direction::Horizontal,
Slider::Type::Opacity,
current);
} else if (_mode == Mode::HSL) {
_lightnessSlider.create(
this,
Slider::Direction::Horizontal,
Slider::Type::Lightness,
current);
}
}
void EditColorBox::setLightnessLimits(int min, int max) {
}
void EditColorBox::prepare() {
setTitle(rpl::single(_title));
const auto hsvChanged = [=] { updateFromHSVFields(); };
const auto hsbChanged = [=] { updateFromHSBFields(); };
const auto rgbChanged = [=] { updateFromRGBFields(); };
connect(_hueField, &Ui::MaskedInputField::changed, hsvChanged);
connect(_saturationField, &Ui::MaskedInputField::changed, hsvChanged);
connect(_brightnessField, &Ui::MaskedInputField::changed, hsvChanged);
connect(_hueField, &Ui::MaskedInputField::changed, hsbChanged);
connect(_saturationField, &Ui::MaskedInputField::changed, hsbChanged);
connect(_brightnessField, &Ui::MaskedInputField::changed, hsbChanged);
connect(_redField, &Ui::MaskedInputField::changed, rgbChanged);
connect(_greenField, &Ui::MaskedInputField::changed, rgbChanged);
connect(_blueField, &Ui::MaskedInputField::changed, rgbChanged);
@ -661,8 +770,15 @@ void EditColorBox::prepare() {
setDimensions(st::colorEditWidth, height);
subscribe(_picker->changed(), [=] { updateFromControls(); });
subscribe(_hueSlider->changed(), [=] { updateFromControls(); });
subscribe(_opacitySlider->changed(), [=] { updateFromControls(); });
if (_hueSlider) {
subscribe(_hueSlider->changed(), [=] { updateFromControls(); });
}
if (_opacitySlider) {
subscribe(_opacitySlider->changed(), [=] { updateFromControls(); });
}
if (_lightnessSlider) {
subscribe(_lightnessSlider->changed(), [=] { updateFromControls(); });
}
boxClosing() | rpl::start_with_next([=] {
if (_cancelCallback) {
@ -671,7 +787,7 @@ void EditColorBox::prepare() {
}, lifetime());
updateRGBFields();
updateHSVFields();
updateHSBFields();
updateResultField();
update();
}
@ -714,14 +830,11 @@ void EditColorBox::saveColor() {
}
}
void EditColorBox::updateHSVFields() {
auto hue = qRound((1. - _hueSlider->value()) * 360);
auto saturation = qRound(_picker->valueX() * 255);
auto brightness = qRound((1. - _picker->valueY()) * 255);
auto alpha = qRound(_opacitySlider->value() * 255);
_hueField->setTextWithFocus(QString::number(hue));
_saturationField->setTextWithFocus(QString::number(percentFromByte(saturation)));
_brightnessField->setTextWithFocus(QString::number(percentFromByte(brightness)));
void EditColorBox::updateHSBFields() {
const auto hsb = hsbFromControls();
_hueField->setTextWithFocus(QString::number(hsb.hue));
_saturationField->setTextWithFocus(QString::number(percentFromByte(hsb.saturation)));
_brightnessField->setTextWithFocus(QString::number(percentFromByte(hsb.brightness)));
}
void EditColorBox::updateRGBFields() {
@ -753,14 +866,28 @@ void EditColorBox::updateResultField() {
}
void EditColorBox::resizeEvent(QResizeEvent *e) {
auto fullwidth = _picker->width() + 2 * (st::colorEditSkip - st::colorSliderSkip) + _hueSlider->width() + st::colorSampleSize.width();
const auto fullwidth = _picker->width()
+ ((_mode == Mode::RGBA)
? (2 * (st::colorEditSkip - st::colorSliderSkip) + _hueSlider->width())
: (2 * st::colorEditSkip))
+ st::colorSampleSize.width();
auto left = (width() - fullwidth) / 2;
_picker->moveToLeft(left, st::colorEditSkip);
_hueSlider->setGeometryToLeft(_picker->x() + _picker->width() + st::colorEditSkip - st::colorSliderSkip, st::colorEditSkip - st::colorSliderSkip, _hueSlider->width(), st::colorPickerSize + 2 * st::colorSliderSkip);
_opacitySlider->setGeometryToLeft(_picker->x() - st::colorSliderSkip, _picker->y() + _picker->height() + st::colorEditSkip - st::colorSliderSkip, _picker->width() + 2 * st::colorSliderSkip, _opacitySlider->height());
auto fieldLeft = _hueSlider->x() + _hueSlider->width() - st::colorSliderSkip + st::colorEditSkip;
auto fieldWidth = st::colorSampleSize.width();
auto fieldHeight = _hueField->height();
if (_hueSlider) {
_hueSlider->setGeometryToLeft(_picker->x() + _picker->width() + st::colorEditSkip - st::colorSliderSkip, st::colorEditSkip - st::colorSliderSkip, _hueSlider->width(), st::colorPickerSize + 2 * st::colorSliderSkip);
}
if (_opacitySlider) {
_opacitySlider->setGeometryToLeft(_picker->x() - st::colorSliderSkip, _picker->y() + _picker->height() + st::colorEditSkip - st::colorSliderSkip, _picker->width() + 2 * st::colorSliderSkip, _opacitySlider->height());
}
if (_lightnessSlider) {
_lightnessSlider->setGeometryToLeft(_picker->x() - st::colorSliderSkip, _picker->y() + _picker->height() + st::colorEditSkip - st::colorSliderSkip, _picker->width() + 2 * st::colorSliderSkip, _lightnessSlider->height());
}
const auto fieldLeft = (_mode == Mode::RGBA)
? (_hueSlider->x() + _hueSlider->width() + st::colorEditSkip - st::colorSliderSkip)
: (_picker->x() + _picker->width() + st::colorEditSkip);
const auto addWidth = (_mode == Mode::RGBA) ? 0 : st::colorEditSkip;
const auto fieldWidth = st::colorSampleSize.width() + addWidth;
const auto fieldHeight = _hueField->height();
_newRect = QRect(fieldLeft, st::colorEditSkip, fieldWidth, st::colorSampleSize.height());
_currentRect = _newRect.translated(0, st::colorSampleSize.height());
_hueField->setGeometryToLeft(fieldLeft, _currentRect.y() + _currentRect.height() + st::colorFieldSkip, fieldWidth, fieldHeight);
@ -769,7 +896,13 @@ void EditColorBox::resizeEvent(QResizeEvent *e) {
_redField->setGeometryToLeft(fieldLeft, _brightnessField->y() + _brightnessField->height() + st::colorFieldSkip, fieldWidth, fieldHeight);
_greenField->setGeometryToLeft(fieldLeft, _redField->y() + _redField->height(), fieldWidth, fieldHeight);
_blueField->setGeometryToLeft(fieldLeft, _greenField->y() + _greenField->height(), fieldWidth, fieldHeight);
_result->setGeometryToLeft(fieldLeft - (st::colorEditSkip + st::colorSliderWidth), _opacitySlider->y() + _opacitySlider->height() - st::colorSliderSkip - _result->height(), fieldWidth + (st::colorEditSkip + st::colorSliderWidth), fieldHeight);
const auto resultDelta = (_mode == Mode::RGBA)
? (st::colorEditSkip + st::colorSliderWidth)
: 0;
const auto resultBottom = (_mode == Mode::RGBA)
? (_opacitySlider->y() + _opacitySlider->height())
: (_lightnessSlider->y() + _lightnessSlider->height());
_result->setGeometryToLeft(fieldLeft - resultDelta, resultBottom - st::colorSliderSkip - _result->height(), fieldWidth + resultDelta, fieldHeight);
}
void EditColorBox::paintEvent(QPaintEvent *e) {
@ -795,39 +928,56 @@ void EditColorBox::mousePressEvent(QMouseEvent *e) {
}
}
EditColorBox::HSB EditColorBox::hsbFromControls() const {
const auto hue = (_mode == Mode::RGBA)
? qRound((1. - _hueSlider->value()) * 360)
: qRound(_picker->valueX() * 360);
const auto saturation = (_mode == Mode::RGBA)
? qRound(_picker->valueX() * 255)
: qRound((1. - _picker->valueY()) * 255);
const auto brightness = (_mode == Mode::RGBA)
? qRound((1. - _picker->valueY()) * 255)
: qRound(_lightnessSlider->value() * 255);
return { hue, saturation, brightness };
}
void EditColorBox::updateFromColor(QColor color) {
_new = color;
updateControlsFromColor();
updateRGBFields();
updateHSVFields();
updateHSBFields();
updateResultField();
update();
}
void EditColorBox::updateFromControls() {
auto hue = qRound((1. - _hueSlider->value()) * 360);
auto saturation = qRound(_picker->valueX() * 255);
auto brightness = qRound((1. - _picker->valueY()) * 255);
auto alpha = qRound(_opacitySlider->value() * 255);
setHSV(hue, saturation, brightness, alpha);
updateHSVFields();
updateControlsFromHSV(hue, saturation, brightness);
const auto hsb = hsbFromControls();
const auto alpha = _opacitySlider
? qRound(_opacitySlider->value() * 255)
: 255;
setHSB(hsb, alpha);
updateHSBFields();
updateControlsFromHSB(hsb);
}
void EditColorBox::updateFromHSVFields() {
void EditColorBox::updateFromHSBFields() {
auto hue = _hueField->value();
auto saturation = percentToByte(_saturationField->value());
auto brightness = percentToByte(_brightnessField->value());
auto alpha = qRound(_opacitySlider->value() * 255);
setHSV(hue, saturation, brightness, alpha);
updateControlsFromHSV(hue, saturation, brightness);
auto alpha = _opacitySlider
? qRound(_opacitySlider->value() * 255)
: 255;
setHSB({ hue, saturation, brightness }, alpha);
updateControlsFromHSB({ hue, saturation, brightness });
}
void EditColorBox::updateFromRGBFields() {
auto red = _redField->value();
auto blue = _blueField->value();
auto green = _greenField->value();
auto alpha = qRound(_opacitySlider->value() * 255);
auto alpha = _opacitySlider
? qRound(_opacitySlider->value() * 255)
: 255;
setRGB(red, green, blue, alpha);
updateResultField();
}
@ -858,10 +1008,17 @@ void EditColorBox::updateFromResultField() {
updateRGBFields();
}
void EditColorBox::updateControlsFromHSV(int hue, int saturation, int brightness) {
_picker->setHSV(hue, saturation, brightness);
_hueSlider->setHSV(hue, saturation, brightness);
_opacitySlider->setHSV(hue, saturation, brightness);
void EditColorBox::updateControlsFromHSB(HSB hsb) {
_picker->setHSB(hsb);
if (_hueSlider) {
_hueSlider->setHSB(hsb);
}
if (_opacitySlider) {
_opacitySlider->setHSB(hsb);
}
if (_lightnessSlider) {
_lightnessSlider->setHSB(hsb);
}
}
void EditColorBox::updateControlsFromColor() {
@ -870,13 +1027,24 @@ void EditColorBox::updateControlsFromColor() {
auto blue = _new.blue();
auto alpha = _new.alpha();
_picker->setRGB(red, green, blue);
_hueSlider->setRGB(red, green, blue);
_opacitySlider->setRGB(red, green, blue);
_opacitySlider->setAlpha(alpha);
if (_hueSlider) {
_hueSlider->setRGB(red, green, blue);
}
if (_opacitySlider) {
_opacitySlider->setRGB(red, green, blue);
_opacitySlider->setAlpha(alpha);
}
if (_lightnessSlider) {
_lightnessSlider->setRGB(red, green, blue);
}
}
void EditColorBox::setHSV(int hue, int saturation, int value, int alpha) {
_new.setHsv(hue, saturation, value, alpha);
void EditColorBox::setHSB(HSB hsb, int alpha) {
if (_mode == Mode::RGBA) {
_new.setHsv(hsb.hue, hsb.saturation, hsb.brightness, alpha);
} else {
_new.setHsl(hsb.hue, hsb.saturation, hsb.brightness, alpha);
}
updateRGBFields();
updateResultField();
update();
@ -885,6 +1053,6 @@ void EditColorBox::setHSV(int hue, int saturation, int value, int alpha) {
void EditColorBox::setRGB(int red, int green, int blue, int alpha) {
_new.setRgb(red, green, blue, alpha);
updateControlsFromColor();
updateHSVFields();
updateHSBFields();
update();
}

View File

@ -11,7 +11,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class EditColorBox : public BoxContent {
public:
EditColorBox(QWidget*, const QString &title, QColor current = QColor(255, 255, 255));
enum class Mode {
RGBA,
HSL,
};
EditColorBox(QWidget*, const QString &title, Mode mode, QColor current);
void setLightnessLimits(int min, int max);
void setSaveCallback(Fn<void(QColor)> callback) {
_saveCallback = std::move(callback);
@ -36,20 +42,26 @@ protected:
void setInnerFocus() override;
private:
struct HSB { // HSV or HSL depending on Mode.
int hue = 0;
int saturation = 0;
int brightness = 0;
};
void saveColor();
void fieldSubmitted();
[[nodiscard]] HSB hsbFromControls() const;
void updateFromColor(QColor color);
void updateControlsFromColor();
void updateControlsFromHSV(int hue, int saturation, int brightness);
void updateHSVFields();
void updateControlsFromHSB(HSB hsb);
void updateHSBFields();
void updateRGBFields();
void updateResultField();
void updateFromControls();
void updateFromHSVFields();
void updateFromHSBFields();
void updateFromRGBFields();
void updateFromResultField();
void setHSV(int hue, int saturation, int brightness, int alpha);
void setHSB(HSB hsb, int alpha);
void setRGB(int red, int green, int blue, int alpha);
int percentFromByte(int byte) {
@ -65,10 +77,12 @@ private:
class ResultField;
QString _title;
Mode _mode = Mode();
object_ptr<Picker> _picker;
object_ptr<Slider> _hueSlider;
object_ptr<Slider> _opacitySlider;
object_ptr<Slider> _hueSlider = { nullptr };
object_ptr<Slider> _opacitySlider = { nullptr };
object_ptr<Slider> _lightnessSlider = { nullptr };
object_ptr<Field> _hueField;
object_ptr<Field> _saturationField;

View File

@ -183,7 +183,7 @@ void ColorsPalette::show(Type type) {
const auto inner = _outer->entity();
const auto size = st::settingsAccentColorSize;
for (const auto &color : list) {
const auto selected = color == current;
const auto selected = (color == current);
const auto button = CreateColorButton(inner, color, selected);
button->clicks(
) | rpl::map([=] {
@ -196,9 +196,16 @@ void ColorsPalette::show(Type type) {
const auto custom = CreateCustomButton(inner, std::move(list));
custom->clicks(
) | rpl::start_with_next([=] {
const auto colorizer = Window::Theme::ColorizerFrom(
*scheme,
scheme->accentColor);
const auto box = Ui::show(Box<EditColorBox>(
"Choose accent color",
tr::lng_settings_theme_accent_title(tr::now),
EditColorBox::Mode::HSL,
current));
box->setLightnessLimits(
colorizer.lightnessMin,
colorizer.lightnessMax);
box->setSaveCallback(crl::guard(custom, [=](QColor result) {
_selected.fire_copy(result);
}));
@ -1006,28 +1013,14 @@ void SetupDefaultThemes(not_null<Ui::VerticalLayout*> container) {
const auto colorizer = Window::Theme::ColorizerFrom(scheme, color);
apply(scheme, &colorizer);
};
const auto applyWithColorize = [=](const Scheme &scheme) {
const auto &colors = Core::App().settings().themesAccentColors();
const auto color = colors.get(scheme.type);
const auto box = Ui::show(Box<EditColorBox>(
"Choose accent color",
color.value_or(scheme.accentColor)));
box->setSaveCallback([=](QColor result) {
applyWithColor(scheme, result);
});
};
const auto schemeClicked = [=](
const Scheme &scheme,
Qt::KeyboardModifiers modifiers) {
if (scheme.accentColor.hue() && (modifiers & Qt::ControlModifier)) {
applyWithColorize(scheme);
const auto &colors = Core::App().settings().themesAccentColors();
if (const auto color = colors.get(scheme.type)) {
applyWithColor(scheme, *color);
} else {
const auto &colors = Core::App().settings().themesAccentColors();
if (const auto color = colors.get(scheme.type)) {
applyWithColor(scheme, *color);
} else {
apply(scheme);
}
apply(scheme);
}
};

View File

@ -293,7 +293,7 @@ void EditorBlock::activateRow(const Row &row) {
}
} else {
_editing = findRowIndex(&row);
if (auto box = Ui::show(Box<EditColorBox>(row.name(), row.value()))) {
if (auto box = Ui::show(Box<EditColorBox>(row.name(), EditColorBox::Mode::RGBA, row.value()))) {
box->setSaveCallback(crl::guard(this, [this](QColor value) {
saveEditing(value);
}));

View File

@ -114,11 +114,32 @@ Colorizer ColorizerFrom(const EmbeddedScheme &scheme, const QColor &color) {
&result.now.lightness);
switch (scheme.type) {
case EmbeddedType::DayBlue:
//result.keepContrast = base::flat_map<QLatin1String, Color>{ {
// { qstr("test"), cColor("aaaaaa") },
//} };
result.lightnessMax = 191;
break;
case EmbeddedType::Night:
result.keepContrast = base::flat_map<QLatin1String, Color>{ {
{ qstr("windowFgActive"), cColor("5288c1") }, // windowBgActive
{ qstr("activeButtonFg"), cColor("2f6ea5") }, // activeButtonBg
{ qstr("profileVerifiedCheckFg"), cColor("5288c1") }, // profileVerifiedCheckBg
{ qstr("overviewCheckFgActive"), cColor("5288c1") }, // overviewCheckBgActive
} };
result.lightnessMin = 64;
break;
case EmbeddedType::NightGreen:
result.keepContrast = base::flat_map<QLatin1String, Color>{ {
{ qstr("windowFgActive"), cColor("3fc1b0") }, // windowBgActive
{ qstr("activeButtonFg"), cColor("2da192") }, // activeButtonBg
{ qstr("profileVerifiedCheckFg"), cColor("3fc1b0") }, // profileVerifiedCheckBg
{ qstr("overviewCheckFgActive"), cColor("3fc1b0") }, // overviewCheckBgActive
{ qstr("callIconFg"), cColor("5ad1c1") }, // callAnswerBg
} };
result.lightnessMin = 64;
break;
}
result.now.lightness = std::clamp(
result.now.lightness,
result.lightnessMin,
result.lightnessMax);
return result;
}

View File

@ -54,6 +54,8 @@ struct Colorizer {
int lightness = 0;
};
int hueThreshold = 0;
int lightnessMin = 0;
int lightnessMax = 255;
Color was;
Color now;
base::flat_set<QLatin1String> ignoreKeys;