Support chat wallpaper set from gallery.

This commit is contained in:
John Preston 2023-04-18 16:47:31 +04:00
parent 9b25973b49
commit 352ae5100a
17 changed files with 347 additions and 102 deletions

View File

@ -729,7 +729,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_settings_manage_enabled_dictionary" = "Dictionary is enabled";
"lng_settings_manage_remove_dictionary" = "Remove Dictionary";
"lng_backgrounds_header" = "Choose your new chat background";
"lng_backgrounds_header" = "Choose Wallpaper";
"lng_theme_sure_keep" = "Keep this theme?";
"lng_theme_reverting#one" = "Reverting to the old theme in {count} second.";
"lng_theme_reverting#other" = "Reverting to the old theme in {count} seconds.";

View File

@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h"
#include "apiwrap.h"
#include "mtproto/sender.h"
#include "data/data_peer.h"
#include "data/data_session.h"
#include "data/data_file_origin.h"
#include "data/data_document.h"
@ -61,11 +62,15 @@ class BackgroundBox::Inner final : public Ui::RpWidget {
public:
Inner(
QWidget *parent,
not_null<Main::Session*> session);
not_null<Main::Session*> session,
PeerData *forPeer);
~Inner();
rpl::producer<Data::WallPaper> chooseEvents() const;
rpl::producer<Data::WallPaper> removeRequests() const;
[[nodiscard]] rpl::producer<Data::WallPaper> chooseEvents() const;
[[nodiscard]] rpl::producer<Data::WallPaper> removeRequests() const;
[[nodiscard]] auto resolveResetCustomPaper() const
->std::optional<Data::WallPaper>;
void removePaper(const Data::WallPaper &data);
@ -109,6 +114,7 @@ private:
void resizeToContentAndPreload();
void updatePapers();
void requestPapers();
void pushResetCustomPaper();
void sortPapers();
void paintPaper(
QPainter &p,
@ -118,9 +124,13 @@ private:
void validatePaperThumbnail(const Paper &paper) const;
const not_null<Main::Session*> _session;
PeerData * const _forPeer = nullptr;
MTP::Sender _api;
std::vector<Paper> _papers;
uint64 _currentId = 0;
uint64 _insertedResetId = 0;
Selection _over;
Selection _overDown;
@ -133,8 +143,10 @@ private:
BackgroundBox::BackgroundBox(
QWidget*,
not_null<Window::SessionController*> controller)
: _controller(controller) {
not_null<Window::SessionController*> controller,
PeerData *forPeer)
: _controller(controller)
, _forPeer(forPeer) {
}
void BackgroundBox::prepare() {
@ -145,14 +157,12 @@ void BackgroundBox::prepare() {
setDimensions(st::boxWideWidth, st::boxMaxListHeight);
_inner = setInnerWidget(
object_ptr<Inner>(this, &_controller->session()),
object_ptr<Inner>(this, &_controller->session(), _forPeer),
st::backgroundScroll);
_inner->chooseEvents(
) | rpl::start_with_next([=](const Data::WallPaper &paper) {
_controller->show(
Box<BackgroundPreviewBox>(_controller, paper),
Ui::LayerOption::KeepOther);
chosen(paper);
}, _inner->lifetime());
_inner->removeRequests(
@ -161,6 +171,74 @@ void BackgroundBox::prepare() {
}, _inner->lifetime());
}
bool BackgroundBox::hasDefaultForPeer() const {
Expects(_forPeer != nullptr);
const auto paper = _forPeer->wallPaper();
if (!paper) {
return true;
}
const auto reset = _inner->resolveResetCustomPaper();
Assert(reset.has_value());
return (paper->id() == reset->id());
}
bool BackgroundBox::chosenDefaultForPeer(
const Data::WallPaper &paper) const {
if (!_forPeer) {
return false;
}
const auto reset = _inner->resolveResetCustomPaper();
Assert(reset.has_value());
return (paper.id() == reset->id());
}
void BackgroundBox::chosen(const Data::WallPaper &paper) {
if (chosenDefaultForPeer(paper)) {
if (!hasDefaultForPeer()) {
const auto reset = crl::guard(this, [=](Fn<void()> close) {
resetForPeer();
close();
});
_controller->show(Ui::MakeConfirmBox({
.text = u"Are you sure you want to reset the wallpaper?"_q,
.confirmed = reset,
.confirmText = u"Reset"_q,
}));
} else {
closeBox();
}
return;
}
_controller->show(
Box<BackgroundPreviewBox>(
_controller,
paper,
BackgroundPreviewArgs{ _forPeer }),
Ui::LayerOption::KeepOther);
}
void BackgroundBox::resetForPeer() {
const auto api = &_controller->session().api();
using Flag = MTPmessages_SetChatWallPaper::Flag;
api->request(MTPmessages_SetChatWallPaper(
MTP_flags(0),
_forPeer->input,
MTPInputWallPaper(),
MTPWallPaperSettings(),
MTPint()
)).done([=](const MTPUpdates &result) {
api->applyUpdates(result);
}).send();
const auto weak = Ui::MakeWeak(this);
_forPeer->setWallPaper(std::nullopt);
if (weak) {
_controller->finishChatThemeEdit(_forPeer);
}
}
void BackgroundBox::removePaper(const Data::WallPaper &paper) {
const auto session = &_controller->session();
const auto remove = [=, weak = Ui::MakeWeak(this)](Fn<void()> &&close) {
@ -186,17 +264,19 @@ void BackgroundBox::removePaper(const Data::WallPaper &paper) {
BackgroundBox::Inner::Inner(
QWidget *parent,
not_null<Main::Session*> session)
not_null<Main::Session*> session,
PeerData *forPeer)
: RpWidget(parent)
, _session(session)
, _forPeer(forPeer)
, _api(&_session->mtp())
, _check(std::make_unique<Ui::RoundCheckbox>(st::overviewCheck, [=] { update(); })) {
_check->setChecked(true, anim::type::instant);
if (_session->data().wallpapers().empty()) {
resize(st::boxWideWidth, 2 * (st::backgroundSize.height() + st::backgroundPadding) + st::backgroundPadding);
} else {
resize(st::boxWideWidth, 2 * (st::backgroundSize.height() + st::backgroundPadding) + st::backgroundPadding);
Window::Theme::IsNightModeValue(
) | rpl::start_with_next([=] {
updatePapers();
}
}, lifetime());
requestPapers();
_session->downloaderTaskFinished(
@ -219,6 +299,7 @@ BackgroundBox::Inner::Inner(
}
}, lifetime());
setMouseTracking(true);
}
@ -232,27 +313,78 @@ void BackgroundBox::Inner::requestPapers() {
}).send();
}
auto BackgroundBox::Inner::resolveResetCustomPaper() const
-> std::optional<Data::WallPaper> {
if (!_forPeer) {
return {};
}
const auto nonCustom = Window::Theme::Background()->paper();
const auto themeEmoji = _forPeer->themeEmoji();
if (themeEmoji.isEmpty()) {
return nonCustom;
}
const auto &themes = _forPeer->owner().cloudThemes();
const auto theme = themes.themeForEmoji(themeEmoji);
if (!theme) {
return nonCustom;
}
using Type = Data::CloudTheme::Type;
const auto dark = Window::Theme::IsNightMode();
const auto i = theme->settings.find(dark ? Type::Dark : Type::Light);
if (i != end(theme->settings) && i->second.paper) {
return *i->second.paper;
}
return nonCustom;
}
void BackgroundBox::Inner::pushResetCustomPaper() {
if (const auto reset = resolveResetCustomPaper()) {
_insertedResetId = reset->id();
const auto j = ranges::find(
_papers,
_insertedResetId,
[](const Paper &paper) { return paper.data.id(); });
if (j != end(_papers)) {
j->data = j->data.withParamsFrom(*reset);
} else {
_papers.insert(begin(_papers), Paper{ *reset });
}
}
}
void BackgroundBox::Inner::sortPapers() {
const auto current = Window::Theme::Background()->id();
const auto night = Window::Theme::IsNightMode();
const auto currentCustom = _forPeer ? _forPeer->wallPaper() : nullptr;
_currentId = currentCustom
? currentCustom->id()
: _insertedResetId
? _insertedResetId
: Window::Theme::Background()->id();
const auto dark = Window::Theme::IsNightMode();
ranges::stable_sort(_papers, std::greater<>(), [&](const Paper &paper) {
const auto &data = paper.data;
return std::make_tuple(
data.id() == current,
night ? data.isDark() : !data.isDark(),
_insertedResetId && (_insertedResetId == data.id()),
data.id() == _currentId,
dark ? data.isDark() : !data.isDark(),
Data::IsDefaultWallPaper(data),
!data.isDefault() && !Data::IsLegacy1DefaultWallPaper(data),
Data::IsLegacy3DefaultWallPaper(data),
Data::IsLegacy2DefaultWallPaper(data),
Data::IsLegacy1DefaultWallPaper(data));
});
if (!_papers.empty() && _papers.front().data.id() == current) {
if (!_papers.empty()
&& _papers.front().data.id() == _currentId
&& !currentCustom
&& !_insertedResetId) {
_papers.front().data = _papers.front().data.withParamsFrom(
Window::Theme::Background()->paper());
}
}
void BackgroundBox::Inner::updatePapers() {
if (_session->data().wallpapers().empty()) {
return;
}
_over = _overDown = Selection();
_papers = _session->data().wallpapers(
@ -261,6 +393,7 @@ void BackgroundBox::Inner::updatePapers() {
}) | ranges::views::transform([](const Data::WallPaper &paper) {
return Paper{ paper };
}) | ranges::to_vector;
pushResetCustomPaper();
sortPapers();
resizeToContentAndPreload();
}
@ -373,7 +506,7 @@ void BackgroundBox::Inner::paintPaper(
}
const auto over = !v::is_null(_overDown) ? _overDown : _over;
if (paper.data.id() == Window::Theme::Background()->id()) {
if (paper.data.id() == _currentId) {
const auto checkLeft = x + st::backgroundSize.width() - st::overviewCheckSkip - st::overviewCheck.size;
const auto checkTop = y + st::backgroundSize.height() - st::overviewCheckSkip - st::overviewCheck.size;
_check->paint(p, checkLeft, checkTop, width());
@ -415,14 +548,13 @@ void BackgroundBox::Inner::mouseMoveEvent(QMouseEvent *e) {
- st::stickerPanDeleteIconBg.width();
const auto deleteBottom = row * (height + skip) + skip
+ st::stickerPanDeleteIconBg.height();
const auto currentId = Window::Theme::Background()->id();
const auto inDelete = (x >= deleteLeft)
&& (y < deleteBottom)
&& Data::IsCloudWallPaper(data)
&& !Data::IsDefaultWallPaper(data)
&& !Data::IsLegacy2DefaultWallPaper(data)
&& !Data::IsLegacy3DefaultWallPaper(data)
&& (currentId != data.id());
&& (_currentId != data.id());
return (result >= _papers.size())
? Selection()
: inDelete

View File

@ -9,6 +9,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/abstract_box.h"
class PeerData;
namespace Window {
class SessionController;
} // namespace Window
@ -19,7 +21,10 @@ class WallPaper;
class BackgroundBox : public Ui::BoxContent {
public:
BackgroundBox(QWidget*, not_null<Window::SessionController*> controller);
BackgroundBox(
QWidget*,
not_null<Window::SessionController*> controller,
PeerData *forPeer = nullptr);
protected:
void prepare() override;
@ -27,10 +32,16 @@ protected:
private:
class Inner;
void chosen(const Data::WallPaper &paper);
[[nodiscard]] bool hasDefaultForPeer() const;
[[nodiscard]] bool chosenDefaultForPeer(
const Data::WallPaper &paper) const;
void removePaper(const Data::WallPaper &paper);
void resetForPeer();
const not_null<Window::SessionController*> _controller;
QPointer<Inner> _inner;
PeerData *_forPeer = nullptr;
};

View File

@ -164,23 +164,17 @@ BackgroundPreviewBox::BackgroundPreviewBox(
, _chatStyle(std::make_unique<Ui::ChatStyle>())
, _serviceHistory(_controller->session().data().history(
PeerData::kServiceNotificationsId))
, _service((_forPeer && !_fromMessageId)
? GenerateServiceItem(
delegate(),
_serviceHistory,
tr::lng_background_other_info(tr::now, lt_user, _forPeer->shortName()),
false)
: nullptr)
, _service(nullptr)
, _text1(GenerateTextItem(
delegate(),
_controller->session().data().history(PeerData::kServiceNotificationsId),
_serviceHistory,
(_forPeer
? tr::lng_background_apply1(tr::now)
: tr::lng_background_text1(tr::now)),
false))
, _text2(GenerateTextItem(
delegate(),
_controller->session().data().history(PeerData::kServiceNotificationsId),
_serviceHistory,
(_forPeer
? tr::lng_background_apply2(tr::now)
: tr::lng_background_text2(tr::now)),
@ -244,11 +238,7 @@ void BackgroundPreviewBox::prepare() {
setScaledFromThumb();
checkLoadedDocument();
if (_service) {
_service->initDimensions();
_service->resizeGetHeight(st::boxWideWidth);
}
_text1->setDisplayDate(!_service);
_text1->setDisplayDate(false);
_text1->initDimensions();
_text1->resizeGetHeight(st::boxWideWidth);
_text2->initDimensions();
@ -285,12 +275,15 @@ void BackgroundPreviewBox::createBlurCheckbox() {
}
void BackgroundPreviewBox::apply() {
const auto weak = Ui::MakeWeak(this);
if (_forPeer) {
applyForPeer();
} else {
applyForEveryone();
}
closeBox();
if (weak) {
closeBox();
}
}
void BackgroundPreviewBox::applyForPeer() {
@ -311,6 +304,7 @@ void BackgroundPreviewBox::applyForPeer() {
}).send();
_forPeer->setWallPaper(_paper);
_controller->finishChatThemeEdit(_forPeer);
}
void BackgroundPreviewBox::applyForEveryone() {
@ -435,8 +429,6 @@ void BackgroundPreviewBox::paintTexts(Painter &p, crl::time ms) {
if (_service) {
_service->draw(p, context);
p.translate(0, heights);
} else {
paintDate(p);
}
context.outbg = _text1->hasOutLayout();
@ -448,27 +440,6 @@ void BackgroundPreviewBox::paintTexts(Painter &p, crl::time ms) {
p.translate(0, height2);
}
void BackgroundPreviewBox::paintDate(Painter &p) {
const auto date = _text1->Get<HistoryView::DateBadge>();
if (!date || !_serviceBg) {
return;
}
auto hq = PainterHighQualityEnabler(p);
const auto text = date->text;
const auto bubbleHeight = st::msgServicePadding.top() + st::msgServiceFont->height + st::msgServicePadding.bottom();
const auto bubbleTop = st::msgServiceMargin.top();
const auto textWidth = st::msgServiceFont->width(text);
const auto bubbleWidth = st::msgServicePadding.left() + textWidth + st::msgServicePadding.right();
const auto bubbleLeft = (width() - bubbleWidth) / 2;
const auto radius = bubbleHeight / 2;
p.setPen(Qt::NoPen);
p.setBrush(*_serviceBg);
p.drawRoundedRect(bubbleLeft, bubbleTop, bubbleWidth, bubbleHeight, radius, radius);
p.setPen(st::msgServiceFg);
p.setFont(st::msgServiceFont);
p.drawText(bubbleLeft + st::msgServicePadding.left(), bubbleTop + st::msgServicePadding.top() + st::msgServiceFont->ascent, text);
}
void BackgroundPreviewBox::radialAnimationCallback(crl::time now) {
Expects(_paper.document() != nullptr);
@ -556,9 +527,29 @@ void BackgroundPreviewBox::updateServiceBg(const std::vector<QColor> &bg) {
green += color.green();
blue += color.blue();
}
_serviceBg = Ui::ThemeAdjustedColor(
st::msgServiceBg->c,
QColor(red / count, green / count, blue / count));
rpl::single(
rpl::empty
) | rpl::then(
style::PaletteChanged()
) | rpl::start_with_next([=] {
_serviceBg = Ui::ThemeAdjustedColor(
st::msgServiceBg->c,
QColor(red / count, green / count, blue / count));
_chatStyle->applyAdjustedServiceBg(*_serviceBg);
}, lifetime());
_service = GenerateServiceItem(
delegate(),
_serviceHistory,
((_forPeer && !_fromMessageId)
? tr::lng_background_other_info(
tr::now,
lt_user,
_forPeer->shortName())
: ItemDateText(_text1->data(), false)),
false);
_service->initDimensions();
_service->resizeGetHeight(st::boxWideWidth);
}
void BackgroundPreviewBox::checkLoadedDocument() {

View File

@ -73,7 +73,6 @@ private:
void paintImage(Painter &p);
void paintRadial(Painter &p);
void paintTexts(Painter &p, crl::time ms);
void paintDate(Painter &p);
void createBlurCheckbox();
int textsTop() const;
void startFadeInFrom(QPixmap previous);

View File

@ -1558,7 +1558,9 @@ bool HistoryWidget::updateStickersByEmoji() {
return (emoji != nullptr);
}
void HistoryWidget::toggleChooseChatTheme(not_null<PeerData*> peer) {
void HistoryWidget::toggleChooseChatTheme(
not_null<PeerData*> peer,
std::optional<bool> show) {
const auto update = [=] {
updateInlineBotQuery();
updateControlsGeometry();
@ -1567,7 +1569,7 @@ void HistoryWidget::toggleChooseChatTheme(not_null<PeerData*> peer) {
if (peer.get() != _peer) {
return;
} else if (_chooseTheme) {
if (isChoosingTheme()) {
if (isChoosingTheme() && !show.value_or(false)) {
const auto was = base::take(_chooseTheme);
if (Ui::InFocusChain(this)) {
setInnerFocus();
@ -1575,6 +1577,8 @@ void HistoryWidget::toggleChooseChatTheme(not_null<PeerData*> peer) {
update();
}
return;
} else if (!show.value_or(true)) {
return;
} else if (_voiceRecordBar->isActive()) {
controller()->showToast({ tr::lng_chat_theme_cant_voice(tr::now) });
return;

View File

@ -226,7 +226,9 @@ public:
void clearDelayedShowAtRequest();
void clearDelayedShowAt();
void toggleChooseChatTheme(not_null<PeerData*> peer);
void toggleChooseChatTheme(
not_null<PeerData*> peer,
std::optional<bool> show = std::nullopt);
[[nodiscard]] Ui::ChatTheme *customChatTheme() const;
void applyCloudDraft(History *history);

View File

@ -341,6 +341,9 @@ void ThemeDocument::prepareThumbnailFrom(
_patternOpacity);
original.setDevicePixelRatio(ratio);
}
if (_serviceWidth) {
original = Images::Circle(std::move(original));
}
_thumbnail = Ui::PixmapFromImage(std::move(original));
_thumbnailGood = good;
}

View File

@ -1231,8 +1231,10 @@ void MainWidget::clearChooseReportMessages() {
_history->setChooseReportMessagesDetails({}, nullptr);
}
void MainWidget::toggleChooseChatTheme(not_null<PeerData*> peer) {
_history->toggleChooseChatTheme(peer);
void MainWidget::toggleChooseChatTheme(
not_null<PeerData*> peer,
std::optional<bool> show) {
_history->toggleChooseChatTheme(peer, show);
}
bool MainWidget::showHistoryInDifferentWindow(

View File

@ -215,7 +215,9 @@ public:
Fn<void(MessageIdsList)> done);
void clearChooseReportMessages();
void toggleChooseChatTheme(not_null<PeerData*> peer);
void toggleChooseChatTheme(
not_null<PeerData*> peer,
std::optional<bool> show);
void showHistory(
PeerId peer,

View File

@ -439,6 +439,12 @@ void ChatStyle::apply(not_null<ChatTheme*> theme) {
}
}
void ChatStyle::applyAdjustedServiceBg(QColor serviceBg) {
auto r = 0, g = 0, b = 0, a = 0;
serviceBg.getRgb(&r, &g, &b, &a);
msgServiceBg().set(uchar(r), uchar(g), uchar(b), uchar(a));
}
void ChatStyle::assignPalette(not_null<const style::palette*> palette) {
*static_cast<style::palette*>(this) = *palette;
style::internal::resetIcons();

View File

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

View File

@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "ui/chat/choose_theme_controller.h"
#include "boxes/background_box.h"
#include "ui/rp_widget.h"
#include "ui/widgets/shadow.h"
#include "ui/widgets/labels.h"
@ -35,7 +36,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Ui {
namespace {
constexpr auto kDisableElement = "disable"_cs;
const auto kDisableElement = [] { return u"disable"_q; };
[[nodiscard]] QImage GeneratePreview(not_null<Ui::ChatTheme*> theme) {
const auto &background = theme->background();
@ -164,6 +165,7 @@ ChooseThemeController::ChooseThemeController(
, _topShadow(std::make_unique<PlainShadow>(parent))
, _content(_wrap->add(object_ptr<RpWidget>(_wrap.get())))
, _inner(CreateChild<RpWidget>(_content.get()))
, _disabledEmoji(Ui::Emoji::Find(QString::fromUtf8("\xe2\x9d\x8c")))
, _dark(Window::Theme::IsThemeDarkValue()) {
init(parent->sizeValue());
}
@ -239,29 +241,45 @@ void ChooseThemeController::initButtons() {
controls,
tr::lng_chat_theme_apply(),
st::defaultActiveButton);
const auto choose = CreateChild<RoundButton>(
controls,
rpl::single(u"Change Wallpaper"_q),
st::defaultActiveButton);
const auto skip = st::normalFont->spacew * 2;
controls->resize(
skip + cancel->width() + skip + apply->width() + skip,
skip + cancel->width() + skip + choose->width() + skip,
apply->height() + skip * 2);
rpl::combine(
controls->widthValue(),
cancel->widthValue(),
apply->widthValue()
apply->widthValue(),
choose->widthValue(),
_chosen.value()
) | rpl::start_with_next([=](
int outer,
int cancelWidth,
int applyWidth) {
const auto inner = skip + cancelWidth + skip + applyWidth + skip;
int applyWidth,
int chooseWidth,
QString chosen) {
const auto was = _peer->themeEmoji();
const auto now = (chosen == kDisableElement()) ? QString() : chosen;
const auto changed = (now != was);
apply->setVisible(changed);
choose->setVisible(!changed);
const auto shown = changed ? apply : choose;
const auto shownWidth = changed ? applyWidth : chooseWidth;
const auto inner = skip + cancelWidth + skip + shownWidth + skip;
const auto left = (outer - inner) / 2;
cancel->moveToLeft(left, 0);
apply->moveToRight(left, 0);
shown->moveToRight(left, 0);
}, controls->lifetime());
cancel->setClickedCallback([=] { close(); });
apply->setClickedCallback([=] {
if (const auto chosen = findChosen()) {
if (Ui::Emoji::Find(_peer->themeEmoji()) != chosen->emoji) {
const auto now = chosen->key ? _chosen : QString();
const auto was = _peer->themeEmoji();
const auto now = chosen->key ? _chosen.current() : QString();
if (was != now) {
_peer->setThemeEmoji(now);
if (chosen->theme) {
// Remember while changes propagate through event loop.
@ -278,6 +296,9 @@ void ChooseThemeController::initButtons() {
}
_controller->toggleChooseChatTheme(_peer);
});
choose->setClickedCallback([=] {
_controller->show(Box<BackgroundBox>(_controller, _peer));
});
}
void ChooseThemeController::paintEntry(QPainter &p, const Entry &entry) {
@ -338,7 +359,7 @@ void ChooseThemeController::initList() {
} else if (entry->key) {
return entry->emoji->text();
} else {
return kDisableElement.utf16();
return kDisableElement();
}
};
_inner->events(
@ -375,7 +396,7 @@ void ChooseThemeController::initList() {
const auto mouse = static_cast<QMouseEvent*>(event.get());
const auto entry = byPoint(mouse->pos());
const auto chosen = chosenText(entry);
if (entry && chosen == _pressed && chosen != _chosen) {
if (entry && chosen == _pressed && chosen != _chosen.current()) {
clearCurrentBackgroundState();
if (const auto was = findChosen()) {
was->chosen = false;
@ -465,13 +486,14 @@ void ChooseThemeController::clearCurrentBackgroundState() {
}
auto ChooseThemeController::findChosen() -> Entry* {
if (_chosen.isEmpty()) {
const auto chosen = _chosen.current();
if (chosen.isEmpty()) {
return nullptr;
}
for (auto &entry : _entries) {
if (!entry.key && _chosen == kDisableElement.utf16()) {
if (!entry.key && chosen == kDisableElement()) {
return &entry;
} else if (_chosen == entry.emoji->text()) {
} else if (chosen == entry.emoji->text()) {
return &entry;
}
}
@ -494,11 +516,14 @@ void ChooseThemeController::fill(
_inner->resize(full, skip + single.height() + skip);
const auto initial = Ui::Emoji::Find(_peer->themeEmoji());
if (!initial) {
_chosen = kDisableElement();
}
_dark.value(
) | rpl::start_with_next([=](bool dark) {
clearCurrentBackgroundState();
if (_chosen.isEmpty() && initial) {
if (_chosen.current().isEmpty() && initial) {
_chosen = initial->text();
}
@ -507,9 +532,9 @@ void ChooseThemeController::fill(
auto x = skip * 2;
_entries.push_back({
.preview = GenerateEmptyPreview(),
.emoji = Ui::Emoji::Find(QString::fromUtf8("\xe2\x9d\x8c")),
.emoji = _disabledEmoji,
.geometry = QRect(QPoint(x, skip), single),
.chosen = (_chosen == kDisableElement.utf16()),
.chosen = (_chosen.current() == kDisableElement()),
});
Assert(_entries.front().emoji != nullptr);
style::PaletteChanged(
@ -528,7 +553,7 @@ void ChooseThemeController::fill(
continue;
}
const auto key = ChatThemeKey{ theme.id, dark };
const auto isChosen = (_chosen == emoji->text());
const auto isChosen = (_chosen.current() == emoji->text());
_entries.push_back({
.key = key,
.emoji = emoji,
@ -552,7 +577,7 @@ void ChooseThemeController::fill(
const auto theme = data.get();
i->theme = std::move(data);
i->preview = GeneratePreview(theme);
if (_chosen == i->emoji->text()) {
if (_chosen.current() == i->emoji->text()) {
_controller->overridePeerTheme(
_peer,
i->theme,

View File

@ -66,9 +66,10 @@ private:
const not_null<RpWidget*> _content;
const not_null<RpWidget*> _inner;
const EmojiPtr _disabledEmoji = nullptr;
std::vector<Entry> _entries;
QString _pressed;
QString _chosen;
rpl::variable<QString> _chosen;
std::optional<QPoint> _pressPosition;
std::optional<QPoint> _dragStartPosition;
int _dragStartInnerLeft = 0;

View File

@ -95,6 +95,33 @@ struct ResolvedPaper {
}) | rpl::flatten_latest();
}
[[nodiscard]] rpl::producer<> DebouncedPaletteValue() {
return [=](auto consumer) {
auto lifetime = rpl::lifetime();
struct State {
base::has_weak_ptr guard;
bool scheduled = false;
};
const auto state = lifetime.make_state<State>();
consumer.put_next_copy(rpl::empty);
style::PaletteChanged(
) | rpl::start_with_next([=] {
if (state->scheduled) {
return;
}
state->scheduled = true;
Ui::PostponeCall(&state->guard, [=] {
state->scheduled = false;
consumer.put_next_copy(rpl::empty);
});
}, lifetime);
return lifetime;
};
}
struct ResolvedTheme {
std::optional<Data::CloudTheme> theme;
std::optional<ResolvedPaper> paper;
@ -111,9 +138,22 @@ struct ResolvedTheme {
) | rpl::map([](
std::optional<Data::CloudTheme> theme,
std::optional<ResolvedPaper> paper,
bool night) {
return ResolvedTheme{ std::move(theme), std::move(paper), night };
});
bool night) -> rpl::producer<ResolvedTheme> {
if (theme || !paper) {
return rpl::single<ResolvedTheme>({
std::move(theme),
std::move(paper),
night,
});
}
return DebouncedPaletteValue(
) | rpl::map([=] {
return ResolvedTheme{
.paper = paper,
.dark = night,
};
});
}) | rpl::flatten_latest();
}
} // namespace

View File

@ -97,9 +97,10 @@ 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&)> PrepareDefaultPaletteCallback() {
return [=](style::palette &palette) {
palette.reset();
[[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);
};
}
@ -788,6 +789,14 @@ SessionController::SessionController(
pushDefaultChatBackground();
}
}, _lifetime);
style::PaletteChanged(
) | rpl::start_with_next([=] {
for (auto &[key, value] : _customChatThemes) {
if (!key.theme.id) {
value.theme.reset();
}
}
}, _lifetime);
_authedName = session->user()->name();
session->changes().peerUpdates(
@ -1831,8 +1840,22 @@ void SessionController::showInNewWindow(
}
}
void SessionController::toggleChooseChatTheme(not_null<PeerData*> peer) {
content()->toggleChooseChatTheme(peer);
void SessionController::toggleChooseChatTheme(
not_null<PeerData*> peer,
std::optional<bool> show) {
content()->toggleChooseChatTheme(peer, show);
}
void SessionController::finishChatThemeEdit(not_null<PeerData*> peer) {
toggleChooseChatTheme(peer, false);
const auto weak = base::make_weak(this);
const auto history = activeChatCurrent().history();
if (!history || history->peer != peer) {
showPeerHistory(peer);
}
if (weak) {
hideLayer();
}
}
void SessionController::updateColumnLayout() {
@ -2082,7 +2105,7 @@ auto SessionController::cachedChatThemeValue(
return rpl::single(_defaultChatTheme);
}
const auto settings = data.settings.find(type);
if (!data.id && settings == end(data.settings)) {
if (data.id && settings == end(data.settings)) {
return rpl::single(_defaultChatTheme);
}
if (paper.isNull()
@ -2242,7 +2265,7 @@ void SessionController::cacheChatTheme(
.key = key.theme,
.preparePalette = (data.id
? PreparePaletteCallback(dark, i->second.accentColor)
: PrepareDefaultPaletteCallback()),
: PrepareCurrentCallback()),
.backgroundData = backgroundData(theme),
.bubblesData = PrepareBubblesData(data, type),
.basedOnDark = dark,

View File

@ -498,7 +498,10 @@ public:
not_null<PeerData*> peer,
MsgId msgId = ShowAtUnreadMsgId);
void toggleChooseChatTheme(not_null<PeerData*> peer);
void toggleChooseChatTheme(
not_null<PeerData*> peer,
std::optional<bool> show = std::nullopt);
void finishChatThemeEdit(not_null<PeerData*> peer);
[[nodiscard]] bool dialogsListFocused() const {
return _dialogsListFocused.current();