mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-02-22 08:16:56 +00:00
Start non-unique ChatTheme.
This commit is contained in:
parent
f3dd8c68b3
commit
3cd0f9d189
@ -1100,6 +1100,8 @@ PRIVATE
|
||||
window/window_slide_animation.cpp
|
||||
window/window_slide_animation.h
|
||||
window/window_top_bar_wrap.h
|
||||
window/themes/window_chat_theme.cpp
|
||||
window/themes/window_chat_theme.h
|
||||
window/themes/window_theme.cpp
|
||||
window/themes/window_theme.h
|
||||
window/themes/window_theme_editor.cpp
|
||||
|
@ -3505,7 +3505,7 @@ void ApiWrap::sharedMediaDone(
|
||||
parsed.fullCount
|
||||
));
|
||||
if (type == SharedMediaType::Pinned && !parsed.messageIds.empty()) {
|
||||
peer->setHasPinnedMessages(true);
|
||||
peer->owner().history(peer)->setHasPinnedMessages(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -590,6 +590,7 @@ void BackgroundPreviewBox::paintTexts(Painter &p, crl::time ms) {
|
||||
const auto height1 = _text1->height();
|
||||
const auto height2 = _text2->height();
|
||||
const auto context = HistoryView::PaintContext{
|
||||
.st = style::main_palette::get(),
|
||||
.bubblesPattern = nullptr, // #TODO bubbles
|
||||
.viewport = rect(),
|
||||
.clip = rect(),
|
||||
|
@ -26,7 +26,7 @@ object_ptr<Window::SectionWidget> TabbedMemento::createWidget(
|
||||
TabbedSection::TabbedSection(
|
||||
QWidget *parent,
|
||||
not_null<Window::SessionController*> controller)
|
||||
: Window::SectionWidget(parent, controller, PaintedBackground::Custom)
|
||||
: Window::SectionWidget(parent, controller)
|
||||
, _selector(controller->tabbedSelector()) {
|
||||
_selector->setParent(this);
|
||||
_selector->setRoundRadius(0);
|
||||
|
@ -57,7 +57,7 @@ struct PeerUpdate {
|
||||
Notifications = (1U << 4),
|
||||
Migration = (1U << 5),
|
||||
UnavailableReason = (1U << 6),
|
||||
PinnedMessages = (1U << 7),
|
||||
ChatThemeEmoji = (1U << 7),
|
||||
IsBlocked = (1U << 8),
|
||||
MessagesTTL = (1U << 9),
|
||||
|
||||
@ -118,8 +118,9 @@ struct HistoryUpdate {
|
||||
BotKeyboard = (1U << 11),
|
||||
CloudDraft = (1U << 12),
|
||||
LocalDraftSet = (1U << 13),
|
||||
PinnedMessages = (1U << 14),
|
||||
|
||||
LastUsedBit = (1U << 13),
|
||||
LastUsedBit = (1U << 14),
|
||||
};
|
||||
using Flags = base::flags<Flag>;
|
||||
friend inline constexpr auto is_flag_type(Flag) { return true; }
|
||||
|
@ -37,10 +37,9 @@ CloudTheme CloudTheme::Parse(
|
||||
const auto document = data.vdocument();
|
||||
const auto paper = [&]() -> std::optional<WallPaper> {
|
||||
if (const auto settings = data.vsettings()) {
|
||||
settings->match([&](const MTPDthemeSettings &data) {
|
||||
return settings->match([&](const MTPDthemeSettings &data) {
|
||||
return data.vwallpaper()
|
||||
? std::make_optional(
|
||||
WallPaper::Create(session, *data.vwallpaper()))
|
||||
? WallPaper::Create(session, *data.vwallpaper())
|
||||
: std::nullopt;
|
||||
});
|
||||
}
|
||||
@ -61,12 +60,27 @@ CloudTheme CloudTheme::Parse(
|
||||
};
|
||||
const auto accentColor = [&]() -> std::optional<QColor> {
|
||||
if (const auto settings = data.vsettings()) {
|
||||
settings->match([&](const MTPDthemeSettings &data) {
|
||||
return settings->match([&](const MTPDthemeSettings &data) {
|
||||
return ColorFromSerialized(data.vaccent_color().v);
|
||||
});
|
||||
}
|
||||
return {};
|
||||
};
|
||||
const auto basedOnDark = [&] {
|
||||
if (const auto settings = data.vsettings()) {
|
||||
return settings->match([&](const MTPDthemeSettings &data) {
|
||||
return data.vbase_theme().match([](
|
||||
const MTPDbaseThemeNight &) {
|
||||
return true;
|
||||
}, [](const MTPDbaseThemeTinted &) {
|
||||
return true;
|
||||
}, [](const auto &) {
|
||||
return false;
|
||||
});
|
||||
});
|
||||
}
|
||||
return false;
|
||||
};
|
||||
return {
|
||||
.id = data.vid().v,
|
||||
.accessHash = data.vaccess_hash().v,
|
||||
@ -82,6 +96,7 @@ CloudTheme CloudTheme::Parse(
|
||||
.outgoingMessagesColors = (parseSettings
|
||||
? outgoingMessagesColors()
|
||||
: std::vector<QColor>()),
|
||||
.basedOnDark = parseSettings && basedOnDark(),
|
||||
};
|
||||
}
|
||||
|
||||
@ -368,6 +383,24 @@ std::optional<ChatTheme> CloudThemes::themeForEmoji(
|
||||
return (i != end(_chatThemes)) ? std::make_optional(*i) : std::nullopt;
|
||||
}
|
||||
|
||||
rpl::producer<std::optional<ChatTheme>> CloudThemes::themeForEmojiValue(
|
||||
const QString &emoji) {
|
||||
if (emoji.isEmpty()) {
|
||||
return rpl::single<std::optional<ChatTheme>>(std::nullopt);
|
||||
} else if (auto result = themeForEmoji(emoji)) {
|
||||
return rpl::single(std::move(result));
|
||||
}
|
||||
refreshChatThemes();
|
||||
return rpl::single<std::optional<ChatTheme>>(
|
||||
std::nullopt
|
||||
) | rpl::then(chatThemesUpdated(
|
||||
) | rpl::map([=] {
|
||||
return themeForEmoji(emoji);
|
||||
}) | rpl::filter([](const std::optional<ChatTheme> &theme) {
|
||||
return theme.has_value();
|
||||
}) | rpl::take(1));
|
||||
}
|
||||
|
||||
void CloudThemes::parseChatThemes(const QVector<MTPChatTheme> &list) {
|
||||
_chatThemes.clear();
|
||||
_chatThemes.reserve(list.size());
|
||||
|
@ -32,9 +32,11 @@ struct CloudTheme {
|
||||
DocumentId documentId = 0;
|
||||
UserId createdBy = 0;
|
||||
int usersCount = 0;
|
||||
|
||||
std::optional<WallPaper> paper;
|
||||
std::optional<QColor> accentColor;
|
||||
std::vector<QColor> outgoingMessagesColors;
|
||||
bool basedOnDark = false;
|
||||
|
||||
static CloudTheme Parse(
|
||||
not_null<Main::Session*> session,
|
||||
@ -69,6 +71,8 @@ public:
|
||||
[[nodiscard]] rpl::producer<> chatThemesUpdated() const;
|
||||
[[nodiscard]] std::optional<ChatTheme> themeForEmoji(
|
||||
const QString &emoji) const;
|
||||
[[nodiscard]] rpl::producer<std::optional<ChatTheme>> themeForEmojiValue(
|
||||
const QString &emoji);
|
||||
|
||||
void applyUpdate(const MTPTheme &theme);
|
||||
|
||||
|
@ -528,15 +528,6 @@ bool PeerData::canEditMessagesIndefinitely() const {
|
||||
Unexpected("Peer type in PeerData::canEditMessagesIndefinitely.");
|
||||
}
|
||||
|
||||
bool PeerData::hasPinnedMessages() const {
|
||||
return _hasPinnedMessages;
|
||||
}
|
||||
|
||||
void PeerData::setHasPinnedMessages(bool has) {
|
||||
_hasPinnedMessages = has;
|
||||
session().changes().peerUpdated(this, UpdateFlag::PinnedMessages);
|
||||
}
|
||||
|
||||
bool PeerData::canExportChatHistory() const {
|
||||
if (isRepliesChat()) {
|
||||
return false;
|
||||
@ -1014,10 +1005,14 @@ PeerId PeerData::groupCallDefaultJoinAs() const {
|
||||
}
|
||||
|
||||
void PeerData::setThemeEmoji(const QString &emoji) {
|
||||
if (_themeEmoji == emoji) {
|
||||
return;
|
||||
}
|
||||
_themeEmoji = emoji;
|
||||
if (!emoji.isEmpty() && !owner().cloudThemes().themeForEmoji(emoji)) {
|
||||
owner().cloudThemes().refreshChatThemes();
|
||||
}
|
||||
session().changes().peerUpdated(this, UpdateFlag::ChatThemeEmoji);
|
||||
}
|
||||
|
||||
const QString &PeerData::themeEmoji() const {
|
||||
@ -1166,7 +1161,7 @@ void SetTopPinnedMessageId(not_null<PeerData*> peer, MsgId messageId) {
|
||||
Storage::SharedMediaType::Pinned,
|
||||
messageId,
|
||||
{ messageId, ServerMaxMsgId }));
|
||||
peer->setHasPinnedMessages(true);
|
||||
peer->owner().history(peer)->setHasPinnedMessages(true);
|
||||
}
|
||||
|
||||
FullMsgId ResolveTopPinnedId(
|
||||
|
@ -406,9 +406,6 @@ public:
|
||||
[[nodiscard]] bool canPinMessages() const;
|
||||
[[nodiscard]] bool canEditMessagesIndefinitely() const;
|
||||
|
||||
[[nodiscard]] bool hasPinnedMessages() const;
|
||||
void setHasPinnedMessages(bool has);
|
||||
|
||||
[[nodiscard]] bool canExportChatHistory() const;
|
||||
|
||||
// Returns true if about text was changed.
|
||||
@ -511,7 +508,6 @@ private:
|
||||
crl::time _lastFullUpdate = 0;
|
||||
|
||||
TimeId _ttlPeriod = 0;
|
||||
bool _hasPinnedMessages = false;
|
||||
|
||||
Settings _settings = PeerSettings(PeerSetting::Unknown);
|
||||
BlockStatus _blockStatus = BlockStatus::Unknown;
|
||||
|
@ -181,12 +181,6 @@ WallPaperId WallPaper::id() const {
|
||||
return _id;
|
||||
}
|
||||
|
||||
std::optional<QColor> WallPaper::backgroundColor() const {
|
||||
return _backgroundColors.empty()
|
||||
? std::nullopt
|
||||
: std::make_optional(_backgroundColors.front());
|
||||
}
|
||||
|
||||
const std::vector<QColor> WallPaper::backgroundColors() const {
|
||||
return _backgroundColors;
|
||||
}
|
||||
|
@ -35,7 +35,6 @@ public:
|
||||
void setLocalImageAsThumbnail(std::shared_ptr<Image> image);
|
||||
|
||||
[[nodiscard]] WallPaperId id() const;
|
||||
[[nodiscard]] std::optional<QColor> backgroundColor() const;
|
||||
[[nodiscard]] const std::vector<QColor> backgroundColors() const;
|
||||
[[nodiscard]] DocumentData *document() const;
|
||||
[[nodiscard]] Image *localThumbnail() const;
|
||||
|
@ -164,7 +164,7 @@ void Widget::BottomButton::paintEvent(QPaintEvent *e) {
|
||||
Widget::Widget(
|
||||
QWidget *parent,
|
||||
not_null<Window::SessionController*> controller)
|
||||
: Window::AbstractSectionWidget(parent, controller, PaintedBackground::Custom)
|
||||
: Window::AbstractSectionWidget(parent, controller, nullptr)
|
||||
, _api(&controller->session().mtp())
|
||||
, _searchControls(this)
|
||||
, _mainMenuToggle(_searchControls, st::dialogsMenuToggle)
|
||||
|
@ -915,6 +915,7 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
|
||||
auto viewport = QRect(); // #TODO bubbles
|
||||
auto top = itemTop(from->get());
|
||||
auto context = HistoryView::PaintContext{
|
||||
.st = style::main_palette::get(),
|
||||
.bubblesPattern = nullptr,
|
||||
.viewport = viewport.translated(0, -top),
|
||||
.clip = clip.translated(0, -top),
|
||||
|
@ -275,7 +275,7 @@ Widget::Widget(
|
||||
QWidget *parent,
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<ChannelData*> channel)
|
||||
: Window::SectionWidget(parent, controller, PaintedBackground::Section)
|
||||
: Window::SectionWidget(parent, controller, rpl::single<PeerData*>(channel))
|
||||
, _scroll(this, st::historyScroll, false)
|
||||
, _fixedBar(this, controller, channel)
|
||||
, _fixedBarShadow(this)
|
||||
@ -306,11 +306,6 @@ Widget::Widget(
|
||||
updateAdaptiveLayout();
|
||||
}, lifetime());
|
||||
|
||||
controller->repaintBackgroundRequests(
|
||||
) | rpl::start_with_next([=] {
|
||||
update();
|
||||
}, lifetime());
|
||||
|
||||
_inner = _scroll->setOwnedWidget(object_ptr<InnerWidget>(this, controller, channel));
|
||||
_inner->showSearchSignal(
|
||||
) | rpl::start_with_next([=] {
|
||||
@ -475,7 +470,11 @@ void Widget::paintEvent(QPaintEvent *e) {
|
||||
//auto ms = crl::now();
|
||||
//_historyDownShown.step(ms);
|
||||
|
||||
SectionWidget::PaintBackground(controller(), this, e->rect());
|
||||
SectionWidget::PaintBackground(
|
||||
controller(),
|
||||
controller()->defaultChatTheme().get(), // #TODO themes
|
||||
this,
|
||||
e->rect());
|
||||
}
|
||||
|
||||
void Widget::onScroll() {
|
||||
|
@ -458,7 +458,7 @@ void History::unpinAllMessages() {
|
||||
Storage::SharedMediaRemoveAll(
|
||||
peer->id,
|
||||
Storage::SharedMediaType::Pinned));
|
||||
peer->setHasPinnedMessages(false);
|
||||
setHasPinnedMessages(false);
|
||||
for (const auto &message : _messages) {
|
||||
if (message->isPinned()) {
|
||||
message->setIsPinned(false);
|
||||
@ -751,7 +751,7 @@ not_null<HistoryItem*> History::addNewToBack(
|
||||
item->id,
|
||||
{ from, till }));
|
||||
if (sharedMediaTypes.test(Storage::SharedMediaType::Pinned)) {
|
||||
peer->setHasPinnedMessages(true);
|
||||
setHasPinnedMessages(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1023,7 +1023,7 @@ void History::applyServiceChanges(
|
||||
Storage::SharedMediaType::Pinned,
|
||||
{ id },
|
||||
{ id, ServerMaxMsgId }));
|
||||
peer->setHasPinnedMessages(true);
|
||||
setHasPinnedMessages(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -1401,7 +1401,7 @@ void History::addToSharedMedia(
|
||||
std::move(medias[i]),
|
||||
{ from, till }));
|
||||
if (type == Storage::SharedMediaType::Pinned) {
|
||||
peer->setHasPinnedMessages(true);
|
||||
setHasPinnedMessages(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3123,6 +3123,15 @@ void History::removeBlock(not_null<HistoryBlock*> block) {
|
||||
}
|
||||
}
|
||||
|
||||
bool History::hasPinnedMessages() const {
|
||||
return _hasPinnedMessages;
|
||||
}
|
||||
|
||||
void History::setHasPinnedMessages(bool has) {
|
||||
_hasPinnedMessages = has;
|
||||
session().changes().historyUpdated(this, UpdateFlag::PinnedMessages);
|
||||
}
|
||||
|
||||
History::~History() = default;
|
||||
|
||||
HistoryBlock::HistoryBlock(not_null<History*> history)
|
||||
|
@ -412,6 +412,9 @@ public:
|
||||
void setInboxReadTill(MsgId upTo);
|
||||
std::optional<int> countStillUnreadLocal(MsgId readTillId) const;
|
||||
|
||||
[[nodiscard]] bool hasPinnedMessages() const;
|
||||
void setHasPinnedMessages(bool has);
|
||||
|
||||
// Still public data.
|
||||
std::deque<std::unique_ptr<HistoryBlock>> blocks;
|
||||
|
||||
@ -581,6 +584,7 @@ private:
|
||||
|
||||
bool _unreadMark = false;
|
||||
bool _fakeUnreadWhileOpened = false;
|
||||
bool _hasPinnedMessages = false;
|
||||
|
||||
// A pointer to the block that is currently being built.
|
||||
// We hold this pointer so we can destroy it while building
|
||||
|
@ -615,7 +615,8 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
||||
auto item = view->data();
|
||||
|
||||
auto top = mtop + block->y() + view->y();
|
||||
auto context = _controller->bubblesContext({
|
||||
auto context = _controller->preparePaintContext({
|
||||
.theme = _controller->defaultChatTheme().get(), // #TODO themes
|
||||
.visibleAreaTop = _visibleAreaTop,
|
||||
.visibleAreaTopGlobal = visibleAreaTopGlobal,
|
||||
.clip = clip,
|
||||
@ -663,7 +664,8 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
||||
auto item = view->data();
|
||||
auto readTill = (HistoryItem*)nullptr;
|
||||
auto top = htop + block->y() + view->y();
|
||||
auto context = _controller->bubblesContext({
|
||||
auto context = _controller->preparePaintContext({
|
||||
.theme = _controller->defaultChatTheme().get(), // #TODO themes
|
||||
.visibleAreaTop = _visibleAreaTop,
|
||||
.visibleAreaTopGlobal = visibleAreaTopGlobal,
|
||||
.visibleAreaWidth = width(),
|
||||
|
@ -364,7 +364,7 @@ void HistoryItem::setIsPinned(bool pinned) {
|
||||
Storage::SharedMediaType::Pinned,
|
||||
id,
|
||||
{ id, id }));
|
||||
history()->peer->setHasPinnedMessages(true);
|
||||
history()->setHasPinnedMessages(true);
|
||||
} else {
|
||||
_flags &= ~MessageFlag::Pinned;
|
||||
history()->session().storage().remove(Storage::SharedMediaRemoveOne(
|
||||
@ -553,7 +553,7 @@ void HistoryItem::indexAsNewItem() {
|
||||
types,
|
||||
id));
|
||||
if (types.test(Storage::SharedMediaType::Pinned)) {
|
||||
_history->peer->setHasPinnedMessages(true);
|
||||
_history->setHasPinnedMessages(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -116,6 +116,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "window/window_session_controller.h"
|
||||
#include "window/window_slide_animation.h"
|
||||
#include "window/window_peer_menu.h"
|
||||
#include "window/themes/window_chat_theme.h"
|
||||
#include "inline_bots/inline_results_widget.h"
|
||||
#include "info/profile/info_profile_values.h" // SharedMediaCountValue.
|
||||
#include "chat_helpers/emoji_suggestions_widget.h"
|
||||
@ -166,12 +167,24 @@ const auto kPsaAboutPrefix = "cloud_lng_about_psa_";
|
||||
crl::time(1000) * 8);
|
||||
}
|
||||
|
||||
[[nodiscard]] rpl::producer<PeerData*> ActivePeerValue(
|
||||
not_null<Window::SessionController*> controller) {
|
||||
return controller->activeChatValue(
|
||||
) | rpl::map([](const Dialogs::Key &key) {
|
||||
const auto history = key.history();
|
||||
return history ? history->peer.get() : nullptr;
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
HistoryWidget::HistoryWidget(
|
||||
QWidget *parent,
|
||||
not_null<Window::SessionController*> controller)
|
||||
: Window::AbstractSectionWidget(parent, controller, PaintedBackground::Section)
|
||||
: Window::AbstractSectionWidget(
|
||||
parent,
|
||||
controller,
|
||||
ActivePeerValue(controller))
|
||||
, _api(&controller->session().mtp())
|
||||
, _updateEditTimeLeftDisplay([=] { updateField(); })
|
||||
, _fieldBarCancel(this, st::historyReplyCancel)
|
||||
@ -432,11 +445,6 @@ HistoryWidget::HistoryWidget(
|
||||
}
|
||||
}, lifetime());
|
||||
|
||||
controller->repaintBackgroundRequests(
|
||||
) | rpl::start_with_next([=] {
|
||||
update();
|
||||
}, lifetime());
|
||||
|
||||
session().data().newItemAdded(
|
||||
) | rpl::start_with_next([=](not_null<HistoryItem*> item) {
|
||||
newItemAdded(item);
|
||||
@ -504,40 +512,52 @@ HistoryWidget::HistoryWidget(
|
||||
}
|
||||
}, lifetime());
|
||||
|
||||
using HistoryUpdateFlag = Data::HistoryUpdate::Flag;
|
||||
session().changes().historyUpdates(
|
||||
Data::HistoryUpdate::Flag::MessageSent
|
||||
| Data::HistoryUpdate::Flag::ForwardDraft
|
||||
| Data::HistoryUpdate::Flag::BotKeyboard
|
||||
| Data::HistoryUpdate::Flag::CloudDraft
|
||||
| Data::HistoryUpdate::Flag::UnreadMentions
|
||||
| Data::HistoryUpdate::Flag::UnreadView
|
||||
| Data::HistoryUpdate::Flag::TopPromoted
|
||||
| Data::HistoryUpdate::Flag::LocalMessages
|
||||
HistoryUpdateFlag::MessageSent
|
||||
| HistoryUpdateFlag::ForwardDraft
|
||||
| HistoryUpdateFlag::BotKeyboard
|
||||
| HistoryUpdateFlag::CloudDraft
|
||||
| HistoryUpdateFlag::UnreadMentions
|
||||
| HistoryUpdateFlag::UnreadView
|
||||
| HistoryUpdateFlag::TopPromoted
|
||||
| HistoryUpdateFlag::LocalMessages
|
||||
| HistoryUpdateFlag::PinnedMessages
|
||||
) | rpl::filter([=](const Data::HistoryUpdate &update) {
|
||||
if (_migrated && update.history.get() == _migrated) {
|
||||
if (_pinnedTracker
|
||||
&& (update.flags & HistoryUpdateFlag::PinnedMessages)) {
|
||||
checkPinnedBarState();
|
||||
}
|
||||
}
|
||||
return (_history == update.history.get());
|
||||
}) | rpl::start_with_next([=](const Data::HistoryUpdate &update) {
|
||||
if (update.flags & Data::HistoryUpdate::Flag::MessageSent) {
|
||||
const auto flags = update.flags;
|
||||
if (flags & HistoryUpdateFlag::MessageSent) {
|
||||
synteticScrollToY(_scroll->scrollTopMax());
|
||||
}
|
||||
if (update.flags & Data::HistoryUpdate::Flag::ForwardDraft) {
|
||||
if (flags & HistoryUpdateFlag::ForwardDraft) {
|
||||
updateForwarding();
|
||||
}
|
||||
if (update.flags & Data::HistoryUpdate::Flag::BotKeyboard) {
|
||||
if (flags & HistoryUpdateFlag::BotKeyboard) {
|
||||
updateBotKeyboard(update.history);
|
||||
}
|
||||
if (update.flags & Data::HistoryUpdate::Flag::CloudDraft) {
|
||||
if (flags & HistoryUpdateFlag::CloudDraft) {
|
||||
applyCloudDraft(update.history);
|
||||
}
|
||||
if (update.flags & Data::HistoryUpdate::Flag::LocalMessages) {
|
||||
if (flags & HistoryUpdateFlag::LocalMessages) {
|
||||
updateSendButtonType();
|
||||
}
|
||||
if (update.flags & Data::HistoryUpdate::Flag::UnreadMentions) {
|
||||
if (flags & HistoryUpdateFlag::UnreadMentions) {
|
||||
updateUnreadMentionsVisibility();
|
||||
}
|
||||
if (update.flags & Data::HistoryUpdate::Flag::UnreadView) {
|
||||
if (flags & HistoryUpdateFlag::UnreadView) {
|
||||
unreadCountUpdated();
|
||||
}
|
||||
if (update.flags & Data::HistoryUpdate::Flag::TopPromoted) {
|
||||
if (_pinnedTracker && (flags & HistoryUpdateFlag::PinnedMessages)) {
|
||||
checkPinnedBarState();
|
||||
}
|
||||
if (flags & HistoryUpdateFlag::TopPromoted) {
|
||||
updateHistoryGeometry();
|
||||
updateControlsVisibility();
|
||||
updateControlsGeometry();
|
||||
@ -545,25 +565,27 @@ HistoryWidget::HistoryWidget(
|
||||
}
|
||||
}, lifetime());
|
||||
|
||||
using MessageUpdateFlag = Data::MessageUpdate::Flag;
|
||||
session().changes().messageUpdates(
|
||||
Data::MessageUpdate::Flag::Destroyed
|
||||
| Data::MessageUpdate::Flag::Edited
|
||||
| Data::MessageUpdate::Flag::ReplyMarkup
|
||||
| Data::MessageUpdate::Flag::BotCallbackSent
|
||||
MessageUpdateFlag::Destroyed
|
||||
| MessageUpdateFlag::Edited
|
||||
| MessageUpdateFlag::ReplyMarkup
|
||||
| MessageUpdateFlag::BotCallbackSent
|
||||
) | rpl::start_with_next([=](const Data::MessageUpdate &update) {
|
||||
if (update.flags & Data::MessageUpdate::Flag::Destroyed) {
|
||||
const auto flags = update.flags;
|
||||
if (flags & MessageUpdateFlag::Destroyed) {
|
||||
itemRemoved(update.item);
|
||||
return;
|
||||
}
|
||||
if (update.flags & Data::MessageUpdate::Flag::Edited) {
|
||||
if (flags & MessageUpdateFlag::Edited) {
|
||||
itemEdited(update.item);
|
||||
}
|
||||
if (update.flags & Data::MessageUpdate::Flag::ReplyMarkup) {
|
||||
if (flags & MessageUpdateFlag::ReplyMarkup) {
|
||||
if (_keyboard->forMsgId() == update.item->fullId()) {
|
||||
updateBotKeyboard(update.item->history(), true);
|
||||
}
|
||||
}
|
||||
if (update.flags & Data::MessageUpdate::Flag::BotCallbackSent) {
|
||||
if (flags & MessageUpdateFlag::BotCallbackSent) {
|
||||
botCallbackSent(update.item);
|
||||
}
|
||||
}, lifetime());
|
||||
@ -574,45 +596,38 @@ HistoryWidget::HistoryWidget(
|
||||
}
|
||||
});
|
||||
|
||||
using UpdateFlag = Data::PeerUpdate::Flag;
|
||||
using PeerUpdateFlag = Data::PeerUpdate::Flag;
|
||||
session().changes().peerUpdates(
|
||||
UpdateFlag::Rights
|
||||
| UpdateFlag::Migration
|
||||
| UpdateFlag::UnavailableReason
|
||||
| UpdateFlag::IsBlocked
|
||||
| UpdateFlag::Admins
|
||||
| UpdateFlag::Members
|
||||
| UpdateFlag::OnlineStatus
|
||||
| UpdateFlag::Notifications
|
||||
| UpdateFlag::ChannelAmIn
|
||||
| UpdateFlag::ChannelLinkedChat
|
||||
| UpdateFlag::Slowmode
|
||||
| UpdateFlag::BotStartToken
|
||||
| UpdateFlag::PinnedMessages
|
||||
| UpdateFlag::MessagesTTL
|
||||
PeerUpdateFlag::Rights
|
||||
| PeerUpdateFlag::Migration
|
||||
| PeerUpdateFlag::UnavailableReason
|
||||
| PeerUpdateFlag::IsBlocked
|
||||
| PeerUpdateFlag::Admins
|
||||
| PeerUpdateFlag::Members
|
||||
| PeerUpdateFlag::OnlineStatus
|
||||
| PeerUpdateFlag::Notifications
|
||||
| PeerUpdateFlag::ChannelAmIn
|
||||
| PeerUpdateFlag::ChannelLinkedChat
|
||||
| PeerUpdateFlag::Slowmode
|
||||
| PeerUpdateFlag::BotStartToken
|
||||
| PeerUpdateFlag::MessagesTTL
|
||||
) | rpl::filter([=](const Data::PeerUpdate &update) {
|
||||
if (_migrated && update.peer.get() == _migrated->peer) {
|
||||
if (_pinnedTracker
|
||||
&& (update.flags & UpdateFlag::PinnedMessages)) {
|
||||
checkPinnedBarState();
|
||||
}
|
||||
}
|
||||
return (update.peer.get() == _peer);
|
||||
}) | rpl::map([](const Data::PeerUpdate &update) {
|
||||
return update.flags;
|
||||
}) | rpl::start_with_next([=](Data::PeerUpdate::Flags flags) {
|
||||
if (flags & UpdateFlag::Rights) {
|
||||
if (flags & PeerUpdateFlag::Rights) {
|
||||
checkPreview();
|
||||
updateStickersByEmoji();
|
||||
updateFieldPlaceholder();
|
||||
}
|
||||
if (flags & UpdateFlag::Migration) {
|
||||
if (flags & PeerUpdateFlag::Migration) {
|
||||
handlePeerMigration();
|
||||
}
|
||||
if (flags & UpdateFlag::Notifications) {
|
||||
if (flags & PeerUpdateFlag::Notifications) {
|
||||
updateNotifyControls();
|
||||
}
|
||||
if (flags & UpdateFlag::UnavailableReason) {
|
||||
if (flags & PeerUpdateFlag::UnavailableReason) {
|
||||
const auto unavailable = _peer->computeUnavailableReason();
|
||||
if (!unavailable.isEmpty()) {
|
||||
controller->showBackFromStack();
|
||||
@ -620,26 +635,23 @@ HistoryWidget::HistoryWidget(
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (flags & UpdateFlag::BotStartToken) {
|
||||
if (flags & PeerUpdateFlag::BotStartToken) {
|
||||
updateControlsVisibility();
|
||||
updateControlsGeometry();
|
||||
}
|
||||
if (flags & UpdateFlag::Slowmode) {
|
||||
if (flags & PeerUpdateFlag::Slowmode) {
|
||||
updateSendButtonType();
|
||||
}
|
||||
if (flags & (UpdateFlag::IsBlocked
|
||||
| UpdateFlag::Admins
|
||||
| UpdateFlag::Members
|
||||
| UpdateFlag::OnlineStatus
|
||||
| UpdateFlag::Rights
|
||||
| UpdateFlag::ChannelAmIn
|
||||
| UpdateFlag::ChannelLinkedChat)) {
|
||||
if (flags & (PeerUpdateFlag::IsBlocked
|
||||
| PeerUpdateFlag::Admins
|
||||
| PeerUpdateFlag::Members
|
||||
| PeerUpdateFlag::OnlineStatus
|
||||
| PeerUpdateFlag::Rights
|
||||
| PeerUpdateFlag::ChannelAmIn
|
||||
| PeerUpdateFlag::ChannelLinkedChat)) {
|
||||
handlePeerUpdate();
|
||||
}
|
||||
if (_pinnedTracker && (flags & UpdateFlag::PinnedMessages)) {
|
||||
checkPinnedBarState();
|
||||
}
|
||||
if (flags & UpdateFlag::MessagesTTL) {
|
||||
if (flags & PeerUpdateFlag::MessagesTTL) {
|
||||
checkMessagesTTL();
|
||||
}
|
||||
}, lifetime());
|
||||
@ -4930,7 +4942,7 @@ void HistoryWidget::startItemRevealAnimations() {
|
||||
HistoryView::ListWidget::kItemRevealDuration,
|
||||
anim::easeOutCirc);
|
||||
if (item->out() || _history->peer->isSelf()) {
|
||||
controller()->rotateComplexGradientBackground();
|
||||
controller()->defaultChatTheme()->rotateComplexGradientBackground(); // #TODO themes
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -6874,7 +6886,11 @@ void HistoryWidget::paintEvent(QPaintEvent *e) {
|
||||
updateListSize();
|
||||
}
|
||||
|
||||
Window::SectionWidget::PaintBackground(controller(), this, e->rect());
|
||||
Window::SectionWidget::PaintBackground(
|
||||
controller(),
|
||||
controller()->defaultChatTheme().get(), // #TODO themes
|
||||
this,
|
||||
e->rect());
|
||||
|
||||
Painter p(this);
|
||||
const auto clip = e->rect();
|
||||
|
@ -193,6 +193,7 @@ struct DateBadge : public RuntimeComponent<DateBadge, Element> {
|
||||
};
|
||||
|
||||
struct PaintContext {
|
||||
not_null<const style::palette*> st;
|
||||
const Ui::BubblePattern *bubblesPattern = nullptr;
|
||||
QRect viewport;
|
||||
QRect clip;
|
||||
|
@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "window/window_adaptive.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "window/window_peer_menu.h"
|
||||
#include "window/themes/window_chat_theme.h"
|
||||
#include "main/main_session.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
@ -1445,7 +1446,7 @@ void ListWidget::startItemRevealAnimations() {
|
||||
kItemRevealDuration,
|
||||
anim::easeOutCirc);
|
||||
if (view->data()->out()) {
|
||||
controller()->rotateComplexGradientBackground();
|
||||
controller()->defaultChatTheme()->rotateComplexGradientBackground(); // #TODO themes
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1614,6 +1615,7 @@ void ListWidget::paintEvent(QPaintEvent *e) {
|
||||
auto viewport = QRect(); // #TODO bubbles
|
||||
auto top = itemTop(from->get());
|
||||
auto context = HistoryView::PaintContext{
|
||||
.st = style::main_palette::get(),
|
||||
.bubblesPattern = nullptr,
|
||||
.viewport = viewport.translated(0, -top),
|
||||
.clip = clip.translated(0, -top),
|
||||
|
@ -90,7 +90,7 @@ PinnedWidget::PinnedWidget(
|
||||
QWidget *parent,
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<History*> history)
|
||||
: Window::SectionWidget(parent, controller, PaintedBackground::Section)
|
||||
: Window::SectionWidget(parent, controller, history->peer)
|
||||
, _history(history->migrateToOrMe())
|
||||
, _migratedPeer(_history->peer->migrateFrom())
|
||||
, _topBar(this, controller)
|
||||
@ -457,7 +457,11 @@ void PinnedWidget::paintEvent(QPaintEvent *e) {
|
||||
const auto aboveHeight = _topBar->height();
|
||||
const auto bg = e->rect().intersected(
|
||||
QRect(0, aboveHeight, width(), height() - aboveHeight));
|
||||
SectionWidget::PaintBackground(controller(), this, bg);
|
||||
SectionWidget::PaintBackground(
|
||||
controller(),
|
||||
controller()->defaultChatTheme().get(), // #TODO themes
|
||||
this,
|
||||
bg);
|
||||
}
|
||||
|
||||
void PinnedWidget::onScroll() {
|
||||
|
@ -31,19 +31,21 @@ PinnedTracker::PinnedTracker(not_null<History*> history)
|
||||
: _history(history->migrateToOrMe())
|
||||
, _migratedPeer(_history->peer->migrateFrom()) {
|
||||
using namespace rpl::mappers;
|
||||
const auto has = [&](PeerData *peer) -> rpl::producer<bool> {
|
||||
const auto has = [&](History *history) -> rpl::producer<bool> {
|
||||
auto &changes = _history->session().changes();
|
||||
const auto flag = Data::PeerUpdate::Flag::PinnedMessages;
|
||||
if (!peer) {
|
||||
const auto flag = Data::HistoryUpdate::Flag::PinnedMessages;
|
||||
if (!history) {
|
||||
return rpl::single(false);
|
||||
}
|
||||
return changes.peerFlagsValue(peer, flag) | rpl::map([=] {
|
||||
return peer->hasPinnedMessages();
|
||||
return changes.historyFlagsValue(history, flag) | rpl::map([=] {
|
||||
return history->hasPinnedMessages();
|
||||
});
|
||||
};
|
||||
rpl::combine(
|
||||
has(_history->peer),
|
||||
has(_migratedPeer),
|
||||
has(_history),
|
||||
has(_migratedPeer
|
||||
? _history->owner().history(_migratedPeer).get()
|
||||
: nullptr),
|
||||
_1 || _2
|
||||
) | rpl::distinct_until_changed(
|
||||
) | rpl::start_with_next([=](bool has) {
|
||||
@ -97,9 +99,10 @@ void PinnedTracker::refreshViewer() {
|
||||
}
|
||||
refreshCurrentFromSlice();
|
||||
if (_slice.fullCount == 0) {
|
||||
_history->peer->setHasPinnedMessages(false);
|
||||
_history->setHasPinnedMessages(false);
|
||||
if (_migratedPeer) {
|
||||
_migratedPeer->setHasPinnedMessages(false);
|
||||
const auto to = _history->owner().history(_migratedPeer);
|
||||
to->setHasPinnedMessages(false);
|
||||
}
|
||||
}
|
||||
}, _dataLifetime);
|
||||
|
@ -146,7 +146,7 @@ RepliesWidget::RepliesWidget(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<History*> history,
|
||||
MsgId rootId)
|
||||
: Window::SectionWidget(parent, controller, PaintedBackground::Section)
|
||||
: Window::SectionWidget(parent, controller, history->peer)
|
||||
, _history(history)
|
||||
, _rootId(rootId)
|
||||
, _root(lookupRoot())
|
||||
@ -1511,7 +1511,11 @@ void RepliesWidget::paintEvent(QPaintEvent *e) {
|
||||
const auto aboveHeight = _topBar->height();
|
||||
const auto bg = e->rect().intersected(
|
||||
QRect(0, aboveHeight, width(), height() - aboveHeight));
|
||||
SectionWidget::PaintBackground(controller(), this, bg);
|
||||
SectionWidget::PaintBackground(
|
||||
controller(),
|
||||
controller()->defaultChatTheme().get(), // #TODO themes
|
||||
this,
|
||||
bg);
|
||||
}
|
||||
|
||||
void RepliesWidget::onScroll() {
|
||||
|
@ -90,7 +90,7 @@ ScheduledWidget::ScheduledWidget(
|
||||
QWidget *parent,
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<History*> history)
|
||||
: Window::SectionWidget(parent, controller, PaintedBackground::Section)
|
||||
: Window::SectionWidget(parent, controller, history->peer)
|
||||
, _history(history)
|
||||
, _scroll(this, st::historyScroll, false)
|
||||
, _topBar(this, controller)
|
||||
@ -996,7 +996,11 @@ void ScheduledWidget::paintEvent(QPaintEvent *e) {
|
||||
//auto ms = crl::now();
|
||||
//_historyDownShown.step(ms);
|
||||
|
||||
SectionWidget::PaintBackground(controller(), this, e->rect());
|
||||
SectionWidget::PaintBackground(
|
||||
controller(),
|
||||
controller()->defaultChatTheme().get(), // #TODO themes
|
||||
this,
|
||||
e->rect());
|
||||
}
|
||||
|
||||
void ScheduledWidget::onScroll() {
|
||||
|
@ -24,7 +24,7 @@ SectionWidget::SectionWidget(
|
||||
not_null<Window::SessionController*> window,
|
||||
Wrap wrap,
|
||||
not_null<Memento*> memento)
|
||||
: Window::SectionWidget(parent, window, PaintedBackground::Custom)
|
||||
: Window::SectionWidget(parent, window)
|
||||
, _content(this, window, wrap, memento) {
|
||||
init();
|
||||
}
|
||||
@ -34,7 +34,7 @@ SectionWidget::SectionWidget(
|
||||
not_null<Window::SessionController*> window,
|
||||
Wrap wrap,
|
||||
not_null<MoveMemento*> memento)
|
||||
: Window::SectionWidget(parent, window, PaintedBackground::Custom)
|
||||
: Window::SectionWidget(parent, window)
|
||||
, _content(memento->takeContent(this, wrap)) {
|
||||
init();
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ WrapWidget::WrapWidget(
|
||||
not_null<Window::SessionController*> window,
|
||||
Wrap wrap,
|
||||
not_null<Memento*> memento)
|
||||
: SectionWidget(parent, window, PaintedBackground::Custom)
|
||||
: SectionWidget(parent, window, rpl::producer<PeerData*>())
|
||||
, _wrap(wrap)
|
||||
, _controller(createController(window, memento->content()))
|
||||
, _topShadow(this) {
|
||||
|
@ -137,23 +137,9 @@ void MainWindow::setupNativeWindowFrame() {
|
||||
Core::App().settings().nativeWindowFrameChanges()
|
||||
);
|
||||
|
||||
using BackgroundUpdate = Window::Theme::BackgroundUpdate;
|
||||
auto themeChanges = Window::Theme::Background()->updates(
|
||||
) | rpl::filter([=](const BackgroundUpdate &update) {
|
||||
return update.type == BackgroundUpdate::Type::ApplyingTheme;
|
||||
}) | rpl::to_empty;
|
||||
|
||||
auto nightMode = rpl::single(
|
||||
rpl::empty_value()
|
||||
) | rpl::then(
|
||||
std::move(themeChanges)
|
||||
) | rpl::map([=] {
|
||||
return Window::Theme::IsNightMode();
|
||||
}) | rpl::distinct_until_changed();
|
||||
|
||||
rpl::combine(
|
||||
std::move(nativeFrame),
|
||||
std::move(nightMode)
|
||||
Window::Theme::IsNightModeValue()
|
||||
) | rpl::skip(1) | rpl::start_with_next([=](bool native, bool night) {
|
||||
validateWindowTheme(native, night);
|
||||
}, lifetime());
|
||||
|
@ -706,11 +706,17 @@ object_ptr<Ui::RpWidget> ForwardsPrivacyController::setupAboveWidget(
|
||||
|
||||
widget->paintRequest(
|
||||
) | rpl::start_with_next([=](QRect rect) {
|
||||
Window::SectionWidget::PaintBackground(_controller, widget, rect);
|
||||
// #TODO themes
|
||||
Window::SectionWidget::PaintBackground(
|
||||
_controller,
|
||||
_controller->defaultChatTheme().get(), // #TODO themes
|
||||
widget,
|
||||
rect);
|
||||
|
||||
Painter p(widget);
|
||||
const auto context = HistoryView::PaintContext{
|
||||
.bubblesPattern = nullptr, // #TODO bubbles
|
||||
.st = style::main_palette::get(),
|
||||
.bubblesPattern = nullptr,
|
||||
.viewport = widget->rect(),
|
||||
.clip = widget->rect(),
|
||||
.now = crl::now(),
|
||||
|
@ -565,6 +565,7 @@ void ConfirmContactBox::paintEvent(QPaintEvent *e) {
|
||||
p.fillRect(e->rect(), st::boxBg);
|
||||
|
||||
const auto context = HistoryView::PaintContext{
|
||||
.st = style::main_palette::get(),
|
||||
.bubblesPattern = nullptr, // #TODO bubbles
|
||||
.viewport = rect(),
|
||||
.clip = rect(),
|
||||
|
@ -9,27 +9,101 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "mainwidget.h"
|
||||
#include "ui/ui_utility.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_cloud_themes.h"
|
||||
#include "main/main_session.h"
|
||||
#include "window/section_memento.h"
|
||||
#include "window/window_slide_animation.h"
|
||||
#include "window/themes/window_theme.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "window/themes/window_theme.h"
|
||||
#include "window/themes/window_chat_theme.h"
|
||||
|
||||
#include <rpl/range.h>
|
||||
|
||||
namespace Window {
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] rpl::producer<QString> PeerThemeEmojiValue(
|
||||
not_null<PeerData*> peer) {
|
||||
return peer->session().changes().peerFlagsValue(
|
||||
peer,
|
||||
Data::PeerUpdate::Flag::ChatThemeEmoji
|
||||
) | rpl::map([=] {
|
||||
return peer->themeEmoji();
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] auto MaybeChatThemeDataValueFromPeer(
|
||||
not_null<PeerData*> peer)
|
||||
-> rpl::producer<std::optional<Data::ChatTheme>> {
|
||||
return PeerThemeEmojiValue(
|
||||
peer
|
||||
) | rpl::map([=](const QString &emoji)
|
||||
-> rpl::producer<std::optional<Data::ChatTheme>> {
|
||||
return peer->owner().cloudThemes().themeForEmojiValue(emoji);
|
||||
}) | rpl::flatten_latest();
|
||||
}
|
||||
|
||||
[[nodiscard]] auto MaybeCloudThemeValueFromPeer(
|
||||
not_null<PeerData*> peer)
|
||||
-> rpl::producer<std::optional<Data::CloudTheme>> {
|
||||
return rpl::combine(
|
||||
MaybeChatThemeDataValueFromPeer(peer),
|
||||
Theme::IsNightModeValue()
|
||||
) | rpl::map([](std::optional<Data::ChatTheme> theme, bool night) {
|
||||
return !theme
|
||||
? std::nullopt
|
||||
: night
|
||||
? std::make_optional(std::move(theme->dark))
|
||||
: std::make_optional(std::move(theme->light));
|
||||
});
|
||||
}
|
||||
|
||||
[[nodiscard]] auto ChatThemeValueFromPeer(
|
||||
not_null<SessionController*> controller,
|
||||
not_null<PeerData*> peer)
|
||||
-> rpl::producer<std::shared_ptr<Theme::ChatTheme>> {
|
||||
return MaybeCloudThemeValueFromPeer(
|
||||
peer
|
||||
) | rpl::map([=](std::optional<Data::CloudTheme> theme)
|
||||
-> rpl::producer<std::shared_ptr<Theme::ChatTheme>> {
|
||||
if (!theme) {
|
||||
return rpl::single(controller->defaultChatTheme());
|
||||
}
|
||||
return controller->cachedChatThemeValue(*theme);
|
||||
}) | rpl::flatten_latest(
|
||||
) | rpl::distinct_until_changed();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
AbstractSectionWidget::AbstractSectionWidget(
|
||||
QWidget *parent,
|
||||
not_null<SessionController*> controller,
|
||||
PaintedBackground paintedBackground)
|
||||
rpl::producer<PeerData*> peerForBackground)
|
||||
: RpWidget(parent)
|
||||
, _controller(controller) {
|
||||
if (paintedBackground == PaintedBackground::Section) {
|
||||
controller->repaintBackgroundRequests(
|
||||
) | rpl::start_with_next([=] {
|
||||
update();
|
||||
}, lifetime());
|
||||
}
|
||||
std::move(
|
||||
peerForBackground
|
||||
) | rpl::map([=](PeerData *peer) -> rpl::producer<> {
|
||||
if (!peer) {
|
||||
return rpl::never<>();
|
||||
}
|
||||
return ChatThemeValueFromPeer(
|
||||
controller,
|
||||
peer
|
||||
) | rpl::map([](const std::shared_ptr<Theme::ChatTheme> &theme) {
|
||||
return rpl::single(
|
||||
rpl::empty_value()
|
||||
) | rpl::then(
|
||||
theme->repaintBackgroundRequests()
|
||||
);
|
||||
}) | rpl::flatten_latest();
|
||||
}) | rpl::flatten_latest() | rpl::start_with_next([=] {
|
||||
update();
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
Main::Session &AbstractSectionWidget::session() const {
|
||||
@ -39,8 +113,18 @@ Main::Session &AbstractSectionWidget::session() const {
|
||||
SectionWidget::SectionWidget(
|
||||
QWidget *parent,
|
||||
not_null<Window::SessionController*> controller,
|
||||
PaintedBackground paintedBackground)
|
||||
: AbstractSectionWidget(parent, controller, paintedBackground) {
|
||||
rpl::producer<PeerData*> peerForBackground)
|
||||
: AbstractSectionWidget(parent, controller, std::move(peerForBackground)) {
|
||||
}
|
||||
|
||||
SectionWidget::SectionWidget(
|
||||
QWidget *parent,
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<PeerData*> peerForBackground)
|
||||
: AbstractSectionWidget(
|
||||
parent,
|
||||
controller,
|
||||
rpl::single(peerForBackground.get())) {
|
||||
}
|
||||
|
||||
void SectionWidget::setGeometryWithTopMoved(
|
||||
@ -101,6 +185,7 @@ QPixmap SectionWidget::grabForShowAnimation(
|
||||
|
||||
void SectionWidget::PaintBackground(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<Window::Theme::ChatTheme*> theme,
|
||||
not_null<QWidget*> widget,
|
||||
QRect clip) {
|
||||
Painter p(widget);
|
||||
@ -113,8 +198,8 @@ void SectionWidget::PaintBackground(
|
||||
const auto gradient = background->gradientForFill();
|
||||
const auto fill = QSize(widget->width(), controller->content()->height());
|
||||
auto fromy = controller->content()->backgroundFromY();
|
||||
auto state = controller->backgroundState(fill);
|
||||
const auto paintCache = [&](const CachedBackground &cache) {
|
||||
auto state = theme->backgroundState(fill);
|
||||
const auto paintCache = [&](const Theme::CachedBackground &cache) {
|
||||
const auto to = QRect(
|
||||
QPoint(cache.x, fromy + cache.y),
|
||||
cache.pixmap.size() / cIntRetinaFactor());
|
||||
|
@ -14,6 +14,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "base/object_ptr.h"
|
||||
#include "window/window_section_common.h"
|
||||
|
||||
class PeerData;
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
@ -22,6 +24,10 @@ namespace Ui {
|
||||
class LayerWidget;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Window::Theme {
|
||||
class ChatTheme;
|
||||
} // namespace Window::Theme
|
||||
|
||||
namespace Window {
|
||||
|
||||
class SessionController;
|
||||
@ -40,14 +46,10 @@ class AbstractSectionWidget
|
||||
, public Media::Player::FloatSectionDelegate
|
||||
, protected base::Subscriber {
|
||||
public:
|
||||
enum class PaintedBackground {
|
||||
Section,
|
||||
Custom,
|
||||
};
|
||||
AbstractSectionWidget(
|
||||
QWidget *parent,
|
||||
not_null<SessionController*> controller,
|
||||
PaintedBackground paintedBackground);
|
||||
rpl::producer<PeerData*> peerForBackground);
|
||||
|
||||
[[nodiscard]] Main::Session &session() const;
|
||||
[[nodiscard]] not_null<SessionController*> controller() const {
|
||||
@ -87,7 +89,11 @@ public:
|
||||
SectionWidget(
|
||||
QWidget *parent,
|
||||
not_null<SessionController*> controller,
|
||||
PaintedBackground paintedBackground);
|
||||
rpl::producer<PeerData*> peerForBackground = nullptr);
|
||||
SectionWidget(
|
||||
QWidget *parent,
|
||||
not_null<SessionController*> controller,
|
||||
not_null<PeerData*> peerForBackground);
|
||||
|
||||
virtual Dialogs::RowDescriptor activeChat() const {
|
||||
return {};
|
||||
@ -158,6 +164,7 @@ public:
|
||||
|
||||
static void PaintBackground(
|
||||
not_null<SessionController*> controller,
|
||||
not_null<Window::Theme::ChatTheme*> theme,
|
||||
not_null<QWidget*> widget,
|
||||
QRect clip);
|
||||
|
||||
|
390
Telegram/SourceFiles/window/themes/window_chat_theme.cpp
Normal file
390
Telegram/SourceFiles/window/themes/window_chat_theme.cpp
Normal file
@ -0,0 +1,390 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "window/themes/window_chat_theme.h"
|
||||
|
||||
#include "window/themes/window_theme.h"
|
||||
#include "ui/image/image_prepare.h"
|
||||
#include "ui/ui_utility.h"
|
||||
#include "ui/chat/message_bubble.h"
|
||||
#include "history/view/history_view_element.h"
|
||||
|
||||
#include <QtGui/QGuiApplication>
|
||||
|
||||
namespace Window::Theme {
|
||||
namespace {
|
||||
|
||||
constexpr auto kMaxChatEntryHistorySize = 50;
|
||||
constexpr auto kCacheBackgroundTimeout = 3 * crl::time(1000);
|
||||
constexpr auto kCacheBackgroundFastTimeout = crl::time(200);
|
||||
constexpr auto kBackgroundFadeDuration = crl::time(200);
|
||||
|
||||
[[nodiscard]] CacheBackgroundResult CacheBackground(
|
||||
const CacheBackgroundRequest &request) {
|
||||
const auto gradient = request.gradient.isNull()
|
||||
? QImage()
|
||||
: request.recreateGradient
|
||||
? Images::GenerateGradient(
|
||||
request.gradient.size(),
|
||||
request.gradientColors,
|
||||
request.gradientRotation)
|
||||
: request.gradient;
|
||||
if (request.isPattern || request.tile || request.prepared.isNull()) {
|
||||
auto result = gradient.isNull()
|
||||
? QImage(
|
||||
request.area * cIntRetinaFactor(),
|
||||
QImage::Format_ARGB32_Premultiplied)
|
||||
: gradient.scaled(
|
||||
request.area * cIntRetinaFactor(),
|
||||
Qt::IgnoreAspectRatio,
|
||||
Qt::SmoothTransformation);
|
||||
result.setDevicePixelRatio(cRetinaFactor());
|
||||
if (!request.prepared.isNull()) {
|
||||
QPainter p(&result);
|
||||
if (!gradient.isNull()) {
|
||||
if (request.patternOpacity >= 0.) {
|
||||
p.setCompositionMode(QPainter::CompositionMode_SoftLight);
|
||||
p.setOpacity(request.patternOpacity);
|
||||
} else {
|
||||
p.setCompositionMode(
|
||||
QPainter::CompositionMode_DestinationIn);
|
||||
}
|
||||
}
|
||||
const auto tiled = request.isPattern
|
||||
? request.prepared.scaled(
|
||||
request.area.height() * cIntRetinaFactor(),
|
||||
request.area.height() * cIntRetinaFactor(),
|
||||
Qt::KeepAspectRatio,
|
||||
Qt::SmoothTransformation)
|
||||
: request.preparedForTiled;
|
||||
const auto w = tiled.width() / cRetinaFactor();
|
||||
const auto h = tiled.height() / cRetinaFactor();
|
||||
const auto cx = qCeil(request.area.width() / w);
|
||||
const auto cy = qCeil(request.area.height() / h);
|
||||
const auto rows = cy;
|
||||
const auto cols = request.isPattern ? (((cx / 2) * 2) + 1) : cx;
|
||||
const auto xshift = request.isPattern
|
||||
? (request.area.width() - cols * w) / 2
|
||||
: 0;
|
||||
for (auto y = 0; y != rows; ++y) {
|
||||
for (auto x = 0; x != cols; ++x) {
|
||||
p.drawImage(QPointF(xshift + x * w, y * h), tiled);
|
||||
}
|
||||
}
|
||||
if (!gradient.isNull()
|
||||
&& request.patternOpacity < 0.
|
||||
&& request.patternOpacity > -1.) {
|
||||
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
||||
p.setOpacity(1. + request.patternOpacity);
|
||||
p.fillRect(QRect(QPoint(), request.area), Qt::black);
|
||||
}
|
||||
}
|
||||
return {
|
||||
.image = std::move(result).convertToFormat(
|
||||
QImage::Format_ARGB32_Premultiplied),
|
||||
.gradient = gradient,
|
||||
.area = request.area,
|
||||
};
|
||||
} else {
|
||||
const auto rects = ComputeBackgroundRects(
|
||||
request.area,
|
||||
request.prepared.size());
|
||||
auto result = request.prepared.copy(rects.from).scaled(
|
||||
rects.to.width() * cIntRetinaFactor(),
|
||||
rects.to.height() * cIntRetinaFactor(),
|
||||
Qt::IgnoreAspectRatio,
|
||||
Qt::SmoothTransformation);
|
||||
result.setDevicePixelRatio(cRetinaFactor());
|
||||
return {
|
||||
.image = std::move(result).convertToFormat(
|
||||
QImage::Format_ARGB32_Premultiplied),
|
||||
.gradient = gradient,
|
||||
.area = request.area,
|
||||
.x = rects.to.x(),
|
||||
.y = rects.to.y(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool operator==(
|
||||
const CacheBackgroundRequest &a,
|
||||
const CacheBackgroundRequest &b) {
|
||||
return (a.prepared.cacheKey() == b.prepared.cacheKey())
|
||||
&& (a.area == b.area)
|
||||
&& (a.gradientRotation == b.gradientRotation)
|
||||
&& (a.tile == b.tile)
|
||||
&& (a.recreateGradient == b.recreateGradient)
|
||||
&& (a.gradient.cacheKey() == b.gradient.cacheKey())
|
||||
&& (a.gradientProgress == b.gradientProgress)
|
||||
&& (a.patternOpacity == b.patternOpacity);
|
||||
}
|
||||
|
||||
bool operator!=(
|
||||
const CacheBackgroundRequest &a,
|
||||
const CacheBackgroundRequest &b) {
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
CachedBackground::CachedBackground(CacheBackgroundResult &&result)
|
||||
: pixmap(Ui::PixmapFromImage(std::move(result.image)))
|
||||
, area(result.area)
|
||||
, x(result.x)
|
||||
, y(result.y) {
|
||||
}
|
||||
|
||||
ChatTheme::ChatTheme() {
|
||||
Background()->updates(
|
||||
) | rpl::start_with_next([=](const BackgroundUpdate &update) {
|
||||
if (update.type == BackgroundUpdate::Type::New
|
||||
|| update.type == BackgroundUpdate::Type::Changed) {
|
||||
clearCachedBackground();
|
||||
}
|
||||
}, _lifetime);
|
||||
}
|
||||
|
||||
// Runs from background thread.
|
||||
ChatTheme::ChatTheme(const Data::CloudTheme &theme)
|
||||
: _id(theme.id)
|
||||
, _palette(std::make_unique<style::palette>()) {
|
||||
}
|
||||
|
||||
uint64 ChatTheme::key() const {
|
||||
return _id;
|
||||
}
|
||||
|
||||
void ChatTheme::setBubblesBackground(QImage image) {
|
||||
_bubblesBackgroundPrepared = std::move(image);
|
||||
if (!_bubblesBackground.area.isEmpty()) {
|
||||
_bubblesBackground = CacheBackground({
|
||||
.prepared = _bubblesBackgroundPrepared,
|
||||
.area = _bubblesBackground.area,
|
||||
});
|
||||
}
|
||||
if (!_bubblesBackgroundPattern) {
|
||||
_bubblesBackgroundPattern = Ui::PrepareBubblePattern();
|
||||
}
|
||||
_bubblesBackgroundPattern->pixmap = _bubblesBackground.pixmap;
|
||||
_repaintBackgroundRequests.fire({});
|
||||
}
|
||||
|
||||
HistoryView::PaintContext ChatTheme::preparePaintContext(
|
||||
QRect viewport,
|
||||
QRect clip) {
|
||||
_bubblesBackground.area = viewport.size();
|
||||
//if (!_bubblesBackgroundPrepared.isNull()
|
||||
// && _bubblesBackground.area != viewport.size()
|
||||
// && !viewport.isEmpty()) {
|
||||
// // #TODO bubbles delayed caching
|
||||
// _bubblesBackground = CacheBackground({
|
||||
// .prepared = _bubblesBackgroundPrepared,
|
||||
// .area = viewport.size(),
|
||||
// });
|
||||
// _bubblesBackgroundPattern->pixmap = _bubblesBackground.pixmap;
|
||||
//}
|
||||
return {
|
||||
.st = _palette ? _palette.get() : style::main_palette::get(),
|
||||
.bubblesPattern = _bubblesBackgroundPattern.get(),
|
||||
.viewport = viewport,
|
||||
.clip = clip,
|
||||
.now = crl::now(),
|
||||
};
|
||||
}
|
||||
|
||||
const BackgroundState &ChatTheme::backgroundState(QSize area) {
|
||||
if (!_cacheBackgroundTimer) {
|
||||
_cacheBackgroundTimer.emplace([=] { cacheBackground(); });
|
||||
}
|
||||
_backgroundState.shown = _backgroundFade.value(1.);
|
||||
if (_backgroundState.now.pixmap.isNull()
|
||||
&& !Background()->gradientForFill().isNull()) {
|
||||
// We don't support direct painting of patterned gradients.
|
||||
// So we need to sync-generate cache image here.
|
||||
setCachedBackground(CacheBackground(currentCacheRequest(area)));
|
||||
_cacheBackgroundTimer->cancel();
|
||||
} else if (_backgroundState.now.area != area) {
|
||||
if (_willCacheForArea != area
|
||||
|| (!_cacheBackgroundTimer->isActive()
|
||||
&& !_backgroundCachingRequest)) {
|
||||
_willCacheForArea = area;
|
||||
_lastAreaChangeTime = crl::now();
|
||||
_cacheBackgroundTimer->callOnce(kCacheBackgroundFastTimeout);
|
||||
}
|
||||
}
|
||||
generateNextBackgroundRotation();
|
||||
return _backgroundState;
|
||||
}
|
||||
|
||||
bool ChatTheme::readyForBackgroundRotation() const {
|
||||
Expects(_cacheBackgroundTimer.has_value());
|
||||
|
||||
return !anim::Disabled()
|
||||
&& !_backgroundFade.animating()
|
||||
&& !_cacheBackgroundTimer->isActive()
|
||||
&& !_backgroundState.now.pixmap.isNull();
|
||||
}
|
||||
|
||||
void ChatTheme::generateNextBackgroundRotation() {
|
||||
if (_backgroundCachingRequest
|
||||
|| !_backgroundNext.image.isNull()
|
||||
|| !readyForBackgroundRotation()) {
|
||||
return;
|
||||
}
|
||||
const auto background = Background();
|
||||
if (background->paper().backgroundColors().size() < 3) {
|
||||
return;
|
||||
}
|
||||
constexpr auto kAddRotation = 315;
|
||||
const auto request = currentCacheRequest(
|
||||
_backgroundState.now.area,
|
||||
kAddRotation);
|
||||
if (!request) {
|
||||
return;
|
||||
}
|
||||
cacheBackgroundAsync(request, [=](CacheBackgroundResult &&result) {
|
||||
const auto forRequest = base::take(_backgroundCachingRequest);
|
||||
if (!readyForBackgroundRotation()) {
|
||||
return;
|
||||
}
|
||||
const auto request = currentCacheRequest(
|
||||
_backgroundState.now.area,
|
||||
kAddRotation);
|
||||
if (forRequest == request) {
|
||||
_backgroundAddRotation
|
||||
= (_backgroundAddRotation + kAddRotation) % 360;
|
||||
_backgroundNext = std::move(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
auto ChatTheme::currentCacheRequest(QSize area, int addRotation) const
|
||||
-> CacheBackgroundRequest {
|
||||
const auto background = Background();
|
||||
if (background->colorForFill()) {
|
||||
return {};
|
||||
}
|
||||
const auto rotation = background->paper().gradientRotation();
|
||||
const auto gradient = background->gradientForFill();
|
||||
return {
|
||||
.prepared = background->prepared(),
|
||||
.preparedForTiled = background->preparedForTiled(),
|
||||
.area = area,
|
||||
.gradientRotation = (rotation
|
||||
+ _backgroundAddRotation
|
||||
+ addRotation) % 360,
|
||||
.tile = background->tile(),
|
||||
.isPattern = background->paper().isPattern(),
|
||||
.recreateGradient = (addRotation != 0),
|
||||
.gradient = gradient,
|
||||
.gradientColors = (gradient.isNull()
|
||||
? std::vector<QColor>()
|
||||
: background->paper().backgroundColors()),
|
||||
.gradientProgress = 1.,
|
||||
.patternOpacity = background->paper().patternOpacity(),
|
||||
};
|
||||
}
|
||||
|
||||
void ChatTheme::cacheBackground() {
|
||||
Expects(_cacheBackgroundTimer.has_value());
|
||||
|
||||
const auto now = crl::now();
|
||||
if (now - _lastAreaChangeTime < kCacheBackgroundTimeout
|
||||
&& QGuiApplication::mouseButtons() != 0) {
|
||||
_cacheBackgroundTimer->callOnce(kCacheBackgroundFastTimeout);
|
||||
return;
|
||||
}
|
||||
cacheBackgroundNow();
|
||||
}
|
||||
|
||||
void ChatTheme::cacheBackgroundNow() {
|
||||
if (!_backgroundCachingRequest) {
|
||||
if (const auto request = currentCacheRequest(_willCacheForArea)) {
|
||||
cacheBackgroundAsync(request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ChatTheme::cacheBackgroundAsync(
|
||||
const CacheBackgroundRequest &request,
|
||||
Fn<void(CacheBackgroundResult&&)> done) {
|
||||
_backgroundCachingRequest = request;
|
||||
const auto weak = base::make_weak(this);
|
||||
crl::async([=] {
|
||||
if (!weak) {
|
||||
return;
|
||||
}
|
||||
crl::on_main(weak, [=, result = CacheBackground(request)]() mutable {
|
||||
if (done) {
|
||||
done(std::move(result));
|
||||
} else if (const auto request = currentCacheRequest(
|
||||
_willCacheForArea)) {
|
||||
if (_backgroundCachingRequest != request) {
|
||||
cacheBackgroundAsync(request);
|
||||
} else {
|
||||
_backgroundCachingRequest = {};
|
||||
setCachedBackground(std::move(result));
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void ChatTheme::setCachedBackground(CacheBackgroundResult &&cached) {
|
||||
_backgroundNext = {};
|
||||
|
||||
const auto background = Background();
|
||||
if (background->gradientForFill().isNull()
|
||||
|| _backgroundState.now.pixmap.isNull()
|
||||
|| anim::Disabled()) {
|
||||
_backgroundFade.stop();
|
||||
_backgroundState.shown = 1.;
|
||||
_backgroundState.now = std::move(cached);
|
||||
return;
|
||||
}
|
||||
// #TODO themes compose several transitions.
|
||||
_backgroundState.was = std::move(_backgroundState.now);
|
||||
_backgroundState.now = std::move(cached);
|
||||
_backgroundState.shown = 0.;
|
||||
const auto callback = [=] {
|
||||
if (!_backgroundFade.animating()) {
|
||||
_backgroundState.was = {};
|
||||
_backgroundState.shown = 1.;
|
||||
}
|
||||
_repaintBackgroundRequests.fire({});
|
||||
};
|
||||
_backgroundFade.start(
|
||||
callback,
|
||||
0.,
|
||||
1.,
|
||||
kBackgroundFadeDuration);
|
||||
}
|
||||
|
||||
void ChatTheme::clearCachedBackground() {
|
||||
_backgroundState = {};
|
||||
_backgroundAddRotation = 0;
|
||||
_backgroundNext = {};
|
||||
_backgroundFade.stop();
|
||||
if (_cacheBackgroundTimer) {
|
||||
_cacheBackgroundTimer->cancel();
|
||||
}
|
||||
_repaintBackgroundRequests.fire({});
|
||||
}
|
||||
|
||||
rpl::producer<> ChatTheme::repaintBackgroundRequests() const {
|
||||
return _repaintBackgroundRequests.events();
|
||||
}
|
||||
|
||||
void ChatTheme::rotateComplexGradientBackground() {
|
||||
if (!_backgroundFade.animating() && !_backgroundNext.image.isNull()) {
|
||||
Background()->recacheGradientForFill(
|
||||
std::move(_backgroundNext.gradient));
|
||||
setCachedBackground(base::take(_backgroundNext));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Window::Theme
|
149
Telegram/SourceFiles/window/themes/window_chat_theme.h
Normal file
149
Telegram/SourceFiles/window/themes/window_chat_theme.h
Normal file
@ -0,0 +1,149 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "ui/effects/animations.h"
|
||||
#include "base/timer.h"
|
||||
#include "base/weak_ptr.h"
|
||||
|
||||
namespace Data {
|
||||
struct CloudTheme;
|
||||
} // namespace Data
|
||||
|
||||
namespace HistoryView {
|
||||
struct PaintContext;
|
||||
} // namespace HistoryView
|
||||
|
||||
namespace Ui {
|
||||
struct BubblePattern;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Window::Theme {
|
||||
|
||||
struct CacheBackgroundRequest {
|
||||
QImage prepared;
|
||||
QImage preparedForTiled;
|
||||
QSize area;
|
||||
int gradientRotation = 0;
|
||||
bool tile = false;
|
||||
bool isPattern = false;
|
||||
bool recreateGradient = false;
|
||||
QImage gradient;
|
||||
std::vector<QColor> gradientColors;
|
||||
float64 gradientProgress = 1.;
|
||||
float64 patternOpacity = 1.;
|
||||
|
||||
explicit operator bool() const {
|
||||
return !prepared.isNull() || !gradient.isNull();
|
||||
}
|
||||
};
|
||||
|
||||
bool operator==(
|
||||
const CacheBackgroundRequest &a,
|
||||
const CacheBackgroundRequest &b);
|
||||
bool operator!=(
|
||||
const CacheBackgroundRequest &a,
|
||||
const CacheBackgroundRequest &b);
|
||||
|
||||
struct CacheBackgroundResult {
|
||||
QImage image;
|
||||
QImage gradient;
|
||||
QSize area;
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
};
|
||||
|
||||
struct CachedBackground {
|
||||
CachedBackground() = default;
|
||||
CachedBackground(CacheBackgroundResult &&result);
|
||||
|
||||
QPixmap pixmap;
|
||||
QSize area;
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
};
|
||||
|
||||
struct BackgroundState {
|
||||
CachedBackground was;
|
||||
CachedBackground now;
|
||||
float64 shown = 1.;
|
||||
};
|
||||
|
||||
struct ChatThemeBackground {
|
||||
QImage prepared;
|
||||
QImage gradientForFill;
|
||||
std::optional<QColor> colorForFill;
|
||||
};
|
||||
|
||||
struct ChatThemeDescriptor {
|
||||
Fn<void(style::palette&)> preparePalette;
|
||||
Fn<ChatThemeBackground()> prepareBackground;
|
||||
std::vector<QColor> backgroundColors;
|
||||
};
|
||||
|
||||
class ChatTheme final : public base::has_weak_ptr {
|
||||
public:
|
||||
ChatTheme();
|
||||
|
||||
// Runs from background thread.
|
||||
ChatTheme(const Data::CloudTheme &theme);
|
||||
|
||||
[[nodiscard]] uint64 key() const;
|
||||
[[nodiscard]] not_null<const style::palette*> palette() const {
|
||||
return _palette.get();
|
||||
}
|
||||
|
||||
void setBackground(ChatThemeBackground);
|
||||
|
||||
void setBubblesBackground(QImage image);
|
||||
const Ui::BubblePattern *bubblesBackgroundPattern() const {
|
||||
return _bubblesBackgroundPattern.get();
|
||||
}
|
||||
|
||||
[[nodiscard]] HistoryView::PaintContext preparePaintContext(
|
||||
QRect viewport,
|
||||
QRect clip);
|
||||
[[nodiscard]] const BackgroundState &backgroundState(QSize area);
|
||||
[[nodiscard]] rpl::producer<> repaintBackgroundRequests() const;
|
||||
void rotateComplexGradientBackground();
|
||||
|
||||
private:
|
||||
void cacheBackground();
|
||||
void cacheBackgroundNow();
|
||||
void cacheBackgroundAsync(
|
||||
const CacheBackgroundRequest &request,
|
||||
Fn<void(CacheBackgroundResult&&)> done = nullptr);
|
||||
void clearCachedBackground();
|
||||
void setCachedBackground(CacheBackgroundResult &&cached);
|
||||
[[nodiscard]] CacheBackgroundRequest currentCacheRequest(
|
||||
QSize area,
|
||||
int addRotation = 0) const;
|
||||
[[nodiscard]] bool readyForBackgroundRotation() const;
|
||||
void generateNextBackgroundRotation();
|
||||
|
||||
uint64 _id = 0;
|
||||
std::unique_ptr<style::palette> _palette;
|
||||
BackgroundState _backgroundState;
|
||||
Ui::Animations::Simple _backgroundFade;
|
||||
CacheBackgroundRequest _backgroundCachingRequest;
|
||||
CacheBackgroundResult _backgroundNext;
|
||||
int _backgroundAddRotation = 0;
|
||||
QSize _willCacheForArea;
|
||||
crl::time _lastAreaChangeTime = 0;
|
||||
std::optional<base::Timer> _cacheBackgroundTimer;
|
||||
CachedBackground _bubblesBackground;
|
||||
QImage _bubblesBackgroundPrepared;
|
||||
std::unique_ptr<Ui::BubblePattern> _bubblesBackgroundPattern;
|
||||
|
||||
rpl::event_stream<> _repaintBackgroundRequests;
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Window::Theme
|
@ -868,9 +868,9 @@ void ChatBackground::adjustPaletteUsingColor(QColor color) {
|
||||
std::optional<QColor> ChatBackground::colorForFill() const {
|
||||
return !_prepared.isNull()
|
||||
? imageMonoColor()
|
||||
: !_gradient.isNull()
|
||||
: (!_gradient.isNull() || _paper.backgroundColors().empty())
|
||||
? std::nullopt
|
||||
: _paper.backgroundColor();
|
||||
: std::make_optional(_paper.backgroundColors().front());
|
||||
}
|
||||
|
||||
QImage ChatBackground::gradientForFill() const {
|
||||
@ -1390,6 +1390,21 @@ bool IsNightMode() {
|
||||
return GlobalBackground ? Background()->nightMode() : false;
|
||||
}
|
||||
|
||||
rpl::producer<bool> IsNightModeValue() {
|
||||
auto changes = Background()->updates(
|
||||
) | rpl::filter([=](const BackgroundUpdate &update) {
|
||||
return update.type == BackgroundUpdate::Type::ApplyingTheme;
|
||||
}) | rpl::to_empty;
|
||||
|
||||
return rpl::single(
|
||||
rpl::empty_value()
|
||||
) | rpl::then(
|
||||
std::move(changes)
|
||||
) | rpl::map([=] {
|
||||
return IsNightMode();
|
||||
}) | rpl::distinct_until_changed();
|
||||
}
|
||||
|
||||
void SetNightModeValue(bool nightMode) {
|
||||
if (GlobalBackground || nightMode) {
|
||||
Background()->setNightModeValue(nightMode);
|
||||
|
@ -78,6 +78,7 @@ void KeepFromEditor(
|
||||
QString NightThemePath();
|
||||
[[nodiscard]] bool IsNightMode();
|
||||
void SetNightModeValue(bool nightMode);
|
||||
[[nodiscard]] rpl::producer<bool> IsNightModeValue();
|
||||
void ToggleNightMode();
|
||||
void ToggleNightMode(const QString &themePath);
|
||||
void ToggleNightModeWithConfirmation(
|
||||
|
@ -14,11 +14,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "window/window_controller.h"
|
||||
#include "window/main_window.h"
|
||||
#include "window/window_filters_menu.h"
|
||||
#include "window/themes/window_chat_theme.h"
|
||||
#include "info/info_memento.h"
|
||||
#include "info/info_controller.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/view/history_view_element.h"
|
||||
#include "history/view/history_view_replies_section.h"
|
||||
#include "media/player/media_player_instance.h"
|
||||
#include "media/view/media_view_open_common.h"
|
||||
@ -66,102 +66,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "styles/style_layers.h" // st::boxLabel
|
||||
#include "styles/style_chat.h" // st::historyMessageRadius
|
||||
|
||||
#include <QtGui/QGuiApplication>
|
||||
|
||||
namespace Window {
|
||||
namespace {
|
||||
|
||||
constexpr auto kMaxChatEntryHistorySize = 50;
|
||||
constexpr auto kCacheBackgroundTimeout = 3 * crl::time(1000);
|
||||
constexpr auto kCacheBackgroundFastTimeout = crl::time(200);
|
||||
constexpr auto kBackgroundFadeDuration = crl::time(200);
|
||||
|
||||
[[nodiscard]] CacheBackgroundResult CacheBackground(
|
||||
const CacheBackgroundRequest &request) {
|
||||
const auto gradient = request.gradient.isNull()
|
||||
? QImage()
|
||||
: request.recreateGradient
|
||||
? Images::GenerateGradient(
|
||||
request.gradient.size(),
|
||||
request.gradientColors,
|
||||
request.gradientRotation)
|
||||
: request.gradient;
|
||||
if (request.isPattern || request.tile || request.prepared.isNull()) {
|
||||
auto result = gradient.isNull()
|
||||
? QImage(
|
||||
request.area * cIntRetinaFactor(),
|
||||
QImage::Format_ARGB32_Premultiplied)
|
||||
: gradient.scaled(
|
||||
request.area * cIntRetinaFactor(),
|
||||
Qt::IgnoreAspectRatio,
|
||||
Qt::SmoothTransformation);
|
||||
result.setDevicePixelRatio(cRetinaFactor());
|
||||
if (!request.prepared.isNull()) {
|
||||
QPainter p(&result);
|
||||
if (!gradient.isNull()) {
|
||||
if (request.patternOpacity >= 0.) {
|
||||
p.setCompositionMode(QPainter::CompositionMode_SoftLight);
|
||||
p.setOpacity(request.patternOpacity);
|
||||
} else {
|
||||
p.setCompositionMode(
|
||||
QPainter::CompositionMode_DestinationIn);
|
||||
}
|
||||
}
|
||||
const auto tiled = request.isPattern
|
||||
? request.prepared.scaled(
|
||||
request.area.height() * cIntRetinaFactor(),
|
||||
request.area.height() * cIntRetinaFactor(),
|
||||
Qt::KeepAspectRatio,
|
||||
Qt::SmoothTransformation)
|
||||
: request.preparedForTiled;
|
||||
const auto w = tiled.width() / cRetinaFactor();
|
||||
const auto h = tiled.height() / cRetinaFactor();
|
||||
const auto cx = qCeil(request.area.width() / w);
|
||||
const auto cy = qCeil(request.area.height() / h);
|
||||
const auto rows = cy;
|
||||
const auto cols = request.isPattern ? (((cx / 2) * 2) + 1) : cx;
|
||||
const auto xshift = request.isPattern
|
||||
? (request.area.width() - cols * w) / 2
|
||||
: 0;
|
||||
for (auto y = 0; y != rows; ++y) {
|
||||
for (auto x = 0; x != cols; ++x) {
|
||||
p.drawImage(QPointF(xshift + x * w, y * h), tiled);
|
||||
}
|
||||
}
|
||||
if (!gradient.isNull()
|
||||
&& request.patternOpacity < 0.
|
||||
&& request.patternOpacity > -1.) {
|
||||
p.setCompositionMode(QPainter::CompositionMode_SourceOver);
|
||||
p.setOpacity(1. + request.patternOpacity);
|
||||
p.fillRect(QRect(QPoint(), request.area), Qt::black);
|
||||
}
|
||||
}
|
||||
return {
|
||||
.image = std::move(result).convertToFormat(
|
||||
QImage::Format_ARGB32_Premultiplied),
|
||||
.gradient = gradient,
|
||||
.area = request.area,
|
||||
};
|
||||
} else {
|
||||
const auto rects = Window::Theme::ComputeBackgroundRects(
|
||||
request.area,
|
||||
request.prepared.size());
|
||||
auto result = request.prepared.copy(rects.from).scaled(
|
||||
rects.to.width() * cIntRetinaFactor(),
|
||||
rects.to.height() * cIntRetinaFactor(),
|
||||
Qt::IgnoreAspectRatio,
|
||||
Qt::SmoothTransformation);
|
||||
result.setDevicePixelRatio(cRetinaFactor());
|
||||
return {
|
||||
.image = std::move(result).convertToFormat(
|
||||
QImage::Format_ARGB32_Premultiplied),
|
||||
.gradient = gradient,
|
||||
.area = request.area,
|
||||
.x = rects.to.x(),
|
||||
.y = rects.to.y(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@ -550,32 +458,6 @@ void SessionNavigation::showPollResults(
|
||||
showSection(std::make_shared<Info::Memento>(poll, contextId), params);
|
||||
}
|
||||
|
||||
bool operator==(
|
||||
const CacheBackgroundRequest &a,
|
||||
const CacheBackgroundRequest &b) {
|
||||
return (a.prepared.cacheKey() == b.prepared.cacheKey())
|
||||
&& (a.area == b.area)
|
||||
&& (a.gradientRotation == b.gradientRotation)
|
||||
&& (a.tile == b.tile)
|
||||
&& (a.recreateGradient == b.recreateGradient)
|
||||
&& (a.gradient.cacheKey() == b.gradient.cacheKey())
|
||||
&& (a.gradientProgress == b.gradientProgress)
|
||||
&& (a.patternOpacity == b.patternOpacity);
|
||||
}
|
||||
|
||||
bool operator!=(
|
||||
const CacheBackgroundRequest &a,
|
||||
const CacheBackgroundRequest &b) {
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
CachedBackground::CachedBackground(CacheBackgroundResult &&result)
|
||||
: pixmap(Ui::PixmapFromImage(std::move(result.image)))
|
||||
, area(result.area)
|
||||
, x(result.x)
|
||||
, y(result.y) {
|
||||
}
|
||||
|
||||
SessionController::SessionController(
|
||||
not_null<Main::Session*> session,
|
||||
not_null<Controller*> window)
|
||||
@ -586,7 +468,7 @@ SessionController::SessionController(
|
||||
_window->widget(),
|
||||
this))
|
||||
, _invitePeekTimer([=] { checkInvitePeek(); })
|
||||
, _cacheBackgroundTimer([=] { cacheBackground(); }) {
|
||||
, _defaultChatTheme(std::make_shared<Theme::ChatTheme>()) {
|
||||
init();
|
||||
|
||||
if (Media::Player::instance()->pauseGifByRoundVideo()) {
|
||||
@ -629,15 +511,6 @@ SessionController::SessionController(
|
||||
}));
|
||||
}, _lifetime);
|
||||
|
||||
using Update = Window::Theme::BackgroundUpdate;
|
||||
Window::Theme::Background()->updates(
|
||||
) | rpl::start_with_next([=](const Update &update) {
|
||||
if (update.type == Update::Type::New
|
||||
|| update.type == Update::Type::Changed) {
|
||||
clearCachedBackground();
|
||||
}
|
||||
}, lifetime());
|
||||
|
||||
session->addWindow(this);
|
||||
}
|
||||
|
||||
@ -1435,23 +1308,50 @@ void SessionController::openDocument(
|
||||
session().data().message(contextId));
|
||||
}
|
||||
|
||||
void SessionController::setBubblesBackground(QImage image) {
|
||||
_bubblesBackgroundPrepared = std::move(image);
|
||||
if (!_bubblesBackground.area.isEmpty()) {
|
||||
_bubblesBackground = CacheBackground({
|
||||
.prepared = _bubblesBackgroundPrepared,
|
||||
.area = _bubblesBackground.area,
|
||||
});
|
||||
auto SessionController::cachedChatThemeValue(
|
||||
const Data::CloudTheme &data)
|
||||
-> rpl::producer<std::shared_ptr<Theme::ChatTheme>> {
|
||||
const auto key = data.id;
|
||||
if (!key || !data.paper || data.paper->backgroundColors().empty()) {
|
||||
return rpl::single(_defaultChatTheme);
|
||||
}
|
||||
if (!_bubblesBackgroundPattern) {
|
||||
_bubblesBackgroundPattern = Ui::PrepareBubblePattern();
|
||||
const auto i = _customChatThemes.find(key);
|
||||
if (i != end(_customChatThemes) && i->second) {
|
||||
return rpl::single(i->second);
|
||||
}
|
||||
_bubblesBackgroundPattern->pixmap = _bubblesBackground.pixmap;
|
||||
_repaintBackgroundRequests.fire({});
|
||||
if (i == end(_customChatThemes)) {
|
||||
cacheChatTheme(data);
|
||||
}
|
||||
using namespace rpl::mappers;
|
||||
return rpl::single(
|
||||
_defaultChatTheme
|
||||
) | rpl::then(_cachedThemesStream.events(
|
||||
) | rpl::filter([=](const std::shared_ptr<Theme::ChatTheme> &theme) {
|
||||
return (theme->key() == key);
|
||||
}) | rpl::take(1));
|
||||
}
|
||||
|
||||
HistoryView::PaintContext SessionController::bubblesContext(
|
||||
BubblesContextArgs &&args) {
|
||||
void SessionController::cacheChatTheme(const Data::CloudTheme &data) {
|
||||
Expects(data.id != 0);
|
||||
|
||||
const auto key = data.id;
|
||||
if (data.paper) {
|
||||
const auto document = data.paper->document();
|
||||
|
||||
}
|
||||
crl::async([this, data, weak = base::make_weak(this)] {
|
||||
crl::on_main(weak, [
|
||||
this,
|
||||
result = std::make_shared<Theme::ChatTheme>(data)
|
||||
]() mutable {
|
||||
_customChatThemes.emplace(result->key(), result);
|
||||
_cachedThemesStream.fire(std::move(result));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
HistoryView::PaintContext SessionController::preparePaintContext(
|
||||
PaintContextArgs &&args) {
|
||||
const auto visibleAreaTopLocal = content()->mapFromGlobal(
|
||||
QPoint(0, args.visibleAreaTopGlobal)).y();
|
||||
const auto viewport = QRect(
|
||||
@ -1459,205 +1359,7 @@ HistoryView::PaintContext SessionController::bubblesContext(
|
||||
args.visibleAreaTop - visibleAreaTopLocal,
|
||||
args.visibleAreaWidth,
|
||||
content()->height());
|
||||
_bubblesBackground.area = viewport.size();
|
||||
//if (!_bubblesBackgroundPrepared.isNull()
|
||||
// && _bubblesBackground.area != viewport.size()
|
||||
// && !viewport.isEmpty()) {
|
||||
// // #TODO bubbles delayed caching
|
||||
// _bubblesBackground = CacheBackground({
|
||||
// .prepared = _bubblesBackgroundPrepared,
|
||||
// .area = viewport.size(),
|
||||
// });
|
||||
// _bubblesBackgroundPattern->pixmap = _bubblesBackground.pixmap;
|
||||
//}
|
||||
return {
|
||||
.bubblesPattern = _bubblesBackgroundPattern.get(),
|
||||
.viewport = viewport,
|
||||
.clip = args.clip,
|
||||
.now = crl::now(),
|
||||
};
|
||||
}
|
||||
|
||||
const BackgroundState &SessionController::backgroundState(QSize area) {
|
||||
_backgroundState.shown = _backgroundFade.value(1.);
|
||||
if (_backgroundState.now.pixmap.isNull()
|
||||
&& !Window::Theme::Background()->gradientForFill().isNull()) {
|
||||
// We don't support direct painting of patterned gradients.
|
||||
// So we need to sync-generate cache image here.
|
||||
setCachedBackground(CacheBackground(currentCacheRequest(area)));
|
||||
_cacheBackgroundTimer.cancel();
|
||||
} else if (_backgroundState.now.area != area) {
|
||||
if (_willCacheForArea != area
|
||||
|| (!_cacheBackgroundTimer.isActive()
|
||||
&& !_backgroundCachingRequest)) {
|
||||
_willCacheForArea = area;
|
||||
_lastAreaChangeTime = crl::now();
|
||||
_cacheBackgroundTimer.callOnce(kCacheBackgroundFastTimeout);
|
||||
}
|
||||
}
|
||||
generateNextBackgroundRotation();
|
||||
return _backgroundState;
|
||||
}
|
||||
|
||||
bool SessionController::readyForBackgroundRotation() const {
|
||||
return !anim::Disabled()
|
||||
&& !_backgroundFade.animating()
|
||||
&& !_cacheBackgroundTimer.isActive()
|
||||
&& !_backgroundState.now.pixmap.isNull();
|
||||
}
|
||||
|
||||
void SessionController::generateNextBackgroundRotation() {
|
||||
if (_backgroundCachingRequest
|
||||
|| !_backgroundNext.image.isNull()
|
||||
|| !readyForBackgroundRotation()) {
|
||||
return;
|
||||
}
|
||||
const auto background = Window::Theme::Background();
|
||||
if (background->paper().backgroundColors().size() < 3) {
|
||||
return;
|
||||
}
|
||||
constexpr auto kAddRotation = 315;
|
||||
const auto request = currentCacheRequest(
|
||||
_backgroundState.now.area,
|
||||
kAddRotation);
|
||||
if (!request) {
|
||||
return;
|
||||
}
|
||||
cacheBackgroundAsync(request, [=](CacheBackgroundResult &&result) {
|
||||
const auto forRequest = base::take(_backgroundCachingRequest);
|
||||
if (!readyForBackgroundRotation()) {
|
||||
return;
|
||||
}
|
||||
const auto request = currentCacheRequest(
|
||||
_backgroundState.now.area,
|
||||
kAddRotation);
|
||||
if (forRequest == request) {
|
||||
_backgroundAddRotation
|
||||
= (_backgroundAddRotation + kAddRotation) % 360;
|
||||
_backgroundNext = std::move(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
auto SessionController::currentCacheRequest(QSize area, int addRotation) const
|
||||
-> CacheBackgroundRequest {
|
||||
const auto background = Window::Theme::Background();
|
||||
if (background->colorForFill()) {
|
||||
return {};
|
||||
}
|
||||
const auto rotation = background->paper().gradientRotation();
|
||||
const auto gradient = background->gradientForFill();
|
||||
return {
|
||||
.prepared = background->prepared(),
|
||||
.preparedForTiled = background->preparedForTiled(),
|
||||
.area = area,
|
||||
.gradientRotation = (rotation
|
||||
+ _backgroundAddRotation
|
||||
+ addRotation) % 360,
|
||||
.tile = background->tile(),
|
||||
.isPattern = background->paper().isPattern(),
|
||||
.recreateGradient = (addRotation != 0),
|
||||
.gradient = gradient,
|
||||
.gradientColors = (gradient.isNull()
|
||||
? std::vector<QColor>()
|
||||
: background->paper().backgroundColors()),
|
||||
.gradientProgress = 1.,
|
||||
.patternOpacity = background->paper().patternOpacity(),
|
||||
};
|
||||
}
|
||||
|
||||
void SessionController::cacheBackground() {
|
||||
const auto now = crl::now();
|
||||
if (now - _lastAreaChangeTime < kCacheBackgroundTimeout
|
||||
&& QGuiApplication::mouseButtons() != 0) {
|
||||
_cacheBackgroundTimer.callOnce(kCacheBackgroundFastTimeout);
|
||||
return;
|
||||
}
|
||||
cacheBackgroundNow();
|
||||
}
|
||||
|
||||
void SessionController::cacheBackgroundNow() {
|
||||
if (!_backgroundCachingRequest) {
|
||||
if (const auto request = currentCacheRequest(_willCacheForArea)) {
|
||||
cacheBackgroundAsync(request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SessionController::cacheBackgroundAsync(
|
||||
const CacheBackgroundRequest &request,
|
||||
Fn<void(CacheBackgroundResult&&)> done) {
|
||||
_backgroundCachingRequest = request;
|
||||
const auto weak = base::make_weak(this);
|
||||
crl::async([=] {
|
||||
if (!weak) {
|
||||
return;
|
||||
}
|
||||
crl::on_main(weak, [=, result = CacheBackground(request)]() mutable {
|
||||
if (done) {
|
||||
done(std::move(result));
|
||||
} else if (const auto request = currentCacheRequest(
|
||||
_willCacheForArea)) {
|
||||
if (_backgroundCachingRequest != request) {
|
||||
cacheBackgroundAsync(request);
|
||||
} else {
|
||||
_backgroundCachingRequest = {};
|
||||
setCachedBackground(std::move(result));
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void SessionController::setCachedBackground(CacheBackgroundResult &&cached) {
|
||||
_backgroundNext = {};
|
||||
|
||||
const auto background = Window::Theme::Background();
|
||||
if (background->gradientForFill().isNull()
|
||||
|| _backgroundState.now.pixmap.isNull()
|
||||
|| anim::Disabled()) {
|
||||
_backgroundFade.stop();
|
||||
_backgroundState.shown = 1.;
|
||||
_backgroundState.now = std::move(cached);
|
||||
return;
|
||||
}
|
||||
// #TODO themes compose several transitions.
|
||||
_backgroundState.was = std::move(_backgroundState.now);
|
||||
_backgroundState.now = std::move(cached);
|
||||
_backgroundState.shown = 0.;
|
||||
const auto callback = [=] {
|
||||
if (!_backgroundFade.animating()) {
|
||||
_backgroundState.was = {};
|
||||
_backgroundState.shown = 1.;
|
||||
}
|
||||
_repaintBackgroundRequests.fire({});
|
||||
};
|
||||
_backgroundFade.start(
|
||||
callback,
|
||||
0.,
|
||||
1.,
|
||||
kBackgroundFadeDuration);
|
||||
}
|
||||
|
||||
void SessionController::clearCachedBackground() {
|
||||
_backgroundState = {};
|
||||
_backgroundAddRotation = 0;
|
||||
_backgroundNext = {};
|
||||
_backgroundFade.stop();
|
||||
_cacheBackgroundTimer.cancel();
|
||||
_repaintBackgroundRequests.fire({});
|
||||
}
|
||||
|
||||
rpl::producer<> SessionController::repaintBackgroundRequests() const {
|
||||
return _repaintBackgroundRequests.events();
|
||||
}
|
||||
|
||||
void SessionController::rotateComplexGradientBackground() {
|
||||
if (!_backgroundFade.animating() && !_backgroundNext.image.isNull()) {
|
||||
Window::Theme::Background()->recacheGradientForFill(
|
||||
std::move(_backgroundNext.gradient));
|
||||
setCachedBackground(base::take(_backgroundNext));
|
||||
}
|
||||
return args.theme->preparePaintContext(viewport, args.clip);
|
||||
}
|
||||
|
||||
SessionController::~SessionController() {
|
||||
|
@ -12,7 +12,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "base/weak_ptr.h"
|
||||
#include "base/timer.h"
|
||||
#include "dialogs/dialogs_key.h"
|
||||
#include "ui/effects/animations.h"
|
||||
#include "ui/layers/layer_widget.h"
|
||||
#include "window/window_adaptive.h"
|
||||
|
||||
@ -50,9 +49,16 @@ class FormController;
|
||||
namespace Ui {
|
||||
class LayerWidget;
|
||||
enum class ReportReason;
|
||||
struct BubblePattern;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Window::Theme {
|
||||
class ChatTheme;
|
||||
} // namespace Window::Theme
|
||||
|
||||
namespace Data {
|
||||
struct CloudTheme;
|
||||
} // namespace Data
|
||||
|
||||
namespace Window {
|
||||
|
||||
class MainWindow;
|
||||
@ -60,55 +66,6 @@ class SectionMemento;
|
||||
class Controller;
|
||||
class FiltersMenu;
|
||||
|
||||
struct CacheBackgroundRequest {
|
||||
QImage prepared;
|
||||
QImage preparedForTiled;
|
||||
QSize area;
|
||||
int gradientRotation = 0;
|
||||
bool tile = false;
|
||||
bool isPattern = false;
|
||||
bool recreateGradient = false;
|
||||
QImage gradient;
|
||||
std::vector<QColor> gradientColors;
|
||||
float64 gradientProgress = 1.;
|
||||
float64 patternOpacity = 1.;
|
||||
|
||||
explicit operator bool() const {
|
||||
return !prepared.isNull() || !gradient.isNull();
|
||||
}
|
||||
};
|
||||
|
||||
bool operator==(
|
||||
const CacheBackgroundRequest &a,
|
||||
const CacheBackgroundRequest &b);
|
||||
bool operator!=(
|
||||
const CacheBackgroundRequest &a,
|
||||
const CacheBackgroundRequest &b);
|
||||
|
||||
struct CacheBackgroundResult {
|
||||
QImage image;
|
||||
QImage gradient;
|
||||
QSize area;
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
};
|
||||
|
||||
struct CachedBackground {
|
||||
CachedBackground() = default;
|
||||
CachedBackground(CacheBackgroundResult &&result);
|
||||
|
||||
QPixmap pixmap;
|
||||
QSize area;
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
};
|
||||
|
||||
struct BackgroundState {
|
||||
CachedBackground was;
|
||||
CachedBackground now;
|
||||
float64 shown = 1.;
|
||||
};
|
||||
|
||||
enum class GifPauseReason {
|
||||
Any = 0,
|
||||
InlineResults = (1 << 0),
|
||||
@ -445,22 +402,23 @@ public:
|
||||
void toggleFiltersMenu(bool enabled);
|
||||
[[nodiscard]] rpl::producer<> filtersMenuChanged() const;
|
||||
|
||||
void setBubblesBackground(QImage image);
|
||||
const Ui::BubblePattern *bubblesBackgroundPattern() const {
|
||||
return _bubblesBackgroundPattern.get();
|
||||
[[nodiscard]] auto defaultChatTheme() const
|
||||
-> const std::shared_ptr<Theme::ChatTheme> & {
|
||||
return _defaultChatTheme;
|
||||
}
|
||||
[[nodiscard]] auto cachedChatThemeValue(
|
||||
const Data::CloudTheme &data)
|
||||
-> rpl::producer<std::shared_ptr<Theme::ChatTheme>>;
|
||||
|
||||
struct BubblesContextArgs {
|
||||
struct PaintContextArgs {
|
||||
not_null<Theme::ChatTheme*> theme;
|
||||
int visibleAreaTop = 0;
|
||||
int visibleAreaTopGlobal = 0;
|
||||
int visibleAreaWidth = 0;
|
||||
QRect clip;
|
||||
};
|
||||
[[nodiscard]] HistoryView::PaintContext bubblesContext(
|
||||
BubblesContextArgs &&args);
|
||||
[[nodiscard]] const BackgroundState &backgroundState(QSize area);
|
||||
[[nodiscard]] rpl::producer<> repaintBackgroundRequests() const;
|
||||
void rotateComplexGradientBackground();
|
||||
[[nodiscard]] HistoryView::PaintContext preparePaintContext(
|
||||
PaintContextArgs &&args);
|
||||
|
||||
rpl::lifetime &lifetime() {
|
||||
return _lifetime;
|
||||
@ -491,18 +449,7 @@ private:
|
||||
|
||||
void checkInvitePeek();
|
||||
|
||||
void cacheBackground();
|
||||
void cacheBackgroundNow();
|
||||
void cacheBackgroundAsync(
|
||||
const CacheBackgroundRequest &request,
|
||||
Fn<void(CacheBackgroundResult&&)> done = nullptr);
|
||||
void clearCachedBackground();
|
||||
void setCachedBackground(CacheBackgroundResult &&cached);
|
||||
[[nodiscard]] CacheBackgroundRequest currentCacheRequest(
|
||||
QSize area,
|
||||
int addRotation = 0) const;
|
||||
[[nodiscard]] bool readyForBackgroundRotation() const;
|
||||
void generateNextBackgroundRotation();
|
||||
void cacheChatTheme(const Data::CloudTheme &data);
|
||||
|
||||
const not_null<Controller*> _window;
|
||||
|
||||
@ -531,19 +478,11 @@ private:
|
||||
|
||||
rpl::event_stream<> _filtersMenuChanged;
|
||||
|
||||
BackgroundState _backgroundState;
|
||||
Ui::Animations::Simple _backgroundFade;
|
||||
CacheBackgroundRequest _backgroundCachingRequest;
|
||||
CacheBackgroundResult _backgroundNext;
|
||||
int _backgroundAddRotation = 0;
|
||||
QSize _willCacheForArea;
|
||||
crl::time _lastAreaChangeTime = 0;
|
||||
base::Timer _cacheBackgroundTimer;
|
||||
CachedBackground _bubblesBackground;
|
||||
QImage _bubblesBackgroundPrepared;
|
||||
std::unique_ptr<Ui::BubblePattern> _bubblesBackgroundPattern;
|
||||
|
||||
rpl::event_stream<> _repaintBackgroundRequests;
|
||||
std::shared_ptr<Theme::ChatTheme> _defaultChatTheme;
|
||||
base::flat_map<
|
||||
uint64,
|
||||
std::shared_ptr<Theme::ChatTheme>> _customChatThemes;
|
||||
rpl::event_stream<std::shared_ptr<Theme::ChatTheme>> _cachedThemesStream;
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 248614b49cd7d5aff69d75a737f2e35b79fbb119
|
||||
Subproject commit 13117d03e5683af00f898a330aa319fd17efe8f7
|
@ -1 +1 @@
|
||||
Subproject commit 5938cb012fd78f5128fdefa3794fbae6c8f1dcf6
|
||||
Subproject commit 15ffd051d605be310051645412c54c2b9f87ff01
|
Loading…
Reference in New Issue
Block a user