From 56a82600f89cc8816906516a8056bfaa68c1701e Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 26 Aug 2019 21:35:51 +0300 Subject: [PATCH] Add HSL color picker box for theming. --- Telegram/Resources/langs/lang.strings | 1 + Telegram/SourceFiles/boxes/edit_color_box.cpp | 334 +++++++++++++----- Telegram/SourceFiles/boxes/edit_color_box.h | 28 +- .../SourceFiles/settings/settings_chat.cpp | 33 +- .../themes/window_theme_editor_block.cpp | 2 +- .../window/themes/window_themes_embedded.cpp | 27 +- .../window/themes/window_themes_embedded.h | 2 + 7 files changed, 313 insertions(+), 114 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 344a975cd..54ef691a6 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -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"; diff --git a/Telegram/SourceFiles/boxes/edit_color_box.cpp b/Telegram/SourceFiles/boxes/edit_color_box.cpp index 0e55de98a..c0a453aa2 100644 --- a/Telegram/SourceFiles/boxes/edit_color_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_color_box.cpp @@ -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 &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(_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(_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(); } \ No newline at end of file diff --git a/Telegram/SourceFiles/boxes/edit_color_box.h b/Telegram/SourceFiles/boxes/edit_color_box.h index 3424cc9e6..869a20292 100644 --- a/Telegram/SourceFiles/boxes/edit_color_box.h +++ b/Telegram/SourceFiles/boxes/edit_color_box.h @@ -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 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; - object_ptr _hueSlider; - object_ptr _opacitySlider; + object_ptr _hueSlider = { nullptr }; + object_ptr _opacitySlider = { nullptr }; + object_ptr _lightnessSlider = { nullptr }; object_ptr _hueField; object_ptr _saturationField; diff --git a/Telegram/SourceFiles/settings/settings_chat.cpp b/Telegram/SourceFiles/settings/settings_chat.cpp index 75ce90bf7..5b2017712 100644 --- a/Telegram/SourceFiles/settings/settings_chat.cpp +++ b/Telegram/SourceFiles/settings/settings_chat.cpp @@ -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( - "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 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( - "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); } }; diff --git a/Telegram/SourceFiles/window/themes/window_theme_editor_block.cpp b/Telegram/SourceFiles/window/themes/window_theme_editor_block.cpp index 8c30bd608..23a80a7ff 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_editor_block.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme_editor_block.cpp @@ -293,7 +293,7 @@ void EditorBlock::activateRow(const Row &row) { } } else { _editing = findRowIndex(&row); - if (auto box = Ui::show(Box(row.name(), row.value()))) { + if (auto box = Ui::show(Box(row.name(), EditColorBox::Mode::RGBA, row.value()))) { box->setSaveCallback(crl::guard(this, [this](QColor value) { saveEditing(value); })); diff --git a/Telegram/SourceFiles/window/themes/window_themes_embedded.cpp b/Telegram/SourceFiles/window/themes/window_themes_embedded.cpp index a5b97e53a..0d3ea0bea 100644 --- a/Telegram/SourceFiles/window/themes/window_themes_embedded.cpp +++ b/Telegram/SourceFiles/window/themes/window_themes_embedded.cpp @@ -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{ { - // { qstr("test"), cColor("aaaaaa") }, - //} }; + result.lightnessMax = 191; + break; + case EmbeddedType::Night: + result.keepContrast = base::flat_map{ { + { 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{ { + { 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; } diff --git a/Telegram/SourceFiles/window/themes/window_themes_embedded.h b/Telegram/SourceFiles/window/themes/window_themes_embedded.h index 88a87b7f7..22bf658dc 100644 --- a/Telegram/SourceFiles/window/themes/window_themes_embedded.h +++ b/Telegram/SourceFiles/window/themes/window_themes_embedded.h @@ -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 ignoreKeys;