diff --git a/Telegram/SourceFiles/settings/settings_chat.cpp b/Telegram/SourceFiles/settings/settings_chat.cpp index 51a6e5bb5..a27e16adf 100644 --- a/Telegram/SourceFiles/settings/settings_chat.cpp +++ b/Telegram/SourceFiles/settings/settings_chat.cpp @@ -48,7 +48,7 @@ namespace Settings { namespace { const auto kSchemesList = Window::Theme::EmbeddedThemes(); -constexpr auto kColorsPerRow = 10; +constexpr auto kCustomColorButtonParts = 7; class ColorsPalette final { public: @@ -62,82 +62,155 @@ public: rpl::producer selected() const; private: + class Button { + public: + Button( + not_null parent, + std::vector &&colors, + bool selected); + + void moveToLeft(int x, int y); + void update(std::vector &&colors, bool selected); + rpl::producer<> clicks() const; + bool selected() const; + QColor color() const; + + private: + void paint(); + + Ui::AbstractButton _widget; + std::vector _colors; + Ui::Animations::Simple _selectedAnimation; + bool _selected = false; + + }; + + void show( + not_null scheme, + std::vector &&colors, + int selected); + void selectCustom(not_null scheme); void updateInnerGeometry(); not_null*> _outer; - std::vector>> _buttons; + std::vector> _buttons; rpl::event_stream _selected; }; -not_null CreateColorButton( - not_null parent, - const QColor &color, - bool selected) { - const auto result = Ui::CreateChild(parent.get()); - result->show(); - result->resize(st::settingsAccentColorSize, st::settingsAccentColorSize); - result->paintRequest( - ) | rpl::start_with_next([=](QRect clip) { - Painter p(result); - PainterHighQualityEnabler hq(p); +void PaintColorButton(Painter &p, QColor color, float64 selected) { + const auto size = st::settingsAccentColorSize; + const auto rect = QRect(0, 0, size, size); - const auto rect = result->rect(); - p.setBrush(color); - p.setPen(Qt::NoPen); - p.drawEllipse(result->rect()); - if (selected) { - const auto skip = st::settingsAccentColorSkip; - auto pen = st::boxBg->p; - pen.setWidth(st::settingsAccentColorLine); - p.setBrush(Qt::NoBrush); - p.setPen(pen); - p.drawEllipse(rect.marginsRemoved({ skip, skip, skip, skip })); - } - }, result->lifetime()); - return result; + p.setBrush(color); + p.setPen(Qt::NoPen); + p.drawEllipse(rect); + + if (selected > 0.) { + const auto startSkip = -st::settingsAccentColorLine / 2.; + const auto endSkip = float64(st::settingsAccentColorSkip); + const auto skip = startSkip + (endSkip - startSkip) * selected; + auto pen = st::boxBg->p; + pen.setWidth(st::settingsAccentColorLine); + p.setBrush(Qt::NoBrush); + p.setPen(pen); + p.setOpacity(selected); + p.drawEllipse(QRectF(rect).marginsRemoved({ skip, skip, skip, skip })); + } } -not_null CreateCustomButton( - not_null parent, - std::vector colors) { - const auto shared = std::make_shared>( - std::move(colors)); - const auto result = Ui::CreateChild(parent.get()); - result->show(); +void PaintCustomButton(Painter &p, const std::vector &colors) { + Expects(colors.size() >= kCustomColorButtonParts); + + p.setPen(Qt::NoPen); + const auto size = st::settingsAccentColorSize; - result->resize(size, size); - result->paintRequest( - ) | rpl::start_with_next([=](QRect clip) { - Painter p(result); - PainterHighQualityEnabler hq(p); - - p.setPen(Qt::NoPen); - - const auto smallSize = size / 8.; - const auto &list = *shared; - const auto drawAround = [&](QPointF center, int index) { - const auto where = QPointF{ - size * (1. + center.x()) / 2, - size * (1. + center.y()) / 2 - }; - p.setBrush(list[index]); - p.drawEllipse( - where.x() - smallSize, - where.y() - smallSize, - 2 * smallSize, - 2 * smallSize); + const auto smallSize = size / 8.; + const auto drawAround = [&](QPointF center, int index) { + const auto where = QPointF{ + size * (1. + center.x()) / 2, + size * (1. + center.y()) / 2 }; - drawAround(QPointF(), 0); - for (auto i = 0; i != 6; ++i) { - const auto angle = i * M_PI / 3.; - const auto point = QPointF{ cos(angle), sin(angle) }; - const auto adjusted = point * (1. - (2 * smallSize / size)); - drawAround(adjusted, i + 1); - } - }, result->lifetime()); - return result; + p.setBrush(colors[index]); + p.drawEllipse( + where.x() - smallSize, + where.y() - smallSize, + 2 * smallSize, + 2 * smallSize); + }; + drawAround(QPointF(), 0); + for (auto i = 0; i != 6; ++i) { + const auto angle = i * M_PI / 3.; + const auto point = QPointF{ cos(angle), sin(angle) }; + const auto adjusted = point * (1. - (2 * smallSize / size)); + drawAround(adjusted, i + 1); + } + +} + +ColorsPalette::Button::Button( + not_null parent, + std::vector &&colors, + bool selected) +: _widget(parent.get()) +, _colors(std::move(colors)) +, _selected(selected) { + _widget.show(); + _widget.resize(st::settingsAccentColorSize, st::settingsAccentColorSize); + _widget.paintRequest( + ) | rpl::start_with_next([=] { + paint(); + }, _widget.lifetime()); +} + +void ColorsPalette::Button::moveToLeft(int x, int y) { + _widget.moveToLeft(x, y); +} + +void ColorsPalette::Button::update( + std::vector &&colors, + bool selected) { + if (_colors != colors) { + _colors = std::move(colors); + _widget.update(); + } + if (_selected != selected) { + _selected = selected; + _selectedAnimation.start( + [=] { _widget.update(); }, + _selected ? 0. : 1., + _selected ? 1. : 0., + st::defaultRadio.duration * 2); + } +} + +rpl::producer<> ColorsPalette::Button::clicks() const { + return _widget.clicks() | rpl::map([] { return rpl::empty_value(); }); +} + +bool ColorsPalette::Button::selected() const { + return _selected; +} + +QColor ColorsPalette::Button::color() const { + Expects(_colors.size() == 1); + + return _colors.front(); +} + +void ColorsPalette::Button::paint() { + Painter p(&_widget); + PainterHighQualityEnabler hq(p); + + if (_colors.size() == 1) { + PaintColorButton( + p, + _colors.front(), + _selectedAnimation.value(_selected ? 1. : 0.)); + } else if (_colors.size() >= kCustomColorButtonParts) { + PaintCustomButton(p, _colors); + } } ColorsPalette::ColorsPalette(not_null container) @@ -172,71 +245,111 @@ void ColorsPalette::show(Type type) { if (i == end(list)) { list.back() = current; } + const auto selected = std::clamp( + int(i - begin(list)), + 0, + int(list.size()) - 1); + _outer->show(anim::type::instant); - _buttons.clear(); - const auto pushButton = [&](not_null button) { - if (_buttons.empty() || _buttons.back().size() == kColorsPerRow) { - _buttons.emplace_back(); - } - _buttons.back().emplace_back(button.get()); - }; + + show(&*scheme, std::move(list), selected); + const auto inner = _outer->entity(); - const auto size = st::settingsAccentColorSize; - for (const auto &color : list) { - const auto selected = (color == current); - const auto button = CreateColorButton(inner, color, selected); - button->clicks( - ) | rpl::map([=] { - return color; - }) | rpl::start_with_next([=](QColor color) { - _selected.fire_copy(color); - }, button->lifetime()); - pushButton(button); - } - const auto custom = CreateCustomButton(inner, std::move(list)); - custom->clicks( - ) | rpl::start_with_next([=] { - const auto colorizer = Window::Theme::ColorizerFrom( - *scheme, - scheme->accentColor); - auto box = Box( - 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); - })); - Ui::show(std::move(box)); - }, custom->lifetime()); - pushButton(custom); inner->resize(_outer->width(), inner->height()); updateInnerGeometry(); } +void ColorsPalette::show( + not_null scheme, + std::vector &&colors, + int selected) { + Expects(selected >= 0 && selected < colors.size()); + + while (_buttons.size() > colors.size()) { + _buttons.pop_back(); + } + + auto index = 0; + const auto inner = _outer->entity(); + const auto pushButton = [&](std::vector &&colors) { + auto result = rpl::producer<>(); + const auto chosen = (index == selected); + if (_buttons.size() > index) { + _buttons[index]->update(std::move(colors), chosen); + } else { + _buttons.push_back(std::make_unique