mirror of
https://github.com/vale981/tdesktop
synced 2025-03-05 17:51:41 -05:00
Make accent color radiobutton animated.
This commit is contained in:
parent
87ea49e094
commit
85cca51154
1 changed files with 226 additions and 113 deletions
|
@ -48,7 +48,7 @@ namespace Settings {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
const auto kSchemesList = Window::Theme::EmbeddedThemes();
|
const auto kSchemesList = Window::Theme::EmbeddedThemes();
|
||||||
constexpr auto kColorsPerRow = 10;
|
constexpr auto kCustomColorButtonParts = 7;
|
||||||
|
|
||||||
class ColorsPalette final {
|
class ColorsPalette final {
|
||||||
public:
|
public:
|
||||||
|
@ -62,82 +62,155 @@ public:
|
||||||
rpl::producer<QColor> selected() const;
|
rpl::producer<QColor> selected() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
class Button {
|
||||||
|
public:
|
||||||
|
Button(
|
||||||
|
not_null<QWidget*> parent,
|
||||||
|
std::vector<QColor> &&colors,
|
||||||
|
bool selected);
|
||||||
|
|
||||||
|
void moveToLeft(int x, int y);
|
||||||
|
void update(std::vector<QColor> &&colors, bool selected);
|
||||||
|
rpl::producer<> clicks() const;
|
||||||
|
bool selected() const;
|
||||||
|
QColor color() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void paint();
|
||||||
|
|
||||||
|
Ui::AbstractButton _widget;
|
||||||
|
std::vector<QColor> _colors;
|
||||||
|
Ui::Animations::Simple _selectedAnimation;
|
||||||
|
bool _selected = false;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
void show(
|
||||||
|
not_null<const Scheme*> scheme,
|
||||||
|
std::vector<QColor> &&colors,
|
||||||
|
int selected);
|
||||||
|
void selectCustom(not_null<const Scheme*> scheme);
|
||||||
void updateInnerGeometry();
|
void updateInnerGeometry();
|
||||||
|
|
||||||
not_null<Ui::SlideWrap<>*> _outer;
|
not_null<Ui::SlideWrap<>*> _outer;
|
||||||
std::vector<std::vector<std::unique_ptr<Ui::RpWidget>>> _buttons;
|
std::vector<std::unique_ptr<Button>> _buttons;
|
||||||
|
|
||||||
rpl::event_stream<QColor> _selected;
|
rpl::event_stream<QColor> _selected;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
not_null<Ui::AbstractButton*> CreateColorButton(
|
void PaintColorButton(Painter &p, QColor color, float64 selected) {
|
||||||
not_null<Ui::RpWidget*> parent,
|
const auto size = st::settingsAccentColorSize;
|
||||||
const QColor &color,
|
const auto rect = QRect(0, 0, size, size);
|
||||||
bool selected) {
|
|
||||||
const auto result = Ui::CreateChild<Ui::AbstractButton>(parent.get());
|
|
||||||
result->show();
|
|
||||||
result->resize(st::settingsAccentColorSize, st::settingsAccentColorSize);
|
|
||||||
result->paintRequest(
|
|
||||||
) | rpl::start_with_next([=](QRect clip) {
|
|
||||||
Painter p(result);
|
|
||||||
PainterHighQualityEnabler hq(p);
|
|
||||||
|
|
||||||
const auto rect = result->rect();
|
p.setBrush(color);
|
||||||
p.setBrush(color);
|
p.setPen(Qt::NoPen);
|
||||||
p.setPen(Qt::NoPen);
|
p.drawEllipse(rect);
|
||||||
p.drawEllipse(result->rect());
|
|
||||||
if (selected) {
|
if (selected > 0.) {
|
||||||
const auto skip = st::settingsAccentColorSkip;
|
const auto startSkip = -st::settingsAccentColorLine / 2.;
|
||||||
auto pen = st::boxBg->p;
|
const auto endSkip = float64(st::settingsAccentColorSkip);
|
||||||
pen.setWidth(st::settingsAccentColorLine);
|
const auto skip = startSkip + (endSkip - startSkip) * selected;
|
||||||
p.setBrush(Qt::NoBrush);
|
auto pen = st::boxBg->p;
|
||||||
p.setPen(pen);
|
pen.setWidth(st::settingsAccentColorLine);
|
||||||
p.drawEllipse(rect.marginsRemoved({ skip, skip, skip, skip }));
|
p.setBrush(Qt::NoBrush);
|
||||||
}
|
p.setPen(pen);
|
||||||
}, result->lifetime());
|
p.setOpacity(selected);
|
||||||
return result;
|
p.drawEllipse(QRectF(rect).marginsRemoved({ skip, skip, skip, skip }));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
not_null<Ui::AbstractButton*> CreateCustomButton(
|
void PaintCustomButton(Painter &p, const std::vector<QColor> &colors) {
|
||||||
not_null<Ui::RpWidget*> parent,
|
Expects(colors.size() >= kCustomColorButtonParts);
|
||||||
std::vector<QColor> colors) {
|
|
||||||
const auto shared = std::make_shared<std::vector<QColor>>(
|
p.setPen(Qt::NoPen);
|
||||||
std::move(colors));
|
|
||||||
const auto result = Ui::CreateChild<Ui::AbstractButton>(parent.get());
|
|
||||||
result->show();
|
|
||||||
const auto size = st::settingsAccentColorSize;
|
const auto size = st::settingsAccentColorSize;
|
||||||
result->resize(size, size);
|
const auto smallSize = size / 8.;
|
||||||
result->paintRequest(
|
const auto drawAround = [&](QPointF center, int index) {
|
||||||
) | rpl::start_with_next([=](QRect clip) {
|
const auto where = QPointF{
|
||||||
Painter p(result);
|
size * (1. + center.x()) / 2,
|
||||||
PainterHighQualityEnabler hq(p);
|
size * (1. + center.y()) / 2
|
||||||
|
|
||||||
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);
|
|
||||||
};
|
};
|
||||||
drawAround(QPointF(), 0);
|
p.setBrush(colors[index]);
|
||||||
for (auto i = 0; i != 6; ++i) {
|
p.drawEllipse(
|
||||||
const auto angle = i * M_PI / 3.;
|
where.x() - smallSize,
|
||||||
const auto point = QPointF{ cos(angle), sin(angle) };
|
where.y() - smallSize,
|
||||||
const auto adjusted = point * (1. - (2 * smallSize / size));
|
2 * smallSize,
|
||||||
drawAround(adjusted, i + 1);
|
2 * smallSize);
|
||||||
}
|
};
|
||||||
}, result->lifetime());
|
drawAround(QPointF(), 0);
|
||||||
return result;
|
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<QWidget*> parent,
|
||||||
|
std::vector<QColor> &&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<QColor> &&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<Ui::VerticalLayout*> container)
|
ColorsPalette::ColorsPalette(not_null<Ui::VerticalLayout*> container)
|
||||||
|
@ -172,71 +245,111 @@ void ColorsPalette::show(Type type) {
|
||||||
if (i == end(list)) {
|
if (i == end(list)) {
|
||||||
list.back() = current;
|
list.back() = current;
|
||||||
}
|
}
|
||||||
|
const auto selected = std::clamp(
|
||||||
|
int(i - begin(list)),
|
||||||
|
0,
|
||||||
|
int(list.size()) - 1);
|
||||||
|
|
||||||
_outer->show(anim::type::instant);
|
_outer->show(anim::type::instant);
|
||||||
_buttons.clear();
|
|
||||||
const auto pushButton = [&](not_null<Ui::RpWidget*> button) {
|
show(&*scheme, std::move(list), selected);
|
||||||
if (_buttons.empty() || _buttons.back().size() == kColorsPerRow) {
|
|
||||||
_buttons.emplace_back();
|
|
||||||
}
|
|
||||||
_buttons.back().emplace_back(button.get());
|
|
||||||
};
|
|
||||||
const auto inner = _outer->entity();
|
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<EditColorBox>(
|
|
||||||
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());
|
inner->resize(_outer->width(), inner->height());
|
||||||
updateInnerGeometry();
|
updateInnerGeometry();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ColorsPalette::show(
|
||||||
|
not_null<const Scheme*> scheme,
|
||||||
|
std::vector<QColor> &&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<QColor> &&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<Button>(
|
||||||
|
inner,
|
||||||
|
std::move(colors),
|
||||||
|
chosen));
|
||||||
|
result = _buttons.back()->clicks();
|
||||||
|
}
|
||||||
|
++index;
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
for (const auto &color : colors) {
|
||||||
|
auto clicks = pushButton({ color });
|
||||||
|
if (clicks) {
|
||||||
|
std::move(
|
||||||
|
clicks
|
||||||
|
) | rpl::map([=] {
|
||||||
|
return _buttons[index - 1]->color();
|
||||||
|
}) | rpl::start_with_next([=](QColor color) {
|
||||||
|
_selected.fire_copy(color);
|
||||||
|
}, inner->lifetime());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto clicks = pushButton(std::move(colors));
|
||||||
|
if (clicks) {
|
||||||
|
std::move(
|
||||||
|
clicks
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
selectCustom(scheme);
|
||||||
|
}, inner->lifetime());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ColorsPalette::selectCustom(not_null<const Scheme*> scheme) {
|
||||||
|
const auto selected = ranges::find(_buttons, true, &Button::selected);
|
||||||
|
Assert(selected != end(_buttons));
|
||||||
|
|
||||||
|
const auto colorizer = Window::Theme::ColorizerFrom(
|
||||||
|
*scheme,
|
||||||
|
scheme->accentColor);
|
||||||
|
auto box = Box<EditColorBox>(
|
||||||
|
tr::lng_settings_theme_accent_title(tr::now),
|
||||||
|
EditColorBox::Mode::HSL,
|
||||||
|
(*selected)->color());
|
||||||
|
box->setLightnessLimits(
|
||||||
|
colorizer.lightnessMin,
|
||||||
|
colorizer.lightnessMax);
|
||||||
|
box->setSaveCallback(crl::guard(_outer, [=](QColor result) {
|
||||||
|
_selected.fire_copy(result);
|
||||||
|
}));
|
||||||
|
Ui::show(std::move(box));
|
||||||
|
}
|
||||||
|
|
||||||
rpl::producer<QColor> ColorsPalette::selected() const {
|
rpl::producer<QColor> ColorsPalette::selected() const {
|
||||||
return _selected.events();
|
return _selected.events();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ColorsPalette::updateInnerGeometry() {
|
void ColorsPalette::updateInnerGeometry() {
|
||||||
|
if (_buttons.size() < 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const auto inner = _outer->entity();
|
const auto inner = _outer->entity();
|
||||||
const auto size = st::settingsAccentColorSize;
|
const auto size = st::settingsAccentColorSize;
|
||||||
const auto padding = st::settingsButton.padding;
|
const auto padding = st::settingsButton.padding;
|
||||||
const auto width = inner->width() - padding.left() - padding.right();
|
const auto width = inner->width() - padding.left() - padding.right();
|
||||||
const auto skip = (width - size * kColorsPerRow)
|
const auto skip = (width - size * _buttons.size())
|
||||||
/ float64(kColorsPerRow - 1);
|
/ float64(_buttons.size() - 1);
|
||||||
auto y = st::settingsSectionSkip * 2;
|
const auto y = st::settingsSectionSkip * 2;
|
||||||
for (const auto &row : _buttons) {
|
auto x = float64(padding.left());
|
||||||
auto x = float64(padding.left());
|
for (const auto &button : _buttons) {
|
||||||
for (const auto &button : row) {
|
button->moveToLeft(int(std::round(x)), y);
|
||||||
button->moveToLeft(int(std::round(x)), y);
|
x += size + skip;
|
||||||
x += size + skip;
|
|
||||||
}
|
|
||||||
y += size + int(std::round(skip));
|
|
||||||
}
|
}
|
||||||
inner->resize(inner->width(), y - int(std::round(skip)));
|
inner->resize(inner->width(), y + size);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
Loading…
Add table
Reference in a new issue