mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-15 21:57:10 +02:00
Support chat wallpaper set from gallery.
This commit is contained in:
parent
9b25973b49
commit
352ae5100a
17 changed files with 347 additions and 102 deletions
|
@ -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.";
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
};
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Add table
Reference in a new issue