Toggle wallpaper dark mode / edit dimming.

This commit is contained in:
John Preston 2023-04-20 14:39:39 +04:00
parent ac57d46f30
commit 65f54d937f
9 changed files with 384 additions and 79 deletions

View file

@ -17,6 +17,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/toast/toast.h" #include "ui/toast/toast.h"
#include "ui/image/image.h" #include "ui/image/image.h"
#include "ui/widgets/checkbox.h" #include "ui/widgets/checkbox.h"
#include "ui/widgets/continuous_sliders.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/painter.h" #include "ui/painter.h"
#include "ui/ui_utility.h" #include "ui/ui_utility.h"
#include "history/history.h" #include "history/history.h"
@ -34,12 +36,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/unixtime.h" #include "base/unixtime.h"
#include "boxes/background_preview_box.h" #include "boxes/background_preview_box.h"
#include "window/window_session_controller.h" #include "window/window_session_controller.h"
#include "window/themes/window_themes_embedded.h"
#include "settings/settings_common.h" #include "settings/settings_common.h"
#include "storage/file_upload.h" #include "storage/file_upload.h"
#include "storage/localimageloader.h" #include "storage/localimageloader.h"
#include "styles/style_chat.h" #include "styles/style_chat.h"
#include "styles/style_layers.h" #include "styles/style_layers.h"
#include "styles/style_boxes.h" #include "styles/style_boxes.h"
#include "styles/style_settings.h"
#include <QtGui/QClipboard> #include <QtGui/QClipboard>
#include <QtGui/QGuiApplication> #include <QtGui/QGuiApplication>
@ -47,6 +51,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace { namespace {
constexpr auto kMaxWallPaperSlugLength = 255; constexpr auto kMaxWallPaperSlugLength = 255;
constexpr auto kDefaultDimming = 50;
[[nodiscard]] bool IsValidWallPaperSlug(const QString &slug) { [[nodiscard]] bool IsValidWallPaperSlug(const QString &slug) {
if (slug.isEmpty() || slug.size() > kMaxWallPaperSlugLength) { if (slug.isEmpty() || slug.size() > kMaxWallPaperSlugLength) {
@ -154,6 +159,13 @@ constexpr auto kMaxWallPaperSlugLength = 255;
} // namespace } // namespace
struct BackgroundPreviewBox::OverridenStyle {
style::Box box;
style::IconButton toggle;
style::MediaSlider slider;
style::FlatLabel subtitle;
};
BackgroundPreviewBox::BackgroundPreviewBox( BackgroundPreviewBox::BackgroundPreviewBox(
QWidget*, QWidget*,
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
@ -183,9 +195,11 @@ BackgroundPreviewBox::BackgroundPreviewBox(
true)) true))
, _paper(paper) , _paper(paper)
, _media(_paper.document() ? _paper.document()->createMediaView() : nullptr) , _media(_paper.document() ? _paper.document()->createMediaView() : nullptr)
, _radial([=](crl::time now) { radialAnimationCallback(now); }) { , _radial([=](crl::time now) { radialAnimationCallback(now); })
_chatStyle->apply(controller->defaultChatTheme().get()); , _appNightMode(Window::Theme::IsNightModeValue())
, _boxDarkMode(_appNightMode.current())
, _dimmingIntensity(std::clamp(paper.patternIntensity(), 0, 100))
, _dimmed(_forPeer && paper.document() && !paper.isPattern()) {
if (_media) { if (_media) {
_media->thumbnailWanted(_paper.fileOrigin()); _media->thumbnailWanted(_paper.fileOrigin());
} }
@ -194,6 +208,201 @@ BackgroundPreviewBox::BackgroundPreviewBox(
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
update(); update();
}, lifetime()); }, lifetime());
_appNightMode.changes(
) | rpl::start_with_next([=](bool night) {
_boxDarkMode = night;
update();
}, lifetime());
_boxDarkMode.changes(
) | rpl::start_with_next([=](bool dark) {
applyDarkMode(dark);
}, lifetime());
const auto prepare = [=](bool dark, auto pointer) {
const auto weak = Ui::MakeWeak(this);
crl::async([=] {
auto result = std::make_unique<style::palette>();
Window::Theme::PreparePaletteCallback(dark, {})(*result);
crl::on_main([=, result = std::move(result)]() mutable {
if (const auto strong = weak.data()) {
strong->*pointer = std::move(result);
strong->paletteReady();
}
});
});
};
prepare(false, &BackgroundPreviewBox::_lightPalette);
prepare(true, &BackgroundPreviewBox::_darkPalette);
}
BackgroundPreviewBox::~BackgroundPreviewBox() = default;
void BackgroundPreviewBox::applyDarkMode(bool dark) {
const auto equals = (dark == Window::Theme::IsNightMode());
const auto &palette = (dark ? _darkPalette : _lightPalette);
if (!equals && !palette) {
_waitingForPalette = true;
return;
}
_waitingForPalette = false;
if (equals) {
setStyle(st::defaultBox);
_chatStyle->applyCustomPalette(nullptr);
_paletteServiceBg = rpl::single(
rpl::empty
) | rpl::then(
style::PaletteChanged()
) | rpl::map([=] {
return st::msgServiceBg->c;
});
} else {
setStyle(overridenStyle(dark));
_chatStyle->applyCustomPalette(palette.get());
_paletteServiceBg = palette->msgServiceBg()->c;
}
resetTitle();
rebuildButtons(dark);
update();
if (const auto parent = parentWidget()) {
parent->update();
}
if (_dimmed) {
createDimmingSlider(dark);
}
}
void BackgroundPreviewBox::createDimmingSlider(bool dark) {
const auto created = !_dimmingWrap;
if (created) {
_dimmingWrap.create(this, object_ptr<Ui::RpWidget>(this));
_dimmingContent = _dimmingWrap->entity();
}
_dimmingSlider = nullptr;
for (const auto &child : _dimmingContent->children()) {
if (child->isWidgetType()) {
static_cast<QWidget*>(child)->hide();
child->deleteLater();
}
}
const auto equals = (dark == Window::Theme::IsNightMode());
const auto inner = Ui::CreateChild<Ui::VerticalLayout>(_dimmingContent);
inner->show();
Settings::AddSubsectionTitle(
inner,
rpl::single(u"Background dimming"_q),
style::margins(0, st::settingsSectionSkip, 0, 0),
equals ? nullptr : dark ? &_dark->subtitle : &_light->subtitle);
_dimmingSlider = inner->add(
object_ptr<Ui::MediaSlider>(
inner,
(equals
? st::defaultContinuousSlider
: dark
? _dark->slider
: _light->slider)),
st::localStorageLimitMargin);
_dimmingSlider->setValue(_dimmingIntensity / 100.);
_dimmingSlider->setAlwaysDisplayMarker(true);
_dimmingSlider->resize(st::defaultContinuousSlider.seekSize);
const auto handle = [=](float64 value) {
const auto intensity = std::clamp(
int(base::SafeRound(value * 100)),
0,
100);
_paper = _paper.withPatternIntensity(intensity);
_dimmingIntensity = intensity;
update();
};
_dimmingSlider->setChangeProgressCallback(handle);
_dimmingSlider->setChangeFinishedCallback(handle);
inner->resizeToWidth(st::boxWideWidth);
Ui::SendPendingMoveResizeEvents(inner);
inner->move(0, 0);
_dimmingContent->resize(inner->size());
_dimmingContent->paintRequest(
) | rpl::start_with_next([=](QRect clip) {
auto p = QPainter(_dimmingContent);
const auto palette = (dark ? _darkPalette : _lightPalette).get();
p.fillRect(clip, equals ? st::boxBg : palette->boxBg());
}, _dimmingContent->lifetime());
_dimmingToggleScheduled = true;
if (created) {
rpl::combine(
heightValue(),
_dimmingWrap->heightValue(),
rpl::mappers::_1 - rpl::mappers::_2
) | rpl::start_with_next([=](int top) {
_dimmingWrap->move(0, top);
}, _dimmingWrap->lifetime());
_dimmingWrap->toggle(!dark, anim::type::instant);
_dimmingHeight = _dimmingWrap->heightValue();
_dimmingHeight.changes() | rpl::start_with_next([=] {
update();
}, _dimmingWrap->lifetime());
}
}
void BackgroundPreviewBox::paletteReady() {
if (_waitingForPalette) {
applyDarkMode(_boxDarkMode.current());
}
}
const style::Box &BackgroundPreviewBox::overridenStyle(bool dark) {
auto &st = dark ? _dark : _light;
if (!st) {
st = std::make_unique<OverridenStyle>(prepareOverridenStyle(dark));
}
return st->box;
}
auto BackgroundPreviewBox::prepareOverridenStyle(bool dark)
-> OverridenStyle {
const auto p = (dark ? _darkPalette : _lightPalette).get();
Assert(p != nullptr);
const auto &toggle = dark
? st::backgroundSwitchToLight
: st::backgroundSwitchToDark;
auto result = OverridenStyle{
.box = st::defaultBox,
.toggle = toggle,
.slider = st::defaultContinuousSlider,
.subtitle = st::settingsSubsectionTitle,
};
result.box.button.textFg = p->lightButtonFg();
result.box.button.textFgOver = p->lightButtonFgOver();
result.box.button.numbersTextFg = p->lightButtonFg();
result.box.button.numbersTextFgOver = p->lightButtonFgOver();
result.box.button.textBg = p->lightButtonBg();
result.box.button.textBgOver = p->lightButtonBgOver();
result.box.button.ripple.color = p->lightButtonBgRipple();
result.box.title.textFg = p->boxTitleFg();
result.box.bg = p->boxBg();
result.box.titleAdditionalFg = p->boxTitleAdditionalFg();
result.toggle.ripple.color = p->windowBgOver();
result.toggle.icon = toggle.icon.withPalette(*p);
result.toggle.iconOver = toggle.iconOver.withPalette(*p);
result.slider.activeFg = p->mediaPlayerActiveFg();
result.slider.inactiveFg = p->mediaPlayerInactiveFg();
result.slider.activeFgOver = p->mediaPlayerActiveFg();
result.slider.inactiveFgOver = p->mediaPlayerInactiveFg();
result.slider.activeFgDisabled = p->mediaPlayerInactiveFg();
result.slider.inactiveFgDisabled = p->windowBg();
result.slider.receivedTillFg = p->mediaPlayerInactiveFg();
result.subtitle.textFg = p->windowActiveTextFg();
return result;
} }
void BackgroundPreviewBox::generateBackground() { void BackgroundPreviewBox::generateBackground() {
@ -215,9 +424,12 @@ not_null<HistoryView::ElementDelegate*> BackgroundPreviewBox::delegate() {
return static_cast<HistoryView::ElementDelegate*>(this); return static_cast<HistoryView::ElementDelegate*>(this);
} }
void BackgroundPreviewBox::prepare() { void BackgroundPreviewBox::resetTitle() {
setTitle(tr::lng_background_header()); setTitle(tr::lng_background_header());
}
void BackgroundPreviewBox::rebuildButtons(bool dark) {
clearButtons();
addButton(_forPeer addButton(_forPeer
? tr::lng_background_apply_button() ? tr::lng_background_apply_button()
: tr::lng_background_apply(), [=] { apply(); }); : tr::lng_background_apply(), [=] { apply(); });
@ -225,18 +437,28 @@ void BackgroundPreviewBox::prepare() {
if (!_forPeer && _paper.hasShareUrl()) { if (!_forPeer && _paper.hasShareUrl()) {
addLeftButton(tr::lng_background_share(), [=] { share(); }); addLeftButton(tr::lng_background_share(), [=] { share(); });
} }
updateServiceBg(_paper.backgroundColors()); const auto equals = (dark == Window::Theme::IsNightMode());
auto toggle = object_ptr<Ui::IconButton>(this, equals
? (dark ? st::backgroundSwitchToLight : st::backgroundSwitchToDark)
: dark ? _dark->toggle : _light->toggle);
toggle->setClickedCallback([=] {
_boxDarkMode = !_boxDarkMode.current();
});
addTopButton(std::move(toggle));
}
void BackgroundPreviewBox::prepare() {
applyDarkMode(Window::Theme::IsNightMode());
_paper.loadDocument(); _paper.loadDocument();
const auto document = _paper.document(); if (const auto document = _paper.document()) {
if (document && document->loading()) { if (document->loading()) {
_radial.start(_media->progress()); _radial.start(_media->progress());
} }
if (!_paper.isPattern()
&& (_paper.localThumbnail()
|| (document && document->hasThumbnail()))) {
createBlurCheckbox();
} }
updateServiceBg(_paper.backgroundColors());
setScaledFromThumb(); setScaledFromThumb();
checkLoadedDocument(); checkLoadedDocument();
@ -249,31 +471,42 @@ void BackgroundPreviewBox::prepare() {
setDimensions(st::boxWideWidth, st::boxWideWidth); setDimensions(st::boxWideWidth, st::boxWideWidth);
} }
void BackgroundPreviewBox::createBlurCheckbox() { void BackgroundPreviewBox::recreateBlurCheckbox() {
const auto document = _paper.document();
if (_paper.isPattern()
|| (!_paper.localThumbnail()
&& (!document || !document->hasThumbnail()))) {
return;
}
const auto blurred = _blur ? _blur->checked() : _paper.isBlurred();
_blur = Ui::MakeChatServiceCheckbox( _blur = Ui::MakeChatServiceCheckbox(
this, this,
tr::lng_background_blur(tr::now), tr::lng_background_blur(tr::now),
st::backgroundCheckbox, st::backgroundCheckbox,
st::backgroundCheck, st::backgroundCheck,
_paper.isBlurred(), blurred,
[=] { return _serviceBg.value_or(QColor(255, 255, 255, 0)); }); [=] { return _serviceBg.value_or(QColor(255, 255, 255, 0)); });
_blur->show();
rpl::combine( rpl::combine(
sizeValue(), sizeValue(),
_blur->sizeValue() _blur->sizeValue(),
) | rpl::start_with_next([=](QSize outer, QSize inner) { _dimmingHeight.value()
) | rpl::start_with_next([=](QSize outer, QSize inner, int dimming) {
const auto bottom = st::historyPaddingBottom;
_blur->move( _blur->move(
(outer.width() - inner.width()) / 2, (outer.width() - inner.width()) / 2,
outer.height() - st::historyPaddingBottom - inner.height()); outer.height() - dimming - bottom - inner.height());
}, _blur->lifetime()); }, _blur->lifetime());
_blur->checkedChanges( _blur->checkedChanges(
) | rpl::start_with_next([=](bool checked) { ) | rpl::start_with_next([=](bool checked) {
checkBlurAnimationStart(); checkBlurAnimationStart();
update(); update();
}, lifetime()); }, _blur->lifetime());
_blur->setDisabled(true); _blur->setDisabled(_paper.document() && _full.isNull());
} }
void BackgroundPreviewBox::apply() { void BackgroundPreviewBox::apply() {
@ -418,6 +651,13 @@ void BackgroundPreviewBox::paintEvent(QPaintEvent *e) {
} }
if (!_scaled.isNull()) { if (!_scaled.isNull()) {
paintImage(p); paintImage(p);
const auto dimming = (_dimmed && _boxDarkMode.current())
? _dimmingIntensity
: 0;
if (dimming > 0) {
const auto alpha = 255 * dimming / 100;
p.fillRect(e->rect(), QColor(0, 0, 0, alpha));
}
paintRadial(p); paintRadial(p);
} else if (_generated.isNull()) { } else if (_generated.isNull()) {
p.fillRect(e->rect(), st::boxBg); p.fillRect(e->rect(), st::boxBg);
@ -427,6 +667,15 @@ void BackgroundPreviewBox::paintEvent(QPaintEvent *e) {
paintRadial(p); paintRadial(p);
} }
paintTexts(p, ms); paintTexts(p, ms);
if (_dimmingToggleScheduled) {
crl::on_main(this, [=] {
if (!_dimmingToggleScheduled) {
return;
}
_dimmingToggleScheduled = false;
_dimmingWrap->toggle(_boxDarkMode.current(), anim::type::normal);
});
}
} }
void BackgroundPreviewBox::paintImage(Painter &p) { void BackgroundPreviewBox::paintImage(Painter &p) {
@ -476,7 +725,9 @@ void BackgroundPreviewBox::paintRadial(Painter &p) {
} }
int BackgroundPreviewBox::textsTop() const { int BackgroundPreviewBox::textsTop() const {
const auto bottom = _blur ? _blur->y() : height(); const auto bottom = _blur
? _blur->y()
: (height() - _dimmingHeight.current());
return bottom return bottom
- st::historyPaddingBottom - st::historyPaddingBottom
- (_service ? _service->height() : 0) - (_service ? _service->height() : 0)
@ -569,8 +820,8 @@ void BackgroundPreviewBox::setScaledFromImage(
} }
_scaled = Ui::PixmapFromImage(std::move(image)); _scaled = Ui::PixmapFromImage(std::move(image));
_blurred = Ui::PixmapFromImage(std::move(blurred)); _blurred = Ui::PixmapFromImage(std::move(blurred));
if (_blur && (!_paper.document() || !_full.isNull())) { if (_blur) {
_blur->setDisabled(false); _blur->setDisabled(_paper.document() && _full.isNull());
} }
} }
@ -595,22 +846,21 @@ void BackgroundPreviewBox::updateServiceBg(const std::vector<QColor> &bg) {
if (!count) { if (!count) {
return; return;
} }
auto red = 0, green = 0, blue = 0; auto red = 0LL, green = 0LL, blue = 0LL;
for (const auto &color : bg) { for (const auto &color : bg) {
red += color.red(); red += color.red();
green += color.green(); green += color.green();
blue += color.blue(); blue += color.blue();
} }
rpl::single(
rpl::empty _serviceBgLifetime = _paletteServiceBg.value(
) | rpl::then( ) | rpl::start_with_next([=](QColor color) {
style::PaletteChanged()
) | rpl::start_with_next([=] {
_serviceBg = Ui::ThemeAdjustedColor( _serviceBg = Ui::ThemeAdjustedColor(
st::msgServiceBg->c, color,
QColor(red / count, green / count, blue / count)); QColor(red / count, green / count, blue / count));
_chatStyle->applyAdjustedServiceBg(*_serviceBg); _chatStyle->applyAdjustedServiceBg(*_serviceBg);
}, lifetime()); recreateBlurCheckbox();
});
_service = GenerateServiceItem( _service = GenerateServiceItem(
delegate(), delegate(),

View file

@ -26,6 +26,9 @@ class SessionController;
namespace Ui { namespace Ui {
class Checkbox; class Checkbox;
class ChatStyle; class ChatStyle;
class MediaSlider;
template <typename Widget>
class SlideWrap;
} // namespace Ui } // namespace Ui
struct BackgroundPreviewArgs { struct BackgroundPreviewArgs {
@ -42,6 +45,7 @@ public:
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
const Data::WallPaper &paper, const Data::WallPaper &paper,
BackgroundPreviewArgs args = {}); BackgroundPreviewArgs args = {});
~BackgroundPreviewBox();
static bool Start( static bool Start(
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
@ -54,6 +58,8 @@ protected:
void paintEvent(QPaintEvent *e) override; void paintEvent(QPaintEvent *e) override;
private: private:
struct OverridenStyle;
using Element = HistoryView::Element; using Element = HistoryView::Element;
not_null<HistoryView::ElementDelegate*> delegate(); not_null<HistoryView::ElementDelegate*> delegate();
HistoryView::Context elementContext() override; HistoryView::Context elementContext() override;
@ -75,11 +81,20 @@ private:
void paintImage(Painter &p); void paintImage(Painter &p);
void paintRadial(Painter &p); void paintRadial(Painter &p);
void paintTexts(Painter &p, crl::time ms); void paintTexts(Painter &p, crl::time ms);
void createBlurCheckbox(); void recreateBlurCheckbox();
int textsTop() const; int textsTop() const;
void startFadeInFrom(QPixmap previous); void startFadeInFrom(QPixmap previous);
void checkBlurAnimationStart(); void checkBlurAnimationStart();
[[nodiscard]] const style::Box &overridenStyle(bool dark);
void paletteReady();
void applyDarkMode(bool dark);
[[nodiscard]] OverridenStyle prepareOverridenStyle(bool dark);
void resetTitle();
void rebuildButtons(bool dark);
void createDimmingSlider(bool dark);
const not_null<Window::SessionController*> _controller; const not_null<Window::SessionController*> _controller;
PeerData * const _forPeer = nullptr; PeerData * const _forPeer = nullptr;
FullMsgId _fromMessageId; FullMsgId _fromMessageId;
@ -98,8 +113,25 @@ private:
std::optional<QColor> _serviceBg; std::optional<QColor> _serviceBg;
object_ptr<Ui::Checkbox> _blur = { nullptr }; object_ptr<Ui::Checkbox> _blur = { nullptr };
rpl::variable<bool> _appNightMode;
rpl::variable<bool> _boxDarkMode;
std::unique_ptr<OverridenStyle> _light, _dark;
std::unique_ptr<style::palette> _lightPalette, _darkPalette;
bool _waitingForPalette = false;
object_ptr<Ui::SlideWrap<Ui::RpWidget>> _dimmingWrap = { nullptr };
Ui::RpWidget *_dimmingContent = nullptr;
Ui::MediaSlider *_dimmingSlider = nullptr;
int _dimmingIntensity = 0;
rpl::variable<int> _dimmingHeight = 0;
bool _dimmed = false;
bool _dimmingToggleScheduled = false;
FullMsgId _uploadId; FullMsgId _uploadId;
float64 _uploadProgress = 0.; float64 _uploadProgress = 0.;
rpl::lifetime _uploadLifetime; rpl::lifetime _uploadLifetime;
rpl::variable<QColor> _paletteServiceBg;
rpl::lifetime _serviceBgLifetime;
}; };

View file

@ -56,7 +56,9 @@ ThemeDocument::ThemeDocument(
_patternOpacity = params->patternOpacity(); _patternOpacity = params->patternOpacity();
_gradientRotation = params->gradientRotation(); _gradientRotation = params->gradientRotation();
_blurredWallPaper = params->isBlurred(); _blurredWallPaper = params->isBlurred();
_dimmingIntensity = (params->isPattern() || !_serviceWidth) _dimmingIntensity = (!params->document()
|| params->isPattern()
|| !_serviceWidth)
? 0 ? 0
: std::max(params->patternIntensity(), 0); : std::max(params->patternIntensity(), 0);
} }

View file

@ -1308,3 +1308,18 @@ moreChatsBarClose: IconButton(defaultIconButton) {
color: windowBgOver; color: windowBgOver;
} }
} }
backgroundSwitchToDark: IconButton(defaultIconButton) {
width: 48px;
height: 48px;
icon: boxTitleCloseIcon;
iconOver: boxTitleCloseIconOver;
rippleAreaPosition: point(4px, 4px);
rippleAreaSize: 40px;
ripple: RippleAnimation(defaultRippleAnimation) {
color: windowBgOver;
}
}
backgroundSwitchToLight: backgroundSwitchToDark;

View file

@ -425,11 +425,12 @@ ChatStyle::ChatStyle(not_null<const style::palette*> isolated)
} }
void ChatStyle::apply(not_null<ChatTheme*> theme) { void ChatStyle::apply(not_null<ChatTheme*> theme) {
const auto themePalette = theme->palette(); applyCustomPalette(theme->palette());
assignPalette(themePalette }
? themePalette
: style::main_palette::get().get()); void ChatStyle::applyCustomPalette(const style::palette *palette) {
if (themePalette) { assignPalette(palette ? palette : style::main_palette::get().get());
if (palette) {
_defaultPaletteChangeLifetime.destroy(); _defaultPaletteChangeLifetime.destroy();
} else { } else {
style::PaletteChanged( style::PaletteChanged(

View file

@ -165,6 +165,7 @@ public:
explicit ChatStyle(not_null<const style::palette*> isolated); explicit ChatStyle(not_null<const style::palette*> isolated);
void apply(not_null<ChatTheme*> theme); void apply(not_null<ChatTheme*> theme);
void applyCustomPalette(const style::palette *palette);
void applyAdjustedServiceBg(QColor serviceBg); void applyAdjustedServiceBg(QColor serviceBg);
[[nodiscard]] rpl::producer<> paletteChanged() const { [[nodiscard]] rpl::producer<> paletteChanged() const {

View file

@ -18,6 +18,8 @@ namespace Theme {
namespace { namespace {
constexpr auto kMaxAccentColors = 3; constexpr auto kMaxAccentColors = 3;
constexpr auto kDayBaseFile = ":/gui/day-custom-base.tdesktop-theme"_cs;
constexpr auto kNightBaseFile = ":/gui/night-custom-base.tdesktop-theme"_cs;
const auto kColorizeIgnoredKeys = base::flat_set<QLatin1String>{ { const auto kColorizeIgnoredKeys = base::flat_set<QLatin1String>{ {
qstr("boxTextFgGood"), qstr("boxTextFgGood"),
@ -311,6 +313,40 @@ std::vector<QColor> DefaultAccentColors(EmbeddedType type) {
Unexpected("Type in Window::Theme::AccentColors."); Unexpected("Type in Window::Theme::AccentColors.");
} }
Fn<void(style::palette&)> PreparePaletteCallback(
bool dark,
std::optional<QColor> accent) {
return [=](style::palette &palette) {
using namespace Theme;
const auto &embedded = EmbeddedThemes();
const auto i = ranges::find(
embedded,
dark ? EmbeddedType::Night : EmbeddedType::Default,
&EmbeddedScheme::type);
Assert(i != end(embedded));
const auto colorizer = accent
? ColorizerFrom(*i, *accent)
: style::colorizer();
auto instance = Instance();
const auto loaded = LoadFromFile(
(dark ? kNightBaseFile : kDayBaseFile).utf16(),
&instance,
nullptr,
nullptr,
colorizer);
Assert(loaded);
palette.finalize();
palette = instance.palette;
};
}
Fn<void(style::palette&)> PrepareCurrentPaletteCallback() {
return [=, data = style::main_palette::save()](style::palette &palette) {
palette.load(data);
};
}
QByteArray AccentColors::serialize() const { QByteArray AccentColors::serialize() const {
auto result = QByteArray(); auto result = QByteArray();
if (_data.empty()) { if (_data.empty()) {

View file

@ -63,5 +63,10 @@ void Colorize(
[[nodiscard]] std::vector<EmbeddedScheme> EmbeddedThemes(); [[nodiscard]] std::vector<EmbeddedScheme> EmbeddedThemes();
[[nodiscard]] std::vector<QColor> DefaultAccentColors(EmbeddedType type); [[nodiscard]] std::vector<QColor> DefaultAccentColors(EmbeddedType type);
[[nodiscard]] Fn<void(style::palette&)> PreparePaletteCallback(
bool dark,
std::optional<QColor> accent);
[[nodiscard]] Fn<void(style::palette&)> PrepareCurrentPaletteCallback();
} // namespace Theme } // namespace Theme
} // namespace Window } // namespace Window

View file

@ -94,47 +94,10 @@ namespace {
constexpr auto kCustomThemesInMemory = 5; constexpr auto kCustomThemesInMemory = 5;
constexpr auto kMaxChatEntryHistorySize = 50; constexpr auto kMaxChatEntryHistorySize = 50;
constexpr auto kDayBaseFile = ":/gui/day-custom-base.tdesktop-theme"_cs;
constexpr auto kNightBaseFile = ":/gui/night-custom-base.tdesktop-theme"_cs;
[[nodiscard]] Fn<void(style::palette&)> PrepareCurrentCallback() {
const auto copy = std::make_shared<style::palette>();
return [=, data = style::main_palette::save()](style::palette &palette) {
palette.load(data);
};
}
[[nodiscard]] Fn<void(style::palette&)> PreparePaletteCallback(
bool dark,
std::optional<QColor> accent) {
return [=](style::palette &palette) {
using namespace Theme;
const auto &embedded = EmbeddedThemes();
const auto i = ranges::find(
embedded,
dark ? EmbeddedType::Night : EmbeddedType::Default,
&EmbeddedScheme::type);
Assert(i != end(embedded));
const auto colorizer = accent
? ColorizerFrom(*i, *accent)
: style::colorizer();
auto instance = Instance();
const auto loaded = LoadFromFile(
(dark ? kNightBaseFile : kDayBaseFile).utf16(),
&instance,
nullptr,
nullptr,
colorizer);
Assert(loaded);
palette.finalize();
palette = instance.palette;
};
}
[[nodiscard]] Ui::ChatThemeBubblesData PrepareBubblesData( [[nodiscard]] Ui::ChatThemeBubblesData PrepareBubblesData(
const Data::CloudTheme &theme, const Data::CloudTheme &theme,
Data::CloudThemeType type) { Data::CloudThemeType type) {
const auto i = theme.settings.find(type); const auto i = theme.settings.find(type);
return { return {
.colors = (i != end(theme.settings) .colors = (i != end(theme.settings)
@ -2264,8 +2227,8 @@ void SessionController::cacheChatTheme(
auto descriptor = Ui::ChatThemeDescriptor{ auto descriptor = Ui::ChatThemeDescriptor{
.key = key.theme, .key = key.theme,
.preparePalette = (data.id .preparePalette = (data.id
? PreparePaletteCallback(dark, i->second.accentColor) ? Theme::PreparePaletteCallback(dark, i->second.accentColor)
: PrepareCurrentCallback()), : Theme::PrepareCurrentPaletteCallback()),
.backgroundData = backgroundData(theme), .backgroundData = backgroundData(theme),
.bubblesData = PrepareBubblesData(data, type), .bubblesData = PrepareBubblesData(data, type),
.basedOnDark = dark, .basedOnDark = dark,