| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881 |
- /*
- This file is part of Telegram Desktop,
- the official desktop application for the Telegram messaging service.
- For license and copyright information please follow this link:
- https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
- */
- #include "settings/settings_chat.h"
- #include "base/timer_rpl.h"
- #include "settings/settings_advanced.h"
- #include "settings/settings_privacy_security.h"
- #include "settings/settings_experimental.h"
- #include "settings/settings_shortcuts.h"
- #include "boxes/abstract_box.h"
- #include "boxes/peers/edit_peer_color_box.h"
- #include "boxes/connection_box.h"
- #include "boxes/auto_download_box.h"
- #include "boxes/reactions_settings_box.h"
- #include "boxes/stickers_box.h"
- #include "ui/boxes/confirm_box.h"
- #include "boxes/background_box.h"
- #include "boxes/background_preview_box.h"
- #include "boxes/download_path_box.h"
- #include "boxes/local_storage_box.h"
- #include "ui/boxes/choose_font_box.h"
- #include "ui/wrap/vertical_layout.h"
- #include "ui/wrap/slide_wrap.h"
- #include "ui/widgets/fields/input_field.h"
- #include "ui/widgets/checkbox.h"
- #include "ui/widgets/color_editor.h"
- #include "ui/widgets/buttons.h"
- #include "ui/chat/attach/attach_extensions.h"
- #include "ui/chat/chat_style.h"
- #include "ui/chat/chat_theme.h"
- #include "ui/layers/generic_box.h"
- #include "ui/effects/radial_animation.h"
- #include "ui/style/style_palette_colorizer.h"
- #include "ui/toast/toast.h"
- #include "ui/image/image.h"
- #include "ui/painter.h"
- #include "ui/vertical_list.h"
- #include "ui/ui_utility.h"
- #include "ui/widgets/menu/menu_add_action_callback.h"
- #include "history/view/history_view_quick_action.h"
- #include "lang/lang_keys.h"
- #include "export/export_manager.h"
- #include "window/themes/window_theme.h"
- #include "window/themes/window_themes_embedded.h"
- #include "window/themes/window_theme_editor_box.h"
- #include "window/themes/window_themes_cloud_list.h"
- #include "window/window_adaptive.h"
- #include "window/window_session_controller.h"
- #include "window/window_controller.h"
- #include "info/downloads/info_downloads_widget.h"
- #include "info/info_memento.h"
- #include "storage/localstorage.h"
- #include "core/file_utilities.h"
- #include "core/application.h"
- #include "data/data_session.h"
- #include "data/data_cloud_themes.h"
- #include "data/data_file_origin.h"
- #include "data/data_message_reactions.h"
- #include "data/data_peer_values.h"
- #include "data/data_user.h"
- #include "chat_helpers/emoji_sets_manager.h"
- #include "base/platform/base_platform_info.h"
- #include "base/call_delayed.h"
- #include "support/support_common.h"
- #include "support/support_templates.h"
- #include "main/main_session.h"
- #include "main/main_session_settings.h"
- #include "mainwidget.h"
- #include "styles/style_chat_helpers.h" // stickersRemove
- #include "styles/style_settings.h"
- #include "styles/style_layers.h"
- #include "styles/style_menu_icons.h"
- #include "styles/style_window.h"
- namespace Settings {
- namespace {
- const auto kSchemesList = Window::Theme::EmbeddedThemes();
- constexpr auto kCustomColorButtonParts = 7;
- class ColorsPalette final {
- public:
- using Type = Window::Theme::EmbeddedType;
- using Scheme = Window::Theme::EmbeddedScheme;
- explicit ColorsPalette(not_null<Ui::VerticalLayout*> container);
- void show(Type type);
- rpl::producer<QColor> selected() const;
- 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();
- not_null<Ui::SlideWrap<>*> _outer;
- std::vector<std::unique_ptr<Button>> _buttons;
- rpl::event_stream<QColor> _selected;
- };
- void PaintCustomButton(QPainter &p, const std::vector<QColor> &colors) {
- Expects(colors.size() >= kCustomColorButtonParts);
- p.setPen(Qt::NoPen);
- const auto size = st::settingsAccentColorSize;
- 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
- };
- 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<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::to_empty;
- }
- bool ColorsPalette::Button::selected() const {
- return _selected;
- }
- QColor ColorsPalette::Button::color() const {
- Expects(_colors.size() == 1);
- return _colors.front();
- }
- void ColorsPalette::Button::paint() {
- auto p = QPainter(&_widget);
- PainterHighQualityEnabler hq(p);
- if (_colors.size() == 1) {
- PaintRoundColorButton(
- p,
- st::settingsAccentColorSize,
- _colors.front(),
- _selectedAnimation.value(_selected ? 1. : 0.));
- } else if (_colors.size() >= kCustomColorButtonParts) {
- PaintCustomButton(p, _colors);
- }
- }
- ColorsPalette::ColorsPalette(not_null<Ui::VerticalLayout*> container)
- : _outer(container->add(
- object_ptr<Ui::SlideWrap<>>(
- container,
- object_ptr<Ui::RpWidget>(container)))) {
- _outer->hide(anim::type::instant);
- const auto inner = _outer->entity();
- inner->widthValue(
- ) | rpl::start_with_next([=] {
- updateInnerGeometry();
- }, inner->lifetime());
- }
- void ColorsPalette::show(Type type) {
- const auto scheme = ranges::find(kSchemesList, type, &Scheme::type);
- if (scheme == end(kSchemesList)) {
- _outer->hide(anim::type::instant);
- return;
- }
- auto list = Window::Theme::DefaultAccentColors(type);
- if (list.empty()) {
- _outer->hide(anim::type::instant);
- return;
- }
- list.insert(list.begin(), scheme->accentColor);
- const auto color = Core::App().settings().themesAccentColors().get(type);
- const auto current = color.value_or(scheme->accentColor);
- const auto i = ranges::find(list, current);
- 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);
- show(&*scheme, std::move(list), selected);
- const auto inner = _outer->entity();
- inner->resize(_outer->width(), inner->height());
- 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);
- Ui::show(Box([=](not_null<Ui::GenericBox*> box) {
- const auto editor = box->addRow(object_ptr<ColorEditor>(
- box,
- ColorEditor::Mode::HSL,
- (*selected)->color()));
- const auto save = crl::guard(_outer, [=] {
- _selected.fire_copy(editor->color());
- box->closeBox();
- });
- editor->submitRequests(
- ) | rpl::start_with_next(save, editor->lifetime());
- editor->setLightnessLimits(
- colorizer.lightnessMin,
- colorizer.lightnessMax);
- box->setFocusCallback([=] {
- editor->setInnerFocus();
- });
- box->addButton(tr::lng_settings_save(), save);
- box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
- box->setTitle(tr::lng_settings_theme_accent_title());
- box->setWidth(editor->width());
- }));
- }
- rpl::producer<QColor> ColorsPalette::selected() const {
- return _selected.events();
- }
- void ColorsPalette::updateInnerGeometry() {
- if (_buttons.size() < 2) {
- return;
- }
- const auto inner = _outer->entity();
- const auto size = st::settingsAccentColorSize;
- const auto padding = st::settingsButtonNoIcon.padding;
- const auto width = inner->width() - padding.left() - padding.right();
- const auto skip = (width - size * _buttons.size())
- / float64(_buttons.size() - 1);
- const auto y = st::defaultVerticalListSkip * 2;
- auto x = float64(padding.left());
- for (const auto &button : _buttons) {
- button->moveToLeft(int(base::SafeRound(x)), y);
- x += size + skip;
- }
- inner->resize(inner->width(), y + size);
- }
- } // namespace
- void PaintRoundColorButton(
- QPainter &p,
- int size,
- QBrush brush,
- float64 selected) {
- const auto rect = QRect(0, 0, size, size);
- p.setBrush(brush);
- 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 }));
- }
- }
- class BackgroundRow : public Ui::RpWidget {
- public:
- BackgroundRow(
- QWidget *parent,
- not_null<Window::SessionController*> controller);
- protected:
- void paintEvent(QPaintEvent *e) override;
- int resizeGetHeight(int newWidth) override;
- private:
- void updateImage();
- float64 radialProgress() const;
- bool radialLoading() const;
- QRect radialRect() const;
- void radialStart();
- crl::time radialTimeShift() const;
- void radialAnimationCallback(crl::time now);
- const not_null<Window::SessionController*> _controller;
- QPixmap _background;
- object_ptr<Ui::LinkButton> _chooseFromGallery;
- object_ptr<Ui::LinkButton> _chooseFromFile;
- Ui::RadialAnimation _radial;
- };
- void ChooseFromFile(
- not_null<Window::SessionController*> controller,
- not_null<QWidget*> parent);
- BackgroundRow::BackgroundRow(
- QWidget *parent,
- not_null<Window::SessionController*> controller)
- : RpWidget(parent)
- , _controller(controller)
- , _chooseFromGallery(
- this,
- tr::lng_settings_bg_from_gallery(tr::now),
- st::settingsLink)
- , _chooseFromFile(this, tr::lng_settings_bg_from_file(tr::now), st::settingsLink)
- , _radial([=](crl::time now) { radialAnimationCallback(now); }) {
- updateImage();
- _chooseFromGallery->addClickHandler([=] {
- controller->show(Box<BackgroundBox>(controller));
- });
- _chooseFromFile->addClickHandler([=] {
- ChooseFromFile(controller, this);
- });
- using Update = const Window::Theme::BackgroundUpdate;
- Window::Theme::Background()->updates(
- ) | rpl::filter([](const Update &update) {
- return (update.type == Update::Type::New
- || update.type == Update::Type::Start
- || update.type == Update::Type::Changed);
- }) | rpl::start_with_next([=] {
- updateImage();
- }, lifetime());
- }
- void BackgroundRow::paintEvent(QPaintEvent *e) {
- auto p = QPainter(this);
- const auto radial = _radial.animating();
- const auto radialOpacity = radial ? _radial.opacity() : 0.;
- if (radial) {
- const auto backThumb = _controller->content()->newBackgroundThumb();
- if (!backThumb) {
- p.drawPixmap(0, 0, _background);
- } else {
- const auto &pix = backThumb->pix(
- st::settingsBackgroundThumb,
- { .options = Images::Option::Blur });
- const auto factor = style::DevicePixelRatio();
- p.drawPixmap(
- 0,
- 0,
- st::settingsBackgroundThumb,
- st::settingsBackgroundThumb,
- pix,
- 0,
- (pix.height() - st::settingsBackgroundThumb * factor) / 2,
- st::settingsBackgroundThumb * factor,
- st::settingsBackgroundThumb * factor);
- }
- const auto outer = radialRect();
- const auto inner = QRect(
- QPoint(
- outer.x() + (outer.width() - st::radialSize.width()) / 2,
- outer.y() + (outer.height() - st::radialSize.height()) / 2),
- st::radialSize);
- p.setPen(Qt::NoPen);
- p.setOpacity(radialOpacity);
- p.setBrush(st::radialBg);
- {
- PainterHighQualityEnabler hq(p);
- p.drawEllipse(inner);
- }
- p.setOpacity(1);
- const auto arc = inner.marginsRemoved(QMargins(
- st::radialLine,
- st::radialLine,
- st::radialLine,
- st::radialLine));
- _radial.draw(p, arc, st::radialLine, st::radialFg);
- } else {
- p.drawPixmap(0, 0, _background);
- }
- }
- int BackgroundRow::resizeGetHeight(int newWidth) {
- auto linkTop = st::settingsFromGalleryTop;
- auto linkLeft = st::settingsBackgroundThumb + st::settingsThumbSkip;
- auto linkWidth = newWidth - linkLeft;
- _chooseFromGallery->resizeToWidth(
- qMin(linkWidth, _chooseFromGallery->naturalWidth()));
- _chooseFromFile->resizeToWidth(
- qMin(linkWidth, _chooseFromFile->naturalWidth()));
- _chooseFromGallery->moveToLeft(linkLeft, linkTop, newWidth);
- linkTop += _chooseFromGallery->height() + st::settingsFromFileTop;
- _chooseFromFile->moveToLeft(linkLeft, linkTop, newWidth);
- return st::settingsBackgroundThumb;
- }
- float64 BackgroundRow::radialProgress() const {
- return _controller->content()->chatBackgroundProgress();
- }
- bool BackgroundRow::radialLoading() const {
- const auto widget = _controller->content();
- if (widget->chatBackgroundLoading()) {
- widget->checkChatBackground();
- if (widget->chatBackgroundLoading()) {
- return true;
- } else {
- const_cast<BackgroundRow*>(this)->updateImage();
- }
- }
- return false;
- }
- QRect BackgroundRow::radialRect() const {
- return QRect(
- 0,
- 0,
- st::settingsBackgroundThumb,
- st::settingsBackgroundThumb);
- }
- void BackgroundRow::radialStart() {
- if (radialLoading() && !_radial.animating()) {
- _radial.start(radialProgress());
- if (const auto shift = radialTimeShift()) {
- _radial.update(
- radialProgress(),
- !radialLoading(),
- crl::now() + shift);
- }
- }
- }
- crl::time BackgroundRow::radialTimeShift() const {
- return st::radialDuration;
- }
- void BackgroundRow::radialAnimationCallback(crl::time now) {
- const auto updated = _radial.update(
- radialProgress(),
- !radialLoading(),
- now + radialTimeShift());
- if (!anim::Disabled() || updated) {
- rtlupdate(radialRect());
- }
- }
- void BackgroundRow::updateImage() {
- const auto size = st::settingsBackgroundThumb;
- const auto fullsize = size * style::DevicePixelRatio();
- const auto &background = *Window::Theme::Background();
- const auto &paper = background.paper();
- const auto &prepared = background.prepared();
- const auto preparePattern = [&] {
- const auto paintPattern = [&](QPainter &p, bool inverted) {
- if (prepared.isNull()) {
- return;
- }
- const auto w = prepared.width();
- const auto h = prepared.height();
- const auto s = [&] {
- const auto scaledw = w * st::windowMinHeight / h;
- const auto result = (w * size) / scaledw;
- return std::min({ result, w, h });
- }();
- auto small = prepared.copy((w - s) / 2, (h - s) / 2, s, s);
- if (inverted) {
- small = Ui::InvertPatternImage(std::move(small));
- }
- p.drawImage(QRect(0, 0, fullsize, fullsize), small);
- };
- return Ui::GenerateBackgroundImage(
- { fullsize, fullsize },
- paper.backgroundColors(),
- paper.gradientRotation(),
- paper.patternOpacity(),
- paintPattern);
- };
- const auto prepareNormal = [&] {
- auto result = QImage(
- QSize{ fullsize, fullsize },
- QImage::Format_ARGB32_Premultiplied);
- result.setDevicePixelRatio(style::DevicePixelRatio());
- if (const auto color = background.colorForFill()) {
- result.fill(*color);
- return result;
- } else if (prepared.isNull()) {
- result.fill(Qt::transparent);
- return result;
- }
- auto p = QPainter(&result);
- PainterHighQualityEnabler hq(p);
- const auto w = prepared.width();
- const auto h = prepared.height();
- const auto s = std::min(w, h);
- p.drawImage(
- QRect(0, 0, size, size),
- prepared,
- QRect((w - s) / 2, (h - s) / 2, s, s));
- p.end();
- return result;
- };
- auto back = (paper.isPattern() || !background.gradientForFill().isNull())
- ? preparePattern()
- : prepareNormal();
- _background = Ui::PixmapFromImage(
- Images::Round(std::move(back), ImageRoundRadius::Small));
- _background.setDevicePixelRatio(style::DevicePixelRatio());
- rtlupdate(radialRect());
- if (radialLoading()) {
- radialStart();
- }
- }
- void ChooseFromFile(
- not_null<Window::SessionController*> controller,
- not_null<QWidget*> parent) {
- auto filters = QStringList(
- u"Theme files (*.tdesktop-theme *.tdesktop-palette *"_q
- + Ui::ImageExtensions().join(u" *"_q)
- + u")"_q);
- filters.push_back(FileDialog::AllFilesFilter());
- const auto callback = crl::guard(controller, [=](
- const FileDialog::OpenResult &result) {
- if (result.paths.isEmpty() && result.remoteContent.isEmpty()) {
- return;
- }
- if (!result.paths.isEmpty()) {
- const auto filePath = result.paths.front();
- const auto hasExtension = [&](QLatin1String extension) {
- return filePath.endsWith(extension, Qt::CaseInsensitive);
- };
- if (hasExtension(qstr(".tdesktop-theme"))
- || hasExtension(qstr(".tdesktop-palette"))) {
- Window::Theme::Apply(filePath);
- return;
- }
- }
- auto image = Images::Read({
- .path = result.paths.isEmpty() ? QString() : result.paths.front(),
- .content = result.remoteContent,
- .forceOpaque = true,
- }).image;
- if (image.isNull() || image.width() <= 0 || image.height() <= 0) {
- return;
- }
- auto local = Data::CustomWallPaper();
- local.setLocalImageAsThumbnail(std::make_shared<Image>(
- std::move(image)));
- controller->show(Box<BackgroundPreviewBox>(controller, local));
- });
- FileDialog::GetOpenPath(
- parent.get(),
- tr::lng_choose_image(tr::now),
- filters.join(u";;"_q),
- crl::guard(parent, callback));
- }
- void SetupStickersEmoji(
- not_null<Window::SessionController*> controller,
- not_null<Ui::VerticalLayout*> container) {
- Ui::AddSkip(container);
- Ui::AddSubsectionTitle(container, tr::lng_settings_stickers_emoji());
- const auto session = &controller->session();
- auto wrap = object_ptr<Ui::VerticalLayout>(container);
- const auto inner = wrap.data();
- container->add(object_ptr<Ui::OverrideMargins>(
- container,
- std::move(wrap),
- QMargins(0, 0, 0, st::settingsCheckbox.margin.bottom())));
- const auto checkbox = [&](const QString &label, bool checked) {
- return object_ptr<Ui::Checkbox>(
- container,
- label,
- checked,
- st::settingsCheckbox);
- };
- const auto add = [&](const QString &label, bool checked, auto &&handle) {
- inner->add(
- checkbox(label, checked),
- st::settingsCheckboxPadding
- )->checkedChanges(
- ) | rpl::start_with_next(
- std::move(handle),
- inner->lifetime());
- };
- const auto addSliding = [&](
- const QString &label,
- bool checked,
- auto &&handle,
- rpl::producer<bool> shown) {
- inner->add(
- object_ptr<Ui::SlideWrap<Ui::Checkbox>>(
- inner,
- checkbox(label, checked),
- st::settingsCheckboxPadding)
- )->setDuration(0)->toggleOn(std::move(shown))->entity()->checkedChanges(
- ) | rpl::start_with_next(
- std::move(handle),
- inner->lifetime());
- };
- add(
- tr::lng_settings_large_emoji(tr::now),
- Core::App().settings().largeEmoji(),
- [=](bool checked) {
- Core::App().settings().setLargeEmoji(checked);
- Core::App().saveSettingsDelayed();
- });
- add(
- tr::lng_settings_replace_emojis(tr::now),
- Core::App().settings().replaceEmoji(),
- [=](bool checked) {
- Core::App().settings().setReplaceEmoji(checked);
- Core::App().saveSettingsDelayed();
- });
- const auto suggestEmoji = inner->lifetime().make_state<
- rpl::variable<bool>
- >(Core::App().settings().suggestEmoji());
- add(
- tr::lng_settings_suggest_emoji(tr::now),
- Core::App().settings().suggestEmoji(),
- [=](bool checked) {
- *suggestEmoji = checked;
- Core::App().settings().setSuggestEmoji(checked);
- Core::App().saveSettingsDelayed();
- });
- using namespace rpl::mappers;
- addSliding(
- tr::lng_settings_suggest_animated_emoji(tr::now),
- Core::App().settings().suggestAnimatedEmoji(),
- [=](bool checked) {
- Core::App().settings().setSuggestAnimatedEmoji(checked);
- Core::App().saveSettingsDelayed();
- },
- rpl::combine(
- Data::AmPremiumValue(session),
- suggestEmoji->value(),
- _1 && _2));
- add(
- tr::lng_settings_suggest_by_emoji(tr::now),
- Core::App().settings().suggestStickersByEmoji(),
- [=](bool checked) {
- Core::App().settings().setSuggestStickersByEmoji(checked);
- Core::App().saveSettingsDelayed();
- });
- add(
- tr::lng_settings_loop_stickers(tr::now),
- Core::App().settings().loopAnimatedStickers(),
- [=](bool checked) {
- Core::App().settings().setLoopAnimatedStickers(checked);
- Core::App().saveSettingsDelayed();
- });
- AddButtonWithIcon(
- container,
- tr::lng_stickers_you_have(),
- st::settingsButton,
- { &st::menuIconStickers }
- )->addClickHandler([=] {
- controller->show(Box<StickersBox>(
- controller->uiShow(),
- StickersBox::Section::Installed));
- });
- AddButtonWithIcon(
- container,
- tr::lng_emoji_manage_sets(),
- st::settingsButton,
- { &st::menuIconEmoji }
- )->addClickHandler([=] {
- controller->show(Box<Ui::Emoji::ManageSetsBox>(session));
- });
- Ui::AddSkip(container, st::settingsCheckboxesSkip);
- }
- void SetupMessages(
- not_null<Window::SessionController*> controller,
- not_null<Ui::VerticalLayout*> container) {
- Ui::AddDivider(container);
- Ui::AddSkip(container);
- Ui::AddSubsectionTitle(container, tr::lng_settings_messages());
- Ui::AddSkip(container, st::settingsSendTypeSkip);
- using SendByType = Ui::InputSubmitSettings;
- using Quick = HistoryView::DoubleClickQuickAction;
- const auto skip = st::settingsSendTypeSkip;
- auto wrap = object_ptr<Ui::VerticalLayout>(container);
- const auto inner = wrap.data();
- container->add(
- object_ptr<Ui::OverrideMargins>(
- container,
- std::move(wrap),
- QMargins(0, skip, 0, skip)));
- const auto groupSend = std::make_shared<Ui::RadioenumGroup<SendByType>>(
- Core::App().settings().sendSubmitWay());
- const auto addSend = [&](SendByType value, const QString &text) {
- inner->add(
- object_ptr<Ui::Radioenum<SendByType>>(
- inner,
- groupSend,
- value,
- text,
- st::settingsSendType),
- st::settingsSendTypePadding);
- };
- addSend(SendByType::Enter, tr::lng_settings_send_enter(tr::now));
- addSend(
- SendByType::CtrlEnter,
- (Platform::IsMac()
- ? tr::lng_settings_send_cmdenter(tr::now)
- : tr::lng_settings_send_ctrlenter(tr::now)));
- groupSend->setChangedCallback([=](SendByType value) {
- Core::App().settings().setSendSubmitWay(value);
- Core::App().saveSettingsDelayed();
- });
- Ui::AddSkip(inner, st::settingsCheckboxesSkip);
- const auto groupQuick = std::make_shared<Ui::RadioenumGroup<Quick>>(
- Core::App().settings().chatQuickAction());
- const auto addQuick = [&](Quick value, const QString &text) {
- return inner->add(
- object_ptr<Ui::Radioenum<Quick>>(
- inner,
- groupQuick,
- value,
- text,
- st::settingsSendType),
- st::settingsSendTypePadding);
- };
- addQuick(Quick::Reply, tr::lng_settings_chat_quick_action_reply(tr::now));
- const auto react = addQuick(
- Quick::React,
- tr::lng_settings_chat_quick_action_react(tr::now));
- const auto buttonRight = Ui::CreateSimpleCircleButton(
- inner,
- st::stickersRemove.ripple);
- buttonRight->resize(st::stickersRemove.width, st::stickersRemove.height);
- const auto toggleButtonRight = [=](bool value) {
- buttonRight->setAttribute(Qt::WA_TransparentForMouseEvents, !value);
- };
- toggleButtonRight(false);
- struct State {
- struct {
- std::vector<rpl::lifetime> lifetimes;
- bool flag = false;
- } icons;
- };
- const auto state = buttonRight->lifetime().make_state<State>();
- state->icons.lifetimes = std::vector<rpl::lifetime>(2);
- const auto &reactions = controller->session().data().reactions();
- auto idValue = rpl::single(
- reactions.favoriteId()
- ) | rpl::then(
- reactions.favoriteUpdates() | rpl::map([=] {
- return controller->session().data().reactions().favoriteId();
- })
- ) | rpl::filter([](const Data::ReactionId &id) {
- return !id.empty();
- });
- auto selected = rpl::duplicate(idValue);
- std::move(
- selected
- ) | rpl::start_with_next([=, idValue = std::move(idValue)](
- const Data::ReactionId &id) {
- const auto index = state->icons.flag ? 1 : 0;
- const auto iconSize = st::settingsReactionRightIcon;
- const auto &reactions = controller->session().data().reactions();
- const auto &list = reactions.list(Data::Reactions::Type::All);
- const auto i = ranges::find(list, id, &Data::Reaction::id);
- state->icons.lifetimes[index] = rpl::lifetime();
- if (i != end(list)) {
- AddReactionAnimatedIcon(
- inner,
- buttonRight->geometryValue(
- ) | rpl::map([=](const QRect &r) {
- return QPoint(
- r.left() + (r.width() - iconSize) / 2,
- r.top() + (r.height() - iconSize) / 2);
- }),
- iconSize,
- *i,
- buttonRight->events(
- ) | rpl::filter([=](not_null<QEvent*> event) {
- return event->type() == QEvent::Enter;
- }) | rpl::to_empty,
- rpl::duplicate(idValue) | rpl::skip(1) | rpl::to_empty,
- &state->icons.lifetimes[index]);
- } else if (const auto customId = id.custom()) {
- AddReactionCustomIcon(
- inner,
- buttonRight->geometryValue(
- ) | rpl::map([=](const QRect &r) {
- return QPoint(
- r.left() + (r.width() - iconSize) / 2,
- r.top() + (r.height() - iconSize) / 2);
- }),
- iconSize,
- controller,
- customId,
- rpl::duplicate(idValue) | rpl::skip(1) | rpl::to_empty,
- &state->icons.lifetimes[index]);
- }
- state->icons.flag = !state->icons.flag;
- toggleButtonRight(true);
- }, buttonRight->lifetime());
- react->geometryValue(
- ) | rpl::start_with_next([=](const QRect &r) {
- const auto rightSize = buttonRight->size();
- buttonRight->moveToRight(
- st::settingsButtonRightSkip,
- r.y() + (r.height() - rightSize.height()) / 2);
- }, buttonRight->lifetime());
- groupQuick->setChangedCallback([=](Quick value) {
- Core::App().settings().setChatQuickAction(value);
- Core::App().saveSettingsDelayed();
- });
- buttonRight->setClickedCallback([=, show = controller->uiShow()] {
- show->showBox(Box(ReactionsSettingsBox, controller));
- });
- Ui::AddSkip(inner, st::settingsSendTypeSkip);
- inner->add(
- object_ptr<Ui::Checkbox>(
- inner,
- tr::lng_settings_chat_corner_reaction(tr::now),
- Core::App().settings().cornerReaction(),
- st::settingsCheckbox),
- st::settingsCheckboxPadding
- )->checkedChanges(
- ) | rpl::start_with_next([=](bool checked) {
- Core::App().settings().setCornerReaction(checked);
- Core::App().saveSettingsDelayed();
- }, inner->lifetime());
- Ui::AddSkip(inner);
- }
- void SetupArchive(
- not_null<Window::SessionController*> controller,
- not_null<Ui::VerticalLayout*> container,
- Fn<void(Type)> showOther) {
- Ui::AddSkip(container);
- AddButtonWithIcon(
- container,
- tr::lng_settings_shortcuts(),
- st::settingsButton,
- { &st::menuIconShortcut }
- )->addClickHandler([=] {
- showOther(Shortcuts::Id());
- });
- PreloadArchiveSettings(&controller->session());
- AddButtonWithIcon(
- container,
- tr::lng_context_archive_settings(),
- st::settingsButton,
- { &st::menuIconArchive }
- )->addClickHandler([=] {
- controller->show(Box(Settings::ArchiveSettingsBox, controller));
- });
- }
- void SetupExport(
- not_null<Window::SessionController*> controller,
- not_null<Ui::VerticalLayout*> container,
- Fn<void(Type)> showOther) {
- AddButtonWithIcon(
- container,
- tr::lng_settings_export_data(),
- st::settingsButton,
- { &st::menuIconExport }
- )->addClickHandler([=] {
- const auto session = &controller->session();
- controller->window().hideSettingsAndLayer();
- base::call_delayed(
- st::boxDuration,
- session,
- [=] { Core::App().exportManager().start(session); });
- });
- AddButtonWithIcon(
- container,
- tr::lng_settings_experimental(),
- st::settingsButton,
- { &st::menuIconExperimental }
- )->addClickHandler([=] {
- showOther(Experimental::Id());
- });
- }
- void SetupLocalStorage(
- not_null<Window::SessionController*> controller,
- not_null<Ui::VerticalLayout*> container) {
- AddButtonWithIcon(
- container,
- tr::lng_settings_manage_local_storage(),
- st::settingsButton,
- { &st::menuIconStorage }
- )->addClickHandler([=] { LocalStorageBox::Show(controller); });
- }
- void SetupDataStorage(
- not_null<Window::SessionController*> controller,
- not_null<Ui::VerticalLayout*> container) {
- using namespace rpl::mappers;
- Ui::AddSkip(container);
- Ui::AddSubsectionTitle(container, tr::lng_settings_data_storage());
- SetupConnectionType(
- &controller->window(),
- &controller->session().account(),
- container);
- #ifndef OS_WIN_STORE
- const auto showpath = container->lifetime(
- ).make_state<rpl::event_stream<bool>>();
- const auto path = container->add(
- object_ptr<Ui::SlideWrap<Button>>(
- container,
- CreateButtonWithIcon(
- container,
- tr::lng_download_path(),
- st::settingsButton,
- { &st::menuIconShowInFolder })));
- auto pathtext = Core::App().settings().downloadPathValue(
- ) | rpl::map([](const QString &text) {
- if (text.isEmpty()) {
- return Core::App().canReadDefaultDownloadPath()
- ? tr::lng_download_path_default(tr::now)
- : tr::lng_download_path_temp(tr::now);
- } else if (text == FileDialog::Tmp()) {
- return tr::lng_download_path_temp(tr::now);
- }
- return QDir::toNativeSeparators(text);
- });
- CreateRightLabel(
- path->entity(),
- std::move(pathtext),
- st::settingsButton,
- tr::lng_download_path());
- path->entity()->addClickHandler([=] {
- controller->show(Box<DownloadPathBox>(controller));
- });
- #endif // OS_WIN_STORE
- SetupLocalStorage(controller, container);
- AddButtonWithIcon(
- container,
- tr::lng_downloads_section(),
- st::settingsButton,
- { &st::menuIconDownload }
- )->setClickedCallback([=] {
- controller->showSection(
- Info::Downloads::Make(controller->session().user()));
- });
- const auto ask = container->add(object_ptr<Ui::SettingsButton>(
- container,
- tr::lng_download_path_ask(),
- st::settingsButtonNoIcon
- ))->toggleOn(rpl::single(Core::App().settings().askDownloadPath()));
- ask->toggledValue(
- ) | rpl::filter([](bool checked) {
- return (checked != Core::App().settings().askDownloadPath());
- }) | rpl::start_with_next([=](bool checked) {
- Core::App().settings().setAskDownloadPath(checked);
- Core::App().saveSettingsDelayed();
- #ifndef OS_WIN_STORE
- showpath->fire_copy(!checked);
- #endif // OS_WIN_STORE
- }, ask->lifetime());
- #ifndef OS_WIN_STORE
- path->toggleOn(ask->toggledValue() | rpl::map(!_1));
- #endif // OS_WIN_STORE
- Ui::AddSkip(container, st::settingsCheckboxesSkip);
- }
- void SetupAutoDownload(
- not_null<Window::SessionController*> controller,
- not_null<Ui::VerticalLayout*> container) {
- Ui::AddDivider(container);
- Ui::AddSkip(container);
- Ui::AddSubsectionTitle(container, tr::lng_media_auto_settings());
- using Source = Data::AutoDownload::Source;
- const auto add = [&](
- rpl::producer<QString> label,
- Source source,
- IconDescriptor &&descriptor) {
- AddButtonWithIcon(
- container,
- std::move(label),
- st::settingsButton,
- std::move(descriptor)
- )->addClickHandler([=] {
- controller->show(
- Box<AutoDownloadBox>(&controller->session(), source));
- });
- };
- add(
- tr::lng_media_auto_in_private(),
- Source::User,
- { &st::menuIconProfile });
- add(
- tr::lng_media_auto_in_groups(),
- Source::Group,
- { &st::menuIconGroups });
- add(
- tr::lng_media_auto_in_channels(),
- Source::Channel,
- { &st::menuIconChannel });
- Ui::AddSkip(container, st::settingsCheckboxesSkip);
- }
- void SetupChatBackground(
- not_null<Window::SessionController*> controller,
- not_null<Ui::VerticalLayout*> container) {
- Ui::AddDivider(container);
- Ui::AddSkip(container);
- Ui::AddSubsectionTitle(container, tr::lng_settings_section_background());
- container->add(
- object_ptr<BackgroundRow>(container, controller),
- st::settingsBackgroundPadding);
- const auto skipTop = st::settingsCheckbox.margin.top();
- const auto skipBottom = st::settingsCheckbox.margin.bottom();
- auto wrap = object_ptr<Ui::VerticalLayout>(container);
- const auto inner = wrap.data();
- container->add(
- object_ptr<Ui::OverrideMargins>(
- container,
- std::move(wrap),
- QMargins(0, skipTop, 0, skipBottom)));
- Ui::AddSkip(container, st::settingsTileSkip);
- const auto background = Window::Theme::Background();
- const auto tile = inner->add(
- object_ptr<Ui::SlideWrap<Ui::Checkbox>>(
- inner,
- object_ptr<Ui::Checkbox>(
- inner,
- tr::lng_settings_bg_tile(tr::now),
- background->tile(),
- st::settingsCheckbox),
- st::settingsSendTypePadding));
- const auto adaptive = inner->add(
- object_ptr<Ui::SlideWrap<Ui::Checkbox>>(
- inner,
- object_ptr<Ui::Checkbox>(
- inner,
- tr::lng_settings_adaptive_wide(tr::now),
- Core::App().settings().adaptiveForWide(),
- st::settingsCheckbox),
- st::settingsSendTypePadding));
- tile->entity()->checkedChanges(
- ) | rpl::start_with_next([=](bool checked) {
- background->setTile(checked);
- }, tile->lifetime());
- const auto shown = [=] {
- return !background->paper().isPattern()
- && !background->colorForFill();
- };
- tile->toggle(shown(), anim::type::instant);
- using Update = const Window::Theme::BackgroundUpdate;
- background->updates(
- ) | rpl::filter([](const Update &update) {
- return (update.type == Update::Type::Changed)
- || (update.type == Update::Type::New);
- }) | rpl::start_with_next([=] {
- tile->entity()->setChecked(background->tile());
- tile->toggle(shown(), anim::type::instant);
- }, tile->lifetime());
- adaptive->toggleOn(controller->adaptive().chatLayoutValue(
- ) | rpl::map([](Window::Adaptive::ChatLayout layout) {
- return (layout == Window::Adaptive::ChatLayout::Wide);
- }));
- adaptive->entity()->checkedChanges(
- ) | rpl::start_with_next([=](bool checked) {
- Core::App().settings().setAdaptiveForWide(checked);
- Core::App().saveSettingsDelayed();
- }, adaptive->lifetime());
- }
- void SetupChatListSwipe(
- not_null<Window::SessionController*> controller,
- not_null<Ui::VerticalLayout*> container) {
- Ui::AddDivider(container);
- Ui::AddSkip(container);
- Ui::AddSubsectionTitle(
- container,
- tr::lng_settings_quick_dialog_action_title());
- using Type = Dialogs::Ui::QuickDialogAction;
- const auto group = std::make_shared<Ui::RadioenumGroup<Type>>(
- Core::App().settings().quickDialogAction());
- group->setChangedCallback([=](Type value) {
- Core::App().settings().setQuickDialogAction(value);
- Core::App().saveSettings();
- });
- container->add(
- object_ptr<Ui::SettingsButton>(
- container,
- group->value() | rpl::map([](Type value) {
- return ((value == Dialogs::Ui::QuickDialogAction::Mute)
- ? tr::lng_settings_quick_dialog_action_mute
- : (value == Dialogs::Ui::QuickDialogAction::Pin)
- ? tr::lng_settings_quick_dialog_action_pin
- : (value == Dialogs::Ui::QuickDialogAction::Read)
- ? tr::lng_settings_quick_dialog_action_read
- : (value == Dialogs::Ui::QuickDialogAction::Archive)
- ? tr::lng_settings_quick_dialog_action_archive
- : tr::lng_settings_quick_dialog_action_disabled)();
- }) | rpl::flatten_latest(),
- st::settingsButtonNoIcon)
- )->setClickedCallback([=] {
- controller->uiShow()->showBox(Box([=](not_null<Ui::GenericBox*> box) {
- box->setTitle(tr::lng_settings_quick_dialog_action_title());
- const auto addRadio = [&](Type value, tr::phrase<> phrase) {
- box->verticalLayout()->add(
- object_ptr<Ui::Radioenum<Type>>(
- box->verticalLayout(),
- group,
- value,
- phrase(tr::now),
- st::settingsSendType),
- st::settingsSendTypePadding);
- };
- addRadio(Type::Mute, tr::lng_settings_quick_dialog_action_mute);
- addRadio(Type::Pin, tr::lng_settings_quick_dialog_action_pin);
- addRadio(Type::Read, tr::lng_settings_quick_dialog_action_read);
- addRadio(
- Type::Archive,
- tr::lng_settings_quick_dialog_action_archive);
- addRadio(
- Type::Delete,
- tr::lng_settings_quick_dialog_action_delete);
- addRadio(
- Type::Disabled,
- tr::lng_settings_quick_dialog_action_disabled);
- box->addButton(tr::lng_box_ok(), [=] { box->closeBox(); });
- }));
- });
- Ui::AddSkip(container);
- Ui::AddDividerText(
- container,
- tr::lng_settings_quick_dialog_action_about());
- Ui::AddSkip(container);
- }
- void SetupDefaultThemes(
- not_null<Window::Controller*> window,
- not_null<Ui::VerticalLayout*> container) {
- using Type = Window::Theme::EmbeddedType;
- using Scheme = Window::Theme::EmbeddedScheme;
- using Check = Window::Theme::CloudListCheck;
- using namespace Window::Theme;
- const auto block = container->add(object_ptr<Ui::FixedHeightWidget>(
- container));
- const auto palette = Ui::CreateChild<ColorsPalette>(
- container.get(),
- container.get());
- const auto chosen = [] {
- const auto &object = Background()->themeObject();
- if (object.cloud.id) {
- return Type(-1);
- }
- for (const auto &scheme : kSchemesList) {
- if (object.pathAbsolute == scheme.path) {
- return scheme.type;
- }
- }
- return Type(-1);
- };
- const auto group = std::make_shared<Ui::RadioenumGroup<Type>>(chosen());
- const auto apply = [=](const Scheme &scheme) {
- const auto isNight = [](const Scheme &scheme) {
- const auto type = scheme.type;
- return (type != Type::DayBlue) && (type != Type::Default);
- };
- const auto currentlyIsCustom = (chosen() == Type(-1))
- && !Background()->themeObject().cloud.id;
- const auto keep = [=] {
- if (!currentlyIsCustom) {
- KeepApplied();
- }
- };
- if (IsNightMode() == isNight(scheme)) {
- ApplyDefaultWithPath(scheme.path);
- keep();
- } else {
- Window::Theme::ToggleNightModeWithConfirmation(
- window,
- [=, path = scheme.path] { ToggleNightMode(path); keep();});
- }
- };
- const auto schemeClicked = [=](
- const Scheme &scheme,
- Qt::KeyboardModifiers modifiers) {
- apply(scheme);
- };
- auto checks = base::flat_map<Type,not_null<Check*>>();
- auto buttons = ranges::views::all(
- kSchemesList
- ) | ranges::views::transform([&](const Scheme &scheme) {
- auto check = std::make_unique<Check>(
- ColorsFromScheme(scheme),
- false);
- const auto weak = check.get();
- const auto result = Ui::CreateChild<Ui::Radioenum<Type>>(
- block,
- group,
- scheme.type,
- QString(),
- st::settingsTheme,
- std::move(check));
- rpl::duplicate(
- scheme.name
- ) | rpl::start_with_next([=](const QString &themeName) {
- result->setText(themeName);
- }, result->lifetime());
- result->addClickHandler([=] {
- schemeClicked(scheme, result->clickModifiers());
- });
- weak->setUpdateCallback([=] { result->update(); });
- checks.emplace(scheme.type, weak);
- return result;
- }) | ranges::to_vector;
- const auto refreshColorizer = [=](Type type) {
- if (type == chosen()) {
- palette->show(type);
- }
- const auto &colors = Core::App().settings().themesAccentColors();
- const auto i = checks.find(type);
- const auto scheme = ranges::find(kSchemesList, type, &Scheme::type);
- if (scheme == end(kSchemesList)) {
- return;
- }
- if (i != end(checks)) {
- if (const auto color = colors.get(type)) {
- const auto colorizer = ColorizerFrom(*scheme, *color);
- i->second->setColors(ColorsFromScheme(*scheme, colorizer));
- } else {
- i->second->setColors(ColorsFromScheme(*scheme));
- }
- }
- };
- group->setChangedCallback([=](Type type) {
- group->setValue(chosen());
- });
- for (const auto &scheme : kSchemesList) {
- refreshColorizer(scheme.type);
- }
- Background()->updates(
- ) | rpl::filter([](const BackgroundUpdate &update) {
- return (update.type == BackgroundUpdate::Type::ApplyingTheme);
- }) | rpl::map([=] {
- return chosen();
- }) | rpl::start_with_next([=](Type type) {
- refreshColorizer(type);
- group->setValue(type);
- }, container->lifetime());
- for (const auto button : buttons) {
- button->setCheckAlignment(style::al_top);
- button->resizeToWidth(button->width());
- }
- block->resize(block->width(), buttons[0]->height());
- block->widthValue(
- ) | rpl::start_with_next([buttons = std::move(buttons)](int width) {
- Expects(!buttons.empty());
- const auto padding = st::settingsButtonNoIcon.padding;
- width -= padding.left() + padding.right();
- const auto desired = st::settingsThemePreviewSize.width();
- const auto count = int(buttons.size());
- const auto skips = count - 1;
- const auto minSkip = st::settingsThemeMinSkip;
- const auto single = [&] {
- if (width >= skips * minSkip + count * desired) {
- return desired;
- }
- return (width - skips * minSkip) / count;
- }();
- if (single <= 0) {
- return;
- }
- const auto fullSkips = width - count * single;
- const auto skip = fullSkips / float64(skips);
- auto left = padding.left() + 0.;
- for (const auto button : buttons) {
- button->resizeToWidth(single);
- button->moveToLeft(int(base::SafeRound(left)), 0);
- left += button->width() + skip;
- }
- }, block->lifetime());
- palette->selected(
- ) | rpl::start_with_next([=](QColor color) {
- if (Background()->editingTheme()) {
- // We don't remember old accent color to revert it properly
- // in Window::Theme::Revert which is called by Editor.
- //
- // So we check here, before we change the saved accent color.
- window->show(Ui::MakeInformBox(
- tr::lng_theme_editor_cant_change_theme()));
- return;
- }
- const auto type = chosen();
- const auto scheme = ranges::find(kSchemesList, type, &Scheme::type);
- if (scheme == end(kSchemesList)) {
- return;
- }
- auto &colors = Core::App().settings().themesAccentColors();
- if (colors.get(type) != color) {
- colors.set(type, color);
- Local::writeSettings();
- }
- apply(*scheme);
- }, container->lifetime());
- Ui::AddSkip(container);
- }
- void SetupThemeOptions(
- not_null<Window::SessionController*> controller,
- not_null<Ui::VerticalLayout*> container) {
- using namespace Window::Theme;
- Ui::AddSkip(container, st::settingsPrivacySkip);
- Ui::AddSubsectionTitle(container, tr::lng_settings_themes());
- Ui::AddSkip(container, st::settingsThemesTopSkip);
- SetupDefaultThemes(&controller->window(), container);
- Ui::AddSkip(container);
- }
- void SetupCloudThemes(
- not_null<Window::SessionController*> controller,
- not_null<Ui::VerticalLayout*> container) {
- using namespace Window::Theme;
- using namespace rpl::mappers;
- const auto wrap = container->add(
- object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
- container,
- object_ptr<Ui::VerticalLayout>(container))
- )->setDuration(0);
- const auto inner = wrap->entity();
- Ui::AddDivider(inner);
- Ui::AddSkip(inner, st::settingsPrivacySkip);
- const auto title = AddSubsectionTitle(
- inner,
- tr::lng_settings_bg_cloud_themes());
- const auto showAll = Ui::CreateChild<Ui::LinkButton>(
- inner,
- tr::lng_settings_bg_show_all(tr::now));
- rpl::combine(
- title->topValue(),
- inner->widthValue(),
- showAll->widthValue()
- ) | rpl::start_with_next([=](int top, int outerWidth, int width) {
- showAll->moveToRight(
- st::defaultSubsectionTitlePadding.left(),
- top,
- outerWidth);
- }, showAll->lifetime());
- Ui::AddSkip(inner, st::settingsThemesTopSkip);
- const auto list = inner->lifetime().make_state<CloudList>(
- inner,
- controller);
- inner->add(
- list->takeWidget(),
- style::margins(
- st::settingsButtonNoIcon.padding.left(),
- 0,
- st::settingsButtonNoIcon.padding.right(),
- 0));
- list->allShown(
- ) | rpl::start_with_next([=](bool shown) {
- showAll->setVisible(!shown);
- }, showAll->lifetime());
- showAll->addClickHandler([=] {
- list->showAll();
- });
- const auto editWrap = inner->add(
- object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
- inner,
- object_ptr<Ui::VerticalLayout>(inner))
- )->setDuration(0);
- const auto edit = editWrap->entity();
- Ui::AddSkip(edit, st::settingsThemesBottomSkip);
- AddButtonWithIcon(
- edit,
- tr::lng_settings_bg_theme_edit(),
- st::settingsButton,
- { &st::menuIconPalette }
- )->addClickHandler([=] {
- StartEditor(
- &controller->window(),
- Background()->themeObject().cloud);
- });
- editWrap->toggleOn(rpl::single(BackgroundUpdate(
- BackgroundUpdate::Type::ApplyingTheme,
- Background()->tile()
- )) | rpl::then(
- Background()->updates()
- ) | rpl::filter([](const BackgroundUpdate &update) {
- return (update.type == BackgroundUpdate::Type::ApplyingTheme);
- }) | rpl::map([=] {
- const auto userId = controller->session().userId();
- return (Background()->themeObject().cloud.createdBy == userId);
- }));
- Ui::AddSkip(inner, 2 * st::defaultVerticalListSkip);
- wrap->setDuration(0)->toggleOn(list->empty() | rpl::map(!_1));
- }
- void SetupThemeSettings(
- not_null<Window::SessionController*> controller,
- not_null<Ui::VerticalLayout*> container) {
- Ui::AddDivider(container);
- Ui::AddSkip(container, st::settingsPrivacySkip);
- Ui::AddSubsectionTitle(container, tr::lng_settings_theme_settings());
- AddPeerColorButton(
- container,
- controller->uiShow(),
- controller->session().user(),
- st::settingsColorButton);
- const auto settings = &Core::App().settings();
- if (settings->systemDarkMode().has_value()) {
- auto label = settings->systemDarkModeEnabledValue(
- ) | rpl::map([=](bool enabled) {
- return enabled
- ? tr::lng_settings_auto_night_mode_on()
- : tr::lng_settings_auto_night_mode_off();
- }) | rpl::flatten_latest();
- AddButtonWithLabel(
- container,
- tr::lng_settings_auto_night_mode(),
- std::move(label),
- st::settingsButton,
- { &st::menuIconNightMode }
- )->setClickedCallback([=] {
- const auto now = !settings->systemDarkModeEnabled();
- if (now && Window::Theme::Background()->editingTheme()) {
- controller->show(Ui::MakeInformBox(
- tr::lng_theme_editor_cant_change_theme()));
- } else {
- settings->setSystemDarkModeEnabled(now);
- Core::App().saveSettingsDelayed();
- }
- });
- }
- const auto family = container->lifetime().make_state<
- rpl::variable<QString>
- >(settings->customFontFamily());
- auto label = family->value() | rpl::map([](QString family) {
- return family.isEmpty()
- ? tr::lng_font_default(tr::now)
- : (family == style::SystemFontTag())
- ? tr::lng_font_system(tr::now)
- : family;
- });
- AddButtonWithLabel(
- container,
- tr::lng_settings_font_family(),
- std::move(label),
- st::settingsButton,
- { &st::menuIconFont }
- )->setClickedCallback([=] {
- const auto save = [=](QString chosen) {
- *family = chosen;
- settings->setCustomFontFamily(chosen);
- Local::writeSettings();
- Core::Restart();
- };
- const auto theme = std::shared_ptr<Ui::ChatTheme>(
- Window::Theme::DefaultChatThemeOn(container->lifetime()));
- const auto generateBg = [=] {
- const auto size = st::boxWidth;
- const auto ratio = style::DevicePixelRatio();
- auto result = QImage(
- QSize(size, size) * ratio,
- QImage::Format_ARGB32_Premultiplied);
- auto p = QPainter(&result);
- Window::SectionWidget::PaintBackground(
- p,
- theme.get(),
- QSize(size, size * 3),
- QRect(0, 0, size, size));
- p.end();
- return result;
- };
- controller->show(
- Box(Ui::ChooseFontBox, generateBg, family->current(), save));
- });
- Ui::AddSkip(container, st::settingsCheckboxesSkip);
- }
- void SetupSupportSwitchSettings(
- not_null<Window::SessionController*> controller,
- not_null<Ui::VerticalLayout*> container) {
- using SwitchType = Support::SwitchSettings;
- const auto group = std::make_shared<Ui::RadioenumGroup<SwitchType>>(
- controller->session().settings().supportSwitch());
- const auto add = [&](SwitchType value, const QString &label) {
- container->add(
- object_ptr<Ui::Radioenum<SwitchType>>(
- container,
- group,
- value,
- label,
- st::settingsSendType),
- st::settingsSendTypePadding);
- };
- add(SwitchType::None, "Just send the reply");
- add(SwitchType::Next, "Send and switch to next");
- add(SwitchType::Previous, "Send and switch to previous");
- group->setChangedCallback([=](SwitchType value) {
- controller->session().settings().setSupportSwitch(value);
- controller->session().saveSettingsDelayed();
- });
- }
- void SetupSupportChatsLimitSlice(
- not_null<Window::SessionController*> controller,
- not_null<Ui::VerticalLayout*> container) {
- constexpr auto kDayDuration = 24 * 60 * 60;
- struct Option {
- int days = 0;
- QString label;
- };
- const auto options = std::vector<Option>{
- { 1, "1 day" },
- { 7, "1 week" },
- { 30, "1 month" },
- { 365, "1 year" },
- { 0, "All of them" },
- };
- const auto current = controller->session().settings().supportChatsTimeSlice();
- const auto days = current / kDayDuration;
- const auto best = ranges::min_element(
- options,
- std::less<>(),
- [&](const Option &option) { return std::abs(option.days - days); });
- const auto group = std::make_shared<Ui::RadiobuttonGroup>(best->days);
- for (const auto &option : options) {
- container->add(
- object_ptr<Ui::Radiobutton>(
- container,
- group,
- option.days,
- option.label,
- st::settingsSendType),
- st::settingsSendTypePadding);
- }
- group->setChangedCallback([=](int days) {
- controller->session().settings().setSupportChatsTimeSlice(
- days * kDayDuration);
- controller->session().saveSettingsDelayed();
- });
- }
- void SetupSupport(
- not_null<Window::SessionController*> controller,
- not_null<Ui::VerticalLayout*> container) {
- Ui::AddSkip(container);
- Ui::AddSubsectionTitle(container, rpl::single(u"Support settings"_q));
- Ui::AddSkip(container, st::settingsSendTypeSkip);
- const auto skip = st::settingsSendTypeSkip;
- auto wrap = object_ptr<Ui::VerticalLayout>(container);
- const auto inner = wrap.data();
- container->add(
- object_ptr<Ui::OverrideMargins>(
- container,
- std::move(wrap),
- QMargins(0, skip, 0, skip)));
- SetupSupportSwitchSettings(controller, inner);
- Ui::AddSkip(inner, st::settingsCheckboxesSkip);
- inner->add(
- object_ptr<Ui::Checkbox>(
- inner,
- "Enable templates autocomplete",
- controller->session().settings().supportTemplatesAutocomplete(),
- st::settingsCheckbox),
- st::settingsSendTypePadding
- )->checkedChanges(
- ) | rpl::start_with_next([=](bool checked) {
- controller->session().settings().setSupportTemplatesAutocomplete(
- checked);
- controller->session().saveSettingsDelayed();
- }, inner->lifetime());
- inner->add(
- object_ptr<Ui::Checkbox>(
- inner,
- "Send all messages without sound",
- controller->session().settings().supportAllSilent(),
- st::settingsCheckbox),
- st::settingsSendTypePadding
- )->checkedChanges(
- ) | rpl::start_with_next([=](bool checked) {
- controller->session().settings().setSupportAllSilent(
- checked);
- controller->session().saveSettingsDelayed();
- }, inner->lifetime());
- Ui::AddSkip(inner, st::settingsCheckboxesSkip);
- Ui::AddSubsectionTitle(inner, rpl::single(u"Load chats for a period"_q));
- SetupSupportChatsLimitSlice(controller, inner);
- Ui::AddSkip(inner, st::settingsCheckboxesSkip);
- Ui::AddSkip(inner);
- }
- Chat::Chat(QWidget *parent, not_null<Window::SessionController*> controller)
- : Section(parent)
- , _controller(controller) {
- setupContent(controller);
- }
- rpl::producer<QString> Chat::title() {
- return tr::lng_settings_section_chat_settings();
- }
- void Chat::fillTopBarMenu(const Ui::Menu::MenuCallback &addAction) {
- const auto window = &_controller->window();
- addAction(
- tr::lng_settings_bg_theme_create(tr::now),
- [=] { window->show(Box(Window::Theme::CreateBox, window)); },
- &st::menuIconChangeColors);
- }
- void Chat::setupContent(not_null<Window::SessionController*> controller) {
- const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
- auto updateOnTick = rpl::single(
- ) | rpl::then(base::timer_each(60 * crl::time(1000)));
- SetupThemeOptions(controller, content);
- SetupThemeSettings(controller, content);
- SetupCloudThemes(controller, content);
- SetupChatBackground(controller, content);
- SetupChatListSwipe(controller, content);
- SetupStickersEmoji(controller, content);
- SetupMessages(controller, content);
- Ui::AddDivider(content);
- SetupSensitiveContent(controller, content, std::move(updateOnTick));
- SetupArchive(controller, content, showOtherMethod());
- Ui::ResizeFitChild(this, content);
- }
- } // namespace Settings
|