diff --git a/Telegram/Resources/day-blue.tdesktop-theme b/Telegram/Resources/day-blue.tdesktop-theme new file mode 100644 index 000000000..ae703848a Binary files /dev/null and b/Telegram/Resources/day-blue.tdesktop-theme differ diff --git a/Telegram/Resources/night-green.tdesktop-theme b/Telegram/Resources/night-green.tdesktop-theme new file mode 100644 index 000000000..3b25f9d55 Binary files /dev/null and b/Telegram/Resources/night-green.tdesktop-theme differ diff --git a/Telegram/Resources/qrc/telegram.qrc b/Telegram/Resources/qrc/telegram.qrc index 91cbeee5c..5d4433627 100644 --- a/Telegram/Resources/qrc/telegram.qrc +++ b/Telegram/Resources/qrc/telegram.qrc @@ -50,7 +50,9 @@ ../art/logo_256.png ../art/logo_256_no_margin.png ../art/sunrise.jpg + ../day-blue.tdesktop-theme ../night.tdesktop-theme + ../night-green.tdesktop-theme ../sounds/msg_incoming.mp3 diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_filter.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_filter.cpp index ae7cb41e6..61c66654e 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_filter.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_filter.cpp @@ -75,7 +75,7 @@ UserCheckbox::UserCheckbox(QWidget *parent, not_null user, bool check void UserCheckbox::setChecked(bool checked, NotifyAboutChange notify) { if (_check->checked() != checked) { - _check->setCheckedAnimated(checked); + _check->setChecked(checked, anim::type::normal); if (notify == NotifyAboutChange::Notify) { checkedChanged.notify(checked, true); } diff --git a/Telegram/SourceFiles/info/profile/info_profile_button.cpp b/Telegram/SourceFiles/info/profile/info_profile_button.cpp index e1b10caf0..1a924d323 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_button.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_button.cpp @@ -40,12 +40,12 @@ Button *Button::toggleOn(rpl::producer &&toggled) { false, [this] { rtlupdate(toggleRect()); }); addClickHandler([this] { - _toggle->setCheckedAnimated(!_toggle->checked()); + _toggle->setChecked(!_toggle->checked(), anim::type::normal); }); std::move( toggled ) | rpl::start_with_next([this](bool toggled) { - _toggle->setCheckedAnimated(toggled); + _toggle->setChecked(toggled, anim::type::normal); }, lifetime()); _toggle->finishAnimating(); return this; diff --git a/Telegram/SourceFiles/settings/settings.style b/Telegram/SourceFiles/settings/settings.style index 05a93207a..75338efdb 100644 --- a/Telegram/SourceFiles/settings/settings.style +++ b/Telegram/SourceFiles/settings/settings.style @@ -152,3 +152,26 @@ settingsBioCountdown: FlatLabel(defaultFlatLabel) { settingsBioLabelPadding: margins(22px, 11px, 22px, 0px); settingsPrivacyEditLabelPadding: margins(22px, 11px, 22px, 11px); + +settingsTheme: Checkbox(defaultCheckbox) { + textFg: windowSubTextFg; + textFgActive: windowActiveTextFg; + + width: 80px; + margin: margins(0px, 0px, 0px, 0px); + + textPosition: point(0px, 88px); + checkPosition: point(0px, 0px); + + style: defaultTextStyle; + + disabledOpacity: 0.5; +} + +settingsThemePreviewSize: size(80px, 80px); +settingsThemeBubbleSize: size(40px, 14px); +settingsThemeBubbleRadius: 2px; +settingsThemeBubblePosition: point(6px, 8px); +settingsThemeBubbleSkip: 4px; +settingsThemeRadioBottom: 8px; +settingsThemeMinSkip: 4px; diff --git a/Telegram/SourceFiles/settings/settings_chat.cpp b/Telegram/SourceFiles/settings/settings_chat.cpp index 2f9a524a4..2adfbc421 100644 --- a/Telegram/SourceFiles/settings/settings_chat.cpp +++ b/Telegram/SourceFiles/settings/settings_chat.cpp @@ -59,6 +59,44 @@ private: }; +class DefaultTheme final : public Ui::AbstractCheckView { +public: + enum class Type { + DayBlue, + Default, + Night, + NightGreen, + }; + struct Scheme { + Type type = Type(); + QColor background; + QColor sent; + QColor received; + QColor radiobuttonInactive; + QColor radiobuttonActive; + QString name; + QString path; + }; + DefaultTheme(Scheme scheme, bool checked); + + QSize getSize() const override; + void paint( + Painter &p, + int left, + int top, + int outerWidth, + TimeMs ms) override; + QImage prepareRippleMask() const override; + bool checkRippleStartPosition(QPoint position) const override; + +private: + void checkedChangedHook(anim::type animated) override; + + Scheme _scheme; + Ui::RadioView _radio; + +}; + void ChooseFromFile(not_null parent); BackgroundRow::BackgroundRow(QWidget *parent) : RpWidget(parent) @@ -252,6 +290,66 @@ void BackgroundRow::updateImage() { } } +DefaultTheme::DefaultTheme(Scheme scheme, bool checked) +: AbstractCheckView(st::defaultRadio.duration, checked, nullptr) +, _scheme(scheme) +, _radio(st::defaultRadio, checked, [=] { update(); }) { + _radio.setToggledOverride(_scheme.radiobuttonActive); + _radio.setUntoggledOverride(_scheme.radiobuttonInactive); +} + +QSize DefaultTheme::getSize() const { + return st::settingsThemePreviewSize; +} + +void DefaultTheme::paint( + Painter &p, + int left, + int top, + int outerWidth, + TimeMs ms) { + const auto received = QRect( + st::settingsThemeBubblePosition, + st::settingsThemeBubbleSize); + const auto sent = QRect( + outerWidth - received.width() - st::settingsThemeBubblePosition.x(), + received.y() + received.height() + st::settingsThemeBubbleSkip, + received.width(), + received.height()); + const auto radius = st::settingsThemeBubbleRadius; + + p.fillRect( + QRect(QPoint(), st::settingsThemePreviewSize), + _scheme.background); + + PainterHighQualityEnabler hq(p); + p.setPen(Qt::NoPen); + p.setBrush(_scheme.received); + p.drawRoundedRect(rtlrect(received, outerWidth), radius, radius); + p.setBrush(_scheme.sent); + p.drawRoundedRect(rtlrect(sent, outerWidth), radius, radius); + + const auto radio = _radio.getSize(); + _radio.paint( + p, + (outerWidth - radio.width()) / 2, + getSize().height() - radio.height() - st::settingsThemeRadioBottom, + outerWidth, + getms()); +} + +QImage DefaultTheme::prepareRippleMask() const { + return QImage(); +} + +bool DefaultTheme::checkRippleStartPosition(QPoint position) const { + return false; +} + +void DefaultTheme::checkedChangedHook(anim::type animated) { + _radio.setChecked(checked(), animated); +} + void ChooseFromFile(not_null parent) { const auto imgExtensions = cImgExtensions(); auto filters = QStringList( @@ -623,6 +721,7 @@ void SetupNightMode(not_null container) { const auto change = [=] { if (!--*calling && toggled != Window::Theme::IsNightMode()) { Window::Theme::ToggleNightMode(); + Window::Theme::KeepApplied(); } }; App::CallDelayed( @@ -655,13 +754,193 @@ void SetupUseDefaultTheme(not_null container) { }); } +void SetupDefaultThemes(not_null container) { + using Type = DefaultTheme::Type; + using Scheme = DefaultTheme::Scheme; + const auto block = container->add(object_ptr( + container)); + const auto scheme = DefaultTheme::Scheme(); + const auto color = [](str_const hex) { + Expects(hex.size() == 6); + + const auto component = [](char a, char b) { + const auto convert = [](char ch) { + Expects((ch >= '0' && ch <= '9') + || (ch >= 'A' && ch <= 'F') + || (ch >= 'a' && ch <= 'f')); + + return (ch >= '0' && ch <= '9') + ? int(ch - '0') + : int(ch - ((ch >= 'A' && ch <= 'F') ? 'A' : 'a') + 10); + }; + return convert(a) * 16 + convert(b); + }; + + return QColor( + component(hex[0], hex[1]), + component(hex[2], hex[3]), + component(hex[4], hex[5])); + }; + static const auto schemes = { + Scheme{ + Type::DayBlue, + color("7ec4ea"), + color("d7f0ff"), + color("ffffff"), + color("d7f0ff"), + color("ffffff"), + "Blue", + ":/gui/day-blue.tdesktop-theme" + }, + Scheme{ + Type::Default, + color("90ce89"), + color("eaffdc"), + color("ffffff"), + color("eaffdc"), + color("ffffff"), + "Classic", + QString() + }, + Scheme{ + Type::Night, + color("485761"), + color("5ca7d4"), + color("6b808d"), + color("6b808d"), + color("5ca7d4"), + "Night #1", + ":/gui/night.tdesktop-theme" + }, + Scheme{ + Type::NightGreen, + color("485761"), + color("74bf93"), + color("6b808d"), + color("6b808d"), + color("74bf93"), + "Night #2", + ":/gui/night-green.tdesktop-theme" + }, + }; + const auto chosen = [&] { + if (Window::Theme::IsNonDefaultBackground()) { + return Type(-1); + } + const auto path = Window::Theme::Background()->themeAbsolutePath(); + for (const auto scheme : schemes) { + if (path == scheme.path) { + return scheme.type; + } + } + return Type(-1); + }; + const auto group = std::make_shared>(chosen()); + auto buttons = ranges::view::all( + schemes + ) | ranges::view::transform([&](const Scheme &scheme) { + auto check = std::make_unique(scheme, false); + const auto weak = check.get(); + const auto result = Ui::CreateChild>( + block, + group, + scheme.type, + scheme.name, + st::settingsTheme, + std::move(check)); + weak->setUpdateCallback([=] { result->update(); }); + return result; + }) | ranges::to_vector; + + using Update = const Window::Theme::BackgroundUpdate; + 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)); + if (Window::Theme::IsNightMode() == isNight(scheme)) { + Window::Theme::ApplyDefaultWithPath(scheme.path); + } else { + Window::Theme::ToggleNightMode(scheme.path); + } + if (!currentlyIsCustom) { + Window::Theme::KeepApplied(); + } + }; + group->setChangedCallback([=](Type type) { + const auto i = ranges::find_if(schemes, [&](const Scheme &scheme) { + return (type == scheme.type && type != chosen()); + }); + if (i != end(schemes)) { + apply(*i); + } + }); + base::ObservableViewer( + *Window::Theme::Background() + ) | rpl::filter([](const Update &update) { + return (update.type == Update::Type::ApplyingTheme + || update.type == Update::Type::New); + }) | rpl::map([=] { + return chosen(); + }) | rpl::start_with_next([=](Type 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()); + + // |------| |---------| |-------| |-------| + // pad | blue | skip | classic | 3*skip | night | skip | night | pad + // |------| |---------| |-------| |-------| + const auto padding = st::settingsButton.padding; + width -= padding.left() + padding.right(); + const auto desired = st::settingsThemePreviewSize.width(); + const auto count = int(buttons.size()); + const auto smallSkips = (count / 2); + const auto bigSkips = ((count - 1) / 2); + const auto skipRatio = 3; + const auto skipSegments = smallSkips + bigSkips * skipRatio; + const auto minSkip = st::settingsThemeMinSkip; + const auto single = [&] { + if (width >= skipSegments * minSkip + count * desired) { + return desired; + } + return (width - skipSegments * minSkip) / count; + }(); + if (single <= 0) { + return; + } + const auto fullSkips = width - count * single; + const auto segment = fullSkips / float64(skipSegments); + const auto smallSkip = segment; + const auto bigSkip = segment * skipRatio; + auto left = padding.left() + 0.; + auto index = 0; + for (const auto button : buttons) { + button->resizeToWidth(single); + button->moveToLeft(int(std::round(left)), 0); + left += button->width() + ((index++ % 2) ? bigSkip : smallSkip); + } + }, block->lifetime()); + + AddSkip(container); +} + void SetupThemeOptions(not_null container) { AddDivider(container); AddSkip(container); AddSubsectionTitle(container, lng_settings_themes); - SetupNightMode(container); + SetupDefaultThemes(container); + //SetupNightMode(container); AddButton( container, @@ -672,7 +951,7 @@ void SetupThemeOptions(not_null container) { container, [] { Window::Theme::Editor::Start(); })); - SetupUseDefaultTheme(container); + //SetupUseDefaultTheme(container); AddSkip(container); } diff --git a/Telegram/SourceFiles/settings/settings_chat.h b/Telegram/SourceFiles/settings/settings_chat.h index b09330c3f..a3fcb3986 100644 --- a/Telegram/SourceFiles/settings/settings_chat.h +++ b/Telegram/SourceFiles/settings/settings_chat.h @@ -14,6 +14,7 @@ namespace Settings { void SetupDataStorage(not_null container); void SetupNightMode(not_null container); void SetupUseDefaultTheme(not_null container); +void SetupDefaultThemes(not_null container); class Chat : public Section { public: diff --git a/Telegram/SourceFiles/settings/settings_intro.cpp b/Telegram/SourceFiles/settings/settings_intro.cpp index 0e31088dd..3ea3b2449 100644 --- a/Telegram/SourceFiles/settings/settings_intro.cpp +++ b/Telegram/SourceFiles/settings/settings_intro.cpp @@ -79,8 +79,7 @@ object_ptr CreateIntroSettings(QWidget *parent) { AddDivider(result); AddSkip(result); SetupInterfaceScale(result, false); - SetupNightMode(result); - SetupUseDefaultTheme(result); + SetupDefaultThemes(result); AddSkip(result); if (anim::Disabled()) { diff --git a/Telegram/SourceFiles/ui/widgets/checkbox.cpp b/Telegram/SourceFiles/ui/widgets/checkbox.cpp index ad0c84d15..405c323ca 100644 --- a/Telegram/SourceFiles/ui/widgets/checkbox.cpp +++ b/Telegram/SourceFiles/ui/widgets/checkbox.cpp @@ -28,14 +28,23 @@ AbstractCheckView::AbstractCheckView(int duration, bool checked, Fn upda , _updateCallback(std::move(updateCallback)) { } -void AbstractCheckView::setCheckedFast(bool checked) { - const auto fire = (_checked != checked); +void AbstractCheckView::setChecked(bool checked, anim::type animated) { + const auto changed = (_checked != checked); _checked = checked; - finishAnimating(); - if (_updateCallback) { - _updateCallback(); + if (animated == anim::type::instant) { + finishAnimating(); + if (_updateCallback) { + _updateCallback(); + } + } else if (changed) { + _toggleAnimation.start( + _updateCallback, + _checked ? 0. : 1., + _checked ? 1. : 0., + _duration); } - if (fire) { + if (changed) { + checkedChangedHook(animated); _checks.fire_copy(_checked); } } @@ -53,14 +62,6 @@ void AbstractCheckView::update() { } } -void AbstractCheckView::setCheckedAnimated(bool checked) { - if (_checked != checked) { - _checked = checked; - _toggleAnimation.start(_updateCallback, _checked ? 0. : 1., _checked ? 1. : 0., _duration); - _checks.fire_copy(_checked); - } -} - void AbstractCheckView::finishAnimating() { _toggleAnimation.finish(); } @@ -69,6 +70,10 @@ float64 AbstractCheckView::currentAnimationValue(TimeMs ms) { return ms ? _toggleAnimation.current(ms, _checked ? 1. : 0.) : _toggleAnimation.current(_checked ? 1. : 0.); } +bool AbstractCheckView::animating() const { + return _toggleAnimation.animating(); +} + ToggleView::ToggleView( const style::Toggle &st, bool checked, @@ -283,9 +288,13 @@ void RadioView::paint(Painter &p, int left, int top, int outerWidth, TimeMs ms) PainterHighQualityEnabler hq(p); auto toggled = currentAnimationValue(ms); - auto pen = _untoggledOverride - ? anim::pen(*_untoggledOverride, _st->toggledFg, toggled) - : anim::pen(_st->untoggledFg, _st->toggledFg, toggled); + auto pen = _toggledOverride + ? (_untoggledOverride + ? anim::pen(*_untoggledOverride, *_toggledOverride, toggled) + : anim::pen(_st->untoggledFg, *_toggledOverride, toggled)) + : (_untoggledOverride + ? anim::pen(*_untoggledOverride, _st->toggledFg, toggled) + : anim::pen(_st->untoggledFg, _st->toggledFg, toggled)); pen.setWidth(_st->thickness); p.setPen(pen); p.setBrush(_st->bg); @@ -295,9 +304,13 @@ void RadioView::paint(Painter &p, int left, int top, int outerWidth, TimeMs ms) if (toggled > 0) { p.setPen(Qt::NoPen); - p.setBrush(_untoggledOverride - ? anim::brush(*_untoggledOverride, _st->toggledFg, toggled) - : anim::brush(_st->untoggledFg, _st->toggledFg, toggled)); + p.setBrush(_toggledOverride + ? (_untoggledOverride + ? anim::brush(*_untoggledOverride, *_toggledOverride, toggled) + : anim::brush(_st->untoggledFg, *_toggledOverride, toggled)) + : (_untoggledOverride + ? anim::brush(*_untoggledOverride, _st->toggledFg, toggled) + : anim::brush(_st->untoggledFg, _st->toggledFg, toggled))); auto skip0 = _st->diameter / 2., skip1 = _st->skip / 10., checkSkip = skip0 * (1. - toggled) + skip1 * toggled; p.drawEllipse(rtlrect(QRectF(left, top, _st->diameter, _st->diameter).marginsRemoved(QMarginsF(checkSkip, checkSkip, checkSkip, checkSkip)), outerWidth)); @@ -327,6 +340,11 @@ bool RadioView::checkRippleStartPosition(QPoint position) const { return QRect(QPoint(0, 0), rippleSize()).contains(position); } +void RadioView::setToggledOverride(std::optional toggledOverride) { + _toggledOverride = toggledOverride; + update(); +} + void RadioView::setUntoggledOverride( std::optional untoggledOverride) { _untoggledOverride = untoggledOverride; @@ -420,7 +438,7 @@ void Checkbox::resizeToText() { void Checkbox::setChecked(bool checked, NotifyAboutChange notify) { if (_check->checked() != checked) { - _check->setCheckedAnimated(checked); + _check->setChecked(checked, anim::type::normal); if (notify == NotifyAboutChange::Notify) { checkedChanged.notify(checked, true); } @@ -447,10 +465,10 @@ void Checkbox::paintEvent(QPaintEvent *e) { auto check = checkRect(); auto ms = getms(); + auto active = _check->currentAnimationValue(ms); if (isDisabled()) { p.setOpacity(_st.disabledOpacity); } else { - auto active = _check->currentAnimationValue(ms); auto color = anim::color(_st.rippleBg, _st.rippleBgActive, active); paintRipple( p, @@ -476,8 +494,7 @@ void Checkbox::paintEvent(QPaintEvent *e) { auto availableTextWidth = qMax(width() - leftSkip, 1); if (!_text.isEmpty()) { - Assert(!(_checkAlignment & Qt::AlignHCenter)); - p.setPen(_st.textFg); + p.setPen(anim::pen(_st.textFg, _st.textFgActive, active)); auto textSkip = _st.checkPosition.x() + check.width() + _st.textPosition.x(); @@ -489,13 +506,21 @@ void Checkbox::paintEvent(QPaintEvent *e) { textTop, availableTextWidth, width()); - } else { + } else if (_checkAlignment & Qt::AlignRight) { _text.drawRightElided( p, textSkip, textTop, availableTextWidth, width()); + } else { + _text.drawLeft( + p, + _st.margin.left(), + textTop, + width() - _st.margin.left() - _st.margin.right(), + width(), + style::al_top); } } } @@ -537,7 +562,15 @@ void Checkbox::handlePress() { } int Checkbox::resizeGetHeight(int newWidth) { - return _check->getSize().height(); + const auto result = _check->getSize().height(); + if (!(_checkAlignment & Qt::AlignHCenter)) { + return result; + } + const auto textBottom = _st.margin.top() + + _st.textPosition.y() + + _text.countHeight( + newWidth - _st.margin.left() - _st.margin.right()); + return std::max(result, textBottom); } QImage Checkbox::prepareRippleMask() const { diff --git a/Telegram/SourceFiles/ui/widgets/checkbox.h b/Telegram/SourceFiles/ui/widgets/checkbox.h index 5a00d6cb3..32a1d8379 100644 --- a/Telegram/SourceFiles/ui/widgets/checkbox.h +++ b/Telegram/SourceFiles/ui/widgets/checkbox.h @@ -17,8 +17,7 @@ class AbstractCheckView { public: AbstractCheckView(int duration, bool checked, Fn updateCallback); - void setCheckedFast(bool checked); - void setCheckedAnimated(bool checked); + void setChecked(bool checked, anim::type animated); void finishAnimating(); void setUpdateCallback(Fn updateCallback); bool checked() const { @@ -26,6 +25,7 @@ public: } void update(); float64 currentAnimationValue(TimeMs ms); + bool animating() const; auto checkedValue() const { return _checks.events_starting_with(checked()); @@ -47,6 +47,9 @@ public: virtual ~AbstractCheckView() = default; private: + virtual void checkedChangedHook(anim::type animated) { + } + int _duration = 0; bool _checked = false; Fn _updateCallback; @@ -90,6 +93,7 @@ public: void setStyle(const style::Radio &st); + void setToggledOverride(std::optional toggledOverride); void setUntoggledOverride(std::optional untoggledOverride); QSize getSize() const override; @@ -101,6 +105,7 @@ private: QSize rippleSize() const; not_null _st; + std::optional _toggledOverride; std::optional _untoggledOverride; }; diff --git a/Telegram/SourceFiles/ui/widgets/menu.cpp b/Telegram/SourceFiles/ui/widgets/menu.cpp index c0897fb15..ee16b4dea 100644 --- a/Telegram/SourceFiles/ui/widgets/menu.cpp +++ b/Telegram/SourceFiles/ui/widgets/menu.cpp @@ -132,7 +132,7 @@ int Menu::processAction(QAction *action, int index, int width) { auto updateCallback = [this, index] { updateItem(index); }; if (data.toggle) { data.toggle->setUpdateCallback(updateCallback); - data.toggle->setCheckedAnimated(action->isChecked()); + data.toggle->setChecked(action->isChecked(), anim::type::normal); } else { data.toggle = std::make_unique(_st.itemToggle, action->isChecked(), updateCallback); } diff --git a/Telegram/SourceFiles/ui/widgets/widgets.style b/Telegram/SourceFiles/ui/widgets/widgets.style index 3c0b09756..dfad6417f 100644 --- a/Telegram/SourceFiles/ui/widgets/widgets.style +++ b/Telegram/SourceFiles/ui/widgets/widgets.style @@ -142,6 +142,7 @@ Radio { Checkbox { textFg: color; + textFgActive: color; width: pixels; margin: margins; diff --git a/Telegram/SourceFiles/ui/wrap/padding_wrap.h b/Telegram/SourceFiles/ui/wrap/padding_wrap.h index 24bf1a888..d13d3477e 100644 --- a/Telegram/SourceFiles/ui/wrap/padding_wrap.h +++ b/Telegram/SourceFiles/ui/wrap/padding_wrap.h @@ -92,7 +92,7 @@ public: class FixedHeightWidget : public RpWidget { public: - FixedHeightWidget(QWidget *parent, int height) + explicit FixedHeightWidget(QWidget *parent, int height = 0) : RpWidget(parent) { resize(width(), height); } diff --git a/Telegram/SourceFiles/window/themes/window_theme.cpp b/Telegram/SourceFiles/window/themes/window_theme.cpp index 6c4649de3..accddc511 100644 --- a/Telegram/SourceFiles/window/themes/window_theme.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme.cpp @@ -33,6 +33,7 @@ struct Data { QByteArray content; QByteArray paletteForRevert; Cached cached; + Fn overrideKeep; }; ChatBackground background; @@ -336,24 +337,6 @@ void adjustColor(style::color color, float64 hue, float64 saturation) { color.set(original.red(), original.green(), original.blue(), original.alpha()); } -void ApplyDefaultWithNightMode(bool nightMode) { - if (nightMode) { - if (auto preview = PreviewFromFile(NightThemePath())) { - Apply(std::move(preview)); - } - } else { - instance.createIfNull(); - instance->applying.pathRelative = QString(); - instance->applying.pathAbsolute = QString(); - instance->applying.content = QByteArray(); - instance->applying.cached = Cached(); - if (instance->applying.paletteForRevert.isEmpty()) { - instance->applying.paletteForRevert = style::main_palette::save(); - } - Background()->setTestingDefaultTheme(); - } -} - void WriteAppliedTheme() { auto saved = Saved(); saved.pathRelative = instance->applying.pathRelative; @@ -723,6 +706,13 @@ bool ChatBackground::isNonDefaultThemeOrBackground() { || _id != kDefaultBackground); } +bool ChatBackground::isNonDefaultBackground() { + start(); + return _themeAbsolutePath.isEmpty() + ? (_id != kDefaultBackground) + : (_id != kThemeBackground); +} + void ChatBackground::writeNewBackgroundSettings() { if (tile() != _tileForRevert) { Local::writeUserSettings(); @@ -755,11 +745,12 @@ bool ChatBackground::nightMode() const { return _nightMode; } -void ChatBackground::toggleNightMode() { +void ChatBackground::toggleNightMode(std::optional themePath) { + const auto settingDefault = themePath.has_value(); const auto oldNightMode = _nightMode; const auto newNightMode = !_nightMode; _nightMode = newNightMode; - auto read = Local::readThemeAfterSwitch(); + auto read = settingDefault ? Saved() : Local::readThemeAfterSwitch(); auto path = read.pathAbsolute; _nightMode = oldNightMode; @@ -784,30 +775,34 @@ void ChatBackground::toggleNightMode() { return true; }(); if (!alreadyOnDisk) { - path = newNightMode ? NightThemePath() : QString(); - ApplyDefaultWithNightMode(newNightMode); + path = themePath + ? *themePath + : (newNightMode ? NightThemePath() : QString()); + ApplyDefaultWithPath(path); } // Theme editor could have already reverted the testing of this toggle. if (AreTestingTheme()) { - _nightMode = newNightMode; + instance->applying.overrideKeep = [=] { + _nightMode = newNightMode; - // Restore the value, it was set inside theme testing. - (oldNightMode ? _tileNightValue : _tileDayValue) = oldTileValue; + // Restore the value, it was set inside theme testing. + (oldNightMode ? _tileNightValue : _tileDayValue) = oldTileValue; - if (!alreadyOnDisk) { - // First-time switch to default night mode should write it. - WriteAppliedTheme(); - } - ClearApplying(); - keepApplied(path, false); - if (tile() != _tileForRevert) { - Local::writeUserSettings(); - } - Local::writeSettings(); - if (!Local::readBackground()) { - setImage(kThemeBackground); - } + if (!alreadyOnDisk) { + // First-time switch to default night mode should write it. + WriteAppliedTheme(); + } + ClearApplying(); + keepApplied(path, settingDefault); + if (tile() != _tileForRevert) { + Local::writeUserSettings(); + } + Local::writeSettings(); + if (!settingDefault && !Local::readBackground()) { + setImage(kThemeBackground); + } + }; } } @@ -863,7 +858,25 @@ bool Apply(std::unique_ptr preview) { } void ApplyDefault() { - ApplyDefaultWithNightMode(IsNightMode()); + ApplyDefaultWithPath(IsNightMode() ? NightThemePath() : QString()); +} + +void ApplyDefaultWithPath(const QString &themePath) { + if (!themePath.isEmpty()) { + if (auto preview = PreviewFromFile(themePath)) { + Apply(std::move(preview)); + } + } else { + instance.createIfNull(); + instance->applying.pathRelative = QString(); + instance->applying.pathAbsolute = QString(); + instance->applying.content = QByteArray(); + instance->applying.cached = Cached(); + if (instance->applying.paletteForRevert.isEmpty()) { + instance->applying.paletteForRevert = style::main_palette::save(); + } + Background()->setTestingDefaultTheme(); + } } bool ApplyEditedPalette(const QString &path, const QByteArray &content) { @@ -895,6 +908,9 @@ bool ApplyEditedPalette(const QString &path, const QByteArray &content) { void KeepApplied() { if (!AreTestingTheme()) { return; + } else if (instance->applying.overrideKeep) { + instance->applying.overrideKeep(); + return; } const auto path = instance->applying.pathAbsolute; WriteAppliedTheme(); @@ -921,6 +937,10 @@ bool IsNonDefaultThemeOrBackground() { return Background()->isNonDefaultThemeOrBackground(); } +bool IsNonDefaultBackground() { + return Background()->isNonDefaultBackground(); +} + bool IsNightMode() { return instance ? Background()->nightMode() : false; } @@ -932,7 +952,11 @@ void SetNightModeValue(bool nightMode) { } void ToggleNightMode() { - Background()->toggleNightMode(); + Background()->toggleNightMode(std::nullopt); +} + +void ToggleNightMode(const QString &path) { + Background()->toggleNightMode(path); } bool SuggestThemeReset() { diff --git a/Telegram/SourceFiles/window/themes/window_theme.h b/Telegram/SourceFiles/window/themes/window_theme.h index aad7d41d3..6ddf725ed 100644 --- a/Telegram/SourceFiles/window/themes/window_theme.h +++ b/Telegram/SourceFiles/window/themes/window_theme.h @@ -57,12 +57,15 @@ struct Preview { bool Apply(const QString &filepath); bool Apply(std::unique_ptr preview); void ApplyDefault(); +void ApplyDefaultWithPath(const QString &themePath); bool ApplyEditedPalette(const QString &path, const QByteArray &content); void KeepApplied(); QString NightThemePath(); bool IsNightMode(); void SetNightModeValue(bool nightMode); void ToggleNightMode(); +void ToggleNightMode(const QString &themePath); +bool IsNonDefaultBackground(); bool IsNonDefaultThemeOrBackground(); bool SuggestThemeReset(); void Revert(); @@ -140,14 +143,17 @@ private: void setNightModeValue(bool nightMode); bool nightMode() const; - void toggleNightMode(); + void toggleNightMode(std::optional themePath); void keepApplied(const QString &path, bool write); bool isNonDefaultThemeOrBackground(); + bool isNonDefaultBackground(); friend bool IsNightMode(); friend void SetNightModeValue(bool nightMode); friend void ToggleNightMode(); + friend void ToggleNightMode(const QString &themePath); friend void KeepApplied(); + friend bool IsNonDefaultBackground(); friend bool IsNonDefaultThemeOrBackground(); int32 _id = internal::kUninitializedBackground; diff --git a/Telegram/SourceFiles/window/window_main_menu.cpp b/Telegram/SourceFiles/window/window_main_menu.cpp index 4546aa189..31e5338b4 100644 --- a/Telegram/SourceFiles/window/window_main_menu.cpp +++ b/Telegram/SourceFiles/window/window_main_menu.cpp @@ -58,6 +58,7 @@ MainMenu::MainMenu( const auto nightMode = Window::Theme::IsNightMode(); if (action->isChecked() != nightMode) { Window::Theme::ToggleNightMode(); + Window::Theme::KeepApplied(); } } });