Handle t.me/bg links with wallpapers / colors.

This commit is contained in:
John Preston 2019-01-17 12:18:23 +04:00
parent e59a68cd68
commit 1894b8fcf7
34 changed files with 805 additions and 194 deletions

View File

@ -392,6 +392,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_theme_reverting#other" = "Reverting to the old theme in {count} seconds.";
"lng_theme_keep_changes" = "Keep changes";
"lng_theme_revert" = "Revert";
"lng_background_header" = "Background preview";
"lng_background_text1" = "You can't swipe left or right to preview anything - this is tdesktop, sorry.";
"lng_background_text2" = "Sounds awful.";
"lng_background_bad_link" = "This background link appears to be invalid.";
"lng_background_apply" = "Apply";
"lng_download_path_ask" = "Ask download path for each file";
"lng_download_path" = "Download path";

View File

@ -40,6 +40,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/notifications_manager.h"
#include "window/window_lock_widgets.h"
#include "window/window_controller.h"
#include "window/themes/window_theme.h"
#include "inline_bots/inline_bot_result.h"
#include "chat_helpers/message_field.h"
#include "chat_helpers/stickers.h"
@ -864,6 +865,52 @@ void ApiWrap::requestFakeChatListMessage(
}).send();
}
void ApiWrap::requestWallPaper(
const QString &slug,
Fn<void(const Data::WallPaper &)> done,
Fn<void(const RPCError &)> fail) {
if (_wallPaperSlug != slug) {
_wallPaperSlug = slug;
if (_wallPaperRequestId) {
request(base::take(_wallPaperRequestId)).cancel();
}
}
_wallPaperDone = std::move(done);
_wallPaperFail = std::move(fail);
if (_wallPaperRequestId) {
return;
}
_wallPaperRequestId = request(MTPaccount_GetWallPaper(
MTP_inputWallPaperSlug(MTP_string(slug))
)).done([=](const MTPWallPaper &result) {
_wallPaperRequestId = 0;
_wallPaperSlug = QString();
result.match([&](const MTPDwallPaper &data) {
const auto document = _session->data().document(data.vdocument);
if (document->checkWallPaperProperties()) {
if (const auto done = base::take(_wallPaperDone)) {
done({
data.vid.v,
data.vaccess_hash.v,
data.vflags.v,
qs(data.vslug),
document->thumb,
document
});
}
} else if (const auto fail = base::take(_wallPaperFail)) {
fail(RPCError::Local("BAD_DOCUMENT", "In a wallpaper."));
}
});
}).fail([=](const RPCError &error) {
_wallPaperRequestId = 0;
_wallPaperSlug = QString();
if (const auto fail = base::take(_wallPaperFail)) {
fail(error);
}
}).send();
}
void ApiWrap::requestFullPeer(not_null<PeerData*> peer) {
if (_fullPeerRequests.contains(peer)) {
return;
@ -2829,8 +2876,11 @@ void ApiWrap::refreshFileReference(
request(
MTPmessages_GetSavedGifs(MTP_int(0)),
[] { crl::on_main([] { Local::writeSavedGifs(); }); });
}, [&](Data::FileOriginWallpapers data) {
request(MTPaccount_GetWallPapers(MTP_int(0)));
}, [&](Data::FileOriginWallpaper data) {
request(MTPaccount_GetWallPaper(
MTP_inputWallPaper(
MTP_long(data.paperId),
MTP_long(data.accessHash))));
}, [&](std::nullopt_t) {
fail();
});

View File

@ -26,6 +26,7 @@ class mtpFileLoader;
namespace Data {
struct UpdatedFileReferences;
struct WallPaper;
} // namespace Data
namespace InlineBots {
@ -92,6 +93,11 @@ public:
//void changeDialogUnreadMark(not_null<Data::Feed*> feed, bool unread); // #feed
void requestFakeChatListMessage(not_null<History*> history);
void requestWallPaper(
const QString &slug,
Fn<void(const Data::WallPaper &)> done,
Fn<void(const RPCError &)> fail);
void requestFullPeer(not_null<PeerData*> peer);
void requestPeer(not_null<PeerData*> peer);
void requestPeers(const QList<PeerData*> &peers);
@ -775,4 +781,9 @@ private:
base::flat_map<FullMsgId, mtpRequestId> _pollCloseRequestIds;
base::flat_map<FullMsgId, mtpRequestId> _pollReloadRequestIds;
mtpRequestId _wallPaperRequestId = 0;
QString _wallPaperSlug;
Fn<void(const Data::WallPaper &)> _wallPaperDone;
Fn<void(const RPCError &)> _wallPaperFail;
};

View File

@ -21,7 +21,9 @@ public:
~binary_guard();
bool alive() const;
void kill();
binary_guard &operator=(std::nullptr_t);
explicit operator bool() const;
private:
void destroy();
@ -44,15 +46,20 @@ inline binary_guard &binary_guard::operator=(binary_guard &&other) {
return *this;
}
inline binary_guard::~binary_guard() {
inline binary_guard &binary_guard::operator=(std::nullptr_t) {
destroy();
return *this;
}
inline binary_guard::operator bool() const {
return alive();
}
inline bool binary_guard::alive() const {
return _bothAlive && _bothAlive->load();
}
inline void binary_guard::kill() {
inline binary_guard::~binary_guard() {
destroy();
}

View File

@ -301,11 +301,11 @@ void ConcurrentTimer::cancelAndSchedule(int timeout) {
runner = _runner,
guard = std::move(guards.second)
]() mutable {
if (!guard.alive()) {
if (!guard) {
return;
}
runner([=, guard = std::move(guard)] {
if (!guard.alive()) {
if (!guard) {
return;
}
timerEvent();

View File

@ -13,14 +13,93 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/themes/window_theme.h"
#include "ui/effects/round_checkbox.h"
#include "ui/image/image.h"
#include "history/history.h"
#include "history/history_message.h"
#include "history/view/history_view_message.h"
#include "auth_session.h"
#include "apiwrap.h"
#include "data/data_session.h"
#include "data/data_user.h"
#include "data/data_document.h"
#include "boxes/confirm_box.h"
#include "styles/style_overview.h"
#include "styles/style_history.h"
#include "styles/style_boxes.h"
namespace {
constexpr auto kBackgroundsInRow = 3;
constexpr auto kMaxWallPaperSlugLength = 255;
[[nodiscard]] bool IsValidWallPaperSlug(const QString &slug) {
if (slug.isEmpty() || slug.size() > kMaxWallPaperSlugLength) {
return false;
}
return ranges::find_if(slug, [](QChar ch) {
return (ch != '.')
&& (ch != '_')
&& (ch != '-')
&& (ch < '0' || ch > '9')
&& (ch < 'a' || ch > 'z')
&& (ch < 'A' || ch > 'Z');
}) == slug.end();
}
AdminLog::OwnedItem GenerateTextItem(
not_null<HistoryView::ElementDelegate*> delegate,
not_null<History*> history,
const QString &text,
bool out) {
Expects(history->peer->isUser());
using Flag = MTPDmessage::Flag;
const auto id = ServerMaxMsgId + (ServerMaxMsgId / 3) + (out ? 1 : 0);
const auto flags = Flag::f_entities
| Flag::f_from_id
| (out ? Flag::f_out : Flag(0));
const auto replyTo = 0;
const auto viaBotId = 0;
const auto item = new HistoryMessage(
history,
id,
flags,
replyTo,
viaBotId,
unixtime(),
out ? history->session().userId() : peerToUser(history->peer->id),
QString(),
TextWithEntities{ TextUtilities::Clean(text) });
return AdminLog::OwnedItem(delegate, item);
}
QImage PrepareScaledFromFull(
const QImage &image,
Images::Option blur = Images::Option(0)) {
const auto size = st::boxWideWidth;
const auto width = std::max(image.width(), 1);
const auto height = std::max(image.height(), 1);
const auto takeWidth = (width > height)
? (width * size / height)
: size;
const auto takeHeight = (width > height)
? size
: (height * size / width);
return Images::prepare(
image,
takeWidth,
takeHeight,
Images::Option::Smooth | blur,
size,
size);
}
QPixmap PrepareScaledFromThumb(ImagePtr thumb) {
return thumb->loaded()
? App::pixmapFromImageInPlace(PrepareScaledFromFull(
thumb->original(),
Images::Option::Blurred))
: QPixmap();
}
} // namespace
@ -202,3 +281,251 @@ void BackgroundBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
}
BackgroundBox::Inner::~Inner() = default;
BackgroundPreviewBox::BackgroundPreviewBox(
QWidget*,
const Data::WallPaper &paper)
: _text1(GenerateTextItem(
this,
App::history(App::user(ServiceUserId)),
lang(lng_background_text1),
false))
, _text2(GenerateTextItem(
this,
App::history(App::user(ServiceUserId)),
lang(lng_background_text2),
true))
, _paper(paper)
, _radial(animation(this, &BackgroundPreviewBox::step_radial)) {
subscribe(Auth().downloaderTaskFinished(), [=] { update(); });
}
void BackgroundPreviewBox::prepare() {
setTitle(langFactory(lng_background_header));
addButton(langFactory(lng_background_apply), [=] { apply(); });
addButton(langFactory(lng_cancel), [=] { closeBox(); });
_scaled = PrepareScaledFromThumb(_paper.thumb);
checkLoadedDocument();
if (_paper.thumb && !_paper.thumb->loaded()) {
_paper.thumb->loadEvenCancelled(Data::FileOriginWallpaper(
_paper.id,
_paper.accessHash));
}
if (_paper.document) {
_paper.document->save(Data::FileOriginWallpaper(
_paper.id,
_paper.accessHash), QString());
if (_paper.document->loading()) {
_radial.start(_paper.document->progress());
}
}
_text1->setDisplayDate(true);
_text1->initDimensions();
_text1->resizeGetHeight(st::boxWideWidth);
_text2->initDimensions();
_text2->resizeGetHeight(st::boxWideWidth);
setDimensions(st::boxWideWidth, st::boxWideWidth);
}
void BackgroundPreviewBox::apply() {
App::main()->setChatBackground(_paper, std::move(_full));
closeBox();
}
void BackgroundPreviewBox::paintEvent(QPaintEvent *e) {
Painter p(this);
const auto ms = getms();
if (const auto color = Window::Theme::GetWallPaperColor(_paper.slug)) {
p.fillRect(e->rect(), *color);
} else {
if (_scaled.isNull()) {
_scaled = PrepareScaledFromThumb(_paper.thumb);
if (_scaled.isNull()) {
p.fillRect(e->rect(), st::boxBg);
return;
}
}
paintImage(p);
paintRadial(p, ms);
}
paintTexts(p, ms);
}
void BackgroundPreviewBox::paintImage(Painter &p) {
Expects(!_scaled.isNull());
const auto factor = cIntRetinaFactor();
const auto size = st::boxWideWidth;
const auto from = QRect(
0,
(size - height()) / 2 * factor,
size * factor,
height() * factor);
p.drawPixmap(rect(), _scaled, from);
}
void BackgroundPreviewBox::paintRadial(Painter &p, TimeMs ms) {
bool radial = false;
float64 radialOpacity = 0;
if (_radial.animating()) {
_radial.step(ms);
radial = _radial.animating();
radialOpacity = _radial.opacity();
}
if (!radial) {
return;
}
auto inner = radialRect();
p.setPen(Qt::NoPen);
p.setOpacity(radialOpacity);
p.setBrush(st::radialBg);
{
PainterHighQualityEnabler hq(p);
p.drawEllipse(inner);
}
p.setOpacity(1);
QRect arc(inner.marginsRemoved(QMargins(st::radialLine, st::radialLine, st::radialLine, st::radialLine)));
_radial.draw(p, arc, st::radialLine, st::radialFg);
}
QRect BackgroundPreviewBox::radialRect() const {
const auto available = height()
- st::historyPaddingBottom
- _text1->height()
- _text2->height()
- st::historyPaddingBottom;
return QRect(
QPoint(
(width() - st::radialSize.width()) / 2,
(available - st::radialSize.height()) / 2),
st::radialSize);
}
void BackgroundPreviewBox::paintTexts(Painter &p, TimeMs ms) {
const auto height1 = _text1->height();
const auto height2 = _text2->height();
const auto top = height()
- height1
- height2
- st::historyPaddingBottom;
p.translate(0, top);
_text1->draw(p, rect(), TextSelection(), ms);
p.translate(0, height1);
_text2->draw(p, rect(), TextSelection(), ms);
p.translate(0, height2);
}
void BackgroundPreviewBox::step_radial(TimeMs ms, bool timer) {
Expects(_paper.document != nullptr);
const auto document = _paper.document;
const auto wasAnimating = _radial.animating();
const auto updated = _radial.update(
document->progress(),
!document->loading(),
ms);
if (timer
&& (wasAnimating || _radial.animating())
&& (!anim::Disabled() || updated)) {
update(radialRect());
}
checkLoadedDocument();
}
void BackgroundPreviewBox::checkLoadedDocument() {
const auto document = _paper.document;
if (!document
|| !document->loaded(DocumentData::FilePathResolveChecked)
|| _generating) {
return;
}
_generating = Data::ReadImageAsync(document, [=](
QImage &&image) mutable {
auto [left, right] = base::make_binary_guard();
_generating = std::move(left);
crl::async([
this,
image = std::move(image),
guard = std::move(right)
]() mutable {
auto scaled = PrepareScaledFromFull(image);
crl::on_main([
this,
image = std::move(image),
scaled = std::move(scaled),
guard = std::move(guard)
]() mutable {
if (!guard) {
return;
}
_scaled = App::pixmapFromImageInPlace(std::move(scaled));
_full = std::move(image);
update();
});
});
});
}
bool BackgroundPreviewBox::Start(const QString &slug, const QString &mode) {
if (Window::Theme::GetWallPaperColor(slug)) {
Ui::show(Box<BackgroundPreviewBox>(Data::WallPaper{
Window::Theme::kCustomBackground,
0ULL, // accessHash
MTPDwallPaper::Flags(0),
slug,
}));
return true;
}
if (!IsValidWallPaperSlug(slug)) {
Ui::show(Box<InformBox>(lang(lng_background_bad_link)));
return false;
}
Auth().api().requestWallPaper(slug, [](const Data::WallPaper &result) {
Ui::show(Box<BackgroundPreviewBox>(result));
}, [](const RPCError &error) {
Ui::show(Box<InformBox>(lang(lng_background_bad_link)));
});
return true;
}
HistoryView::Context BackgroundPreviewBox::elementContext() {
return HistoryView::Context::ContactPreview;
}
std::unique_ptr<HistoryView::Element> BackgroundPreviewBox::elementCreate(
not_null<HistoryMessage*> message) {
return std::make_unique<HistoryView::Message>(this, message);
}
std::unique_ptr<HistoryView::Element> BackgroundPreviewBox::elementCreate(
not_null<HistoryService*> message) {
Unexpected("Service message in BackgroundPreviewBox.");
}
bool BackgroundPreviewBox::elementUnderCursor(
not_null<const Element*> view) {
return false;
}
void BackgroundPreviewBox::elementAnimationAutoplayAsync(
not_null<const Element*> element) {
}
TimeMs BackgroundPreviewBox::elementHighlightTime(
not_null<const Element*> element) {
return TimeMs();
}
bool BackgroundPreviewBox::elementInSelectionMode() {
return false;
}

View File

@ -7,7 +7,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "base/binary_guard.h"
#include "boxes/abstract_box.h"
#include "window/themes/window_theme.h"
#include "history/admin_log/history_admin_log_item.h"
#include "history/view/history_view_element.h"
#include "ui/effects/radial_animation.h"
namespace Ui {
class RoundCheckbox;
@ -27,3 +32,49 @@ private:
QPointer<Inner> _inner;
};
class BackgroundPreviewBox
: public BoxContent
, public HistoryView::ElementDelegate {
public:
BackgroundPreviewBox(QWidget*, const Data::WallPaper &paper);
static bool Start(const QString &slug, const QString &mode);
using Element = HistoryView::Element;
HistoryView::Context elementContext() override;
std::unique_ptr<Element> elementCreate(
not_null<HistoryMessage*> message) override;
std::unique_ptr<Element> elementCreate(
not_null<HistoryService*> message) override;
bool elementUnderCursor(not_null<const Element*> view) override;
void elementAnimationAutoplayAsync(
not_null<const Element*> element) override;
TimeMs elementHighlightTime(
not_null<const Element*> element) override;
bool elementInSelectionMode() override;
protected:
void prepare() override;
void paintEvent(QPaintEvent *e) override;
private:
void apply();
void step_radial(TimeMs ms, bool timer);
QRect radialRect() const;
void checkLoadedDocument();
void paintImage(Painter &p);
void paintRadial(Painter &p, TimeMs ms);
void paintTexts(Painter &p, TimeMs ms);
AdminLog::OwnedItem _text1;
AdminLog::OwnedItem _text2;
Data::WallPaper _paper;
QImage _full;
QPixmap _scaled;
Ui::RadialAnimation _radial;
base::binary_guard _generating;
};

View File

@ -44,7 +44,7 @@ QString tryConvertUrlToLocal(QString url) {
return qsl("tg://msg_url?") + shareUrlMatch->captured(1);
} else if (auto confirmPhoneMatch = regex_match(qsl("^confirmphone/?\\?(.+)"), query, matchOptions)) {
return qsl("tg://confirmphone?") + confirmPhoneMatch->captured(1);
} else if (auto ivMatch = regex_match(qsl("iv/?\\?(.+)(#|$)"), query, matchOptions)) {
} else if (auto ivMatch = regex_match(qsl("^iv/?\\?(.+)(#|$)"), query, matchOptions)) {
//
// We need to show our t.me page, not the url directly.
//
@ -55,10 +55,13 @@ QString tryConvertUrlToLocal(QString url) {
// return previewedUrl;
//}
return url;
} else if (auto socksMatch = regex_match(qsl("socks/?\\?(.+)(#|$)"), query, matchOptions)) {
} else if (auto socksMatch = regex_match(qsl("^socks/?\\?(.+)(#|$)"), query, matchOptions)) {
return qsl("tg://socks?") + socksMatch->captured(1);
} else if (auto proxyMatch = regex_match(qsl("proxy/?\\?(.+)(#|$)"), query, matchOptions)) {
} else if (auto proxyMatch = regex_match(qsl("^proxy/?\\?(.+)(#|$)"), query, matchOptions)) {
return qsl("tg://proxy?") + proxyMatch->captured(1);
} else if (auto bgMatch = regex_match(qsl("^bg/([a-zA-Z0-9\\.\\_\\-]+)(\\?(.+)?)?$"), query, matchOptions)) {
const auto params = bgMatch->captured(3);
return qsl("tg://bg?slug=") + bgMatch->captured(1) + (params.isEmpty() ? QString() : '&' + params);
} else if (auto usernameMatch = regex_match(qsl("^([a-zA-Z0-9\\.\\_]+)(/?\\?|/?$|/(\\d+)/?(?:\\?|$))"), query, matchOptions)) {
auto params = query.mid(usernameMatch->captured(0).size()).toString();
auto postParam = QString();

View File

@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "core/update_checker.h"
#include "boxes/confirm_phone_box.h"
#include "boxes/background_box.h"
#include "boxes/confirm_box.h"
#include "boxes/share_box.h"
#include "boxes/connection_box.h"
@ -162,11 +163,23 @@ bool ShowPassport(const Match &match, const QVariant &context) {
qthelp::UrlParamNameTransform::ToLower));
}
bool ShowWallPaper(const Match &match, const QVariant &context) {
if (!AuthSession::Exists()) {
return false;
}
const auto params = url_parse_params(
match->captured(1),
qthelp::UrlParamNameTransform::ToLower);
return BackgroundPreviewBox::Start(
params.value(qsl("slug")),
params.value(qsl("mode")));
}
bool ResolveUsername(const Match &match, const QVariant &context) {
if (!AuthSession::Exists()) {
return false;
}
auto params = url_parse_params(
const auto params = url_parse_params(
match->captured(1),
qthelp::UrlParamNameTransform::ToLower);
const auto domain = params.value(qsl("domain"));
@ -280,6 +293,10 @@ const std::vector<LocalUrlHandler> &LocalUrlHandlers() {
qsl("^passport/?\\?(.+)(#|$)"),
ShowPassport
},
{
qsl("^bg/?\\?(.+)(#|$)"),
ShowWallPaper
},
{
qsl("^resolve/?\\?(.+)(#|$)"),
ResolveUsername

View File

@ -382,7 +382,8 @@ void DocumentOpenClickHandler::Open(
if (data->status != FileReady) return;
QString filename;
if (!data->saveToCache()) {
if (!data->saveToCache()
|| (location.isEmpty() || (!data->data().isEmpty()))) {
filename = documentSaveFilename(data);
if (filename.isEmpty()) return;
}
@ -575,6 +576,9 @@ void DocumentData::setattributes(const QVector<MTPDocumentAttribute> &attributes
}
bool DocumentData::checkWallPaperProperties() {
if (type == WallPaperDocument) {
return true;
}
if (type != FileDocument
|| !thumb
|| !dimensions.width()
@ -1301,6 +1305,8 @@ uint8 DocumentData::cacheTag() const {
return Data::kVideoMessageCacheTag;
} else if (isAnimation()) {
return Data::kAnimationCacheTag;
} else if (type == WallPaperDocument) {
return Data::kImageCacheTag;
}
return 0;
}
@ -1526,4 +1532,39 @@ paf pif ps1 reg rgs scr sct shb shs u3p vb vbe vbs vbscript ws wsf");
FileExtension(filepath).toLower());
}
base::binary_guard ReadImageAsync(
not_null<DocumentData*> document,
FnMut<void(QImage&&)> done) {
auto [left, right] = base::make_binary_guard();
crl::async([
bytes = document->data(),
path = document->filepath(),
guard = std::move(left),
callback = std::move(done)
]() mutable {
auto format = QByteArray();
if (bytes.isEmpty()) {
QFile f(path);
if (f.size() <= App::kImageSizeLimit
&& f.open(QIODevice::ReadOnly)) {
bytes = f.readAll();
}
}
auto image = bytes.isEmpty()
? QImage()
: App::readImage(bytes, &format, false, nullptr);
crl::on_main([
guard = std::move(guard),
image = std::move(image),
callback = std::move(callback)
]() mutable {
if (!guard) {
return;
}
callback(std::move(image));
});
});
return std::move(right);
}
} // namespace Data

View File

@ -340,5 +340,8 @@ namespace Data {
QString FileExtension(const QString &filepath);
bool IsValidMediaFile(const QString &filepath);
bool IsExecutableName(const QString &filepath);
base::binary_guard ReadImageAsync(
not_null<DocumentData*> document,
FnMut<void(QImage&&)> done);
} // namespace Data

View File

@ -25,7 +25,7 @@ GoodThumbSource::GoodThumbSource(not_null<DocumentData*> document)
}
void GoodThumbSource::generate(base::binary_guard &&guard) {
if (!guard.alive()) {
if (!guard) {
return;
}
const auto data = _document->data();
@ -74,7 +74,7 @@ void GoodThumbSource::ready(
image = std::move(image),
bytes = std::move(bytesForCache)
]() mutable {
if (!guard.alive()) {
if (!guard) {
return;
}
if (image.isNull()) {
@ -162,7 +162,7 @@ bool GoodThumbSource::displayLoading() {
}
void GoodThumbSource::cancel() {
_loading.kill();
_loading = nullptr;
}
float64 GoodThumbSource::progress() {

View File

@ -178,12 +178,6 @@ struct FileReferenceAccumulator {
}, [](const MTPDmessages_savedGifsNotModified &data) {
});
}
void push(const MTPaccount_WallPapers &data) {
data.match([&](const MTPDaccount_wallPapers &data) {
push(data.vwallpapers);
}, [](const MTPDaccount_wallPapersNotModified &) {
});
}
UpdatedFileReferences result;
};
@ -248,7 +242,7 @@ UpdatedFileReferences GetFileReferences(const MTPmessages_SavedGifs &data) {
return GetFileReferencesHelper(data);
}
UpdatedFileReferences GetFileReferences(const MTPaccount_WallPapers &data) {
UpdatedFileReferences GetFileReferences(const MTPWallPaper &data) {
return GetFileReferencesHelper(data);
}

View File

@ -60,9 +60,17 @@ struct FileOriginSavedGifs {
}
};
struct FileOriginWallpapers {
inline bool operator<(const FileOriginWallpapers &) const {
return false;
struct FileOriginWallpaper {
FileOriginWallpaper(uint64 paperId, uint64 accessHash)
: paperId(paperId)
, accessHash(accessHash) {
}
uint64 paperId = 0;
uint64 accessHash = 0;
inline bool operator<(const FileOriginWallpaper &other) const {
return paperId < other.paperId;
}
};
@ -73,7 +81,7 @@ struct FileOrigin {
FileOriginPeerPhoto,
FileOriginStickerSet,
FileOriginSavedGifs,
FileOriginWallpapers>;
FileOriginWallpaper>;
FileOrigin() = default;
FileOrigin(FileOriginMessage data) : data(data) {
@ -86,7 +94,7 @@ struct FileOrigin {
}
FileOrigin(FileOriginSavedGifs data) : data(data) {
}
FileOrigin(FileOriginWallpapers data) : data(data) {
FileOrigin(FileOriginWallpaper data) : data(data) {
}
explicit operator bool() const {
@ -130,6 +138,6 @@ UpdatedFileReferences GetFileReferences(
const MTPmessages_FavedStickers &data);
UpdatedFileReferences GetFileReferences(const MTPmessages_StickerSet &data);
UpdatedFileReferences GetFileReferences(const MTPmessages_SavedGifs &data);
UpdatedFileReferences GetFileReferences(const MTPaccount_WallPapers &data);
UpdatedFileReferences GetFileReferences(const MTPWallPaper &data);
} // namespace Data

View File

@ -133,7 +133,7 @@ WebPageType ParseWebPageType(const MTPDwebPage &page) {
if (type == qstr("photo")) return WebPageType::Photo;
if (type == qstr("video")) return WebPageType::Video;
if (type == qstr("profile")) return WebPageType::Profile;
if (type == qstr("telegram_wallpaper")) return WebPageType::WallPaper;
if (type == qstr("telegram_background")) return WebPageType::WallPaper;
return page.has_cached_page()
? WebPageType::ArticleWithIV
: WebPageType::Article;
@ -218,7 +218,7 @@ bool WebPageData::applyChanges(
pendingTill = newPendingTill;
++version;
if (type == WebPageType::WallPaper) {
if (type == WebPageType::WallPaper && document) {
document->checkWallPaperProperties();
}

View File

@ -926,7 +926,9 @@ bool MainWidget::sendMessageFail(const RPCError &error) {
}
void MainWidget::onCacheBackground() {
if (Window::Theme::Background()->tile()) {
if (Window::Theme::Background()->color()) {
return;
} else if (Window::Theme::Background()->tile()) {
auto &bg = Window::Theme::Background()->pixmapForTiled();
auto result = QImage(_willCacheFor.width() * cIntRetinaFactor(), _willCacheFor.height() * cIntRetinaFactor(), QImage::Format_RGB32);
@ -1418,17 +1420,23 @@ void MainWidget::updateScrollColors() {
_history->updateScrollColors();
}
void MainWidget::setChatBackground(const Data::WallPaper &background) {
void MainWidget::setChatBackground(
const Data::WallPaper &background,
QImage &&image) {
using namespace Window::Theme;
if (isReadyChatBackground(background, image)) {
setReadyChatBackground(background, std::move(image));
return;
}
_background = std::make_unique<SettingBackground>();
_background->data = background;
if (_background->data.document) {
_background->data.document->save(
Data::FileOriginWallpapers(),
QString());
} else if (_background->data.thumb) {
_background->data.thumb->loadEvenCancelled(
Data::FileOriginWallpapers());
}
_background->data.document->save(
Data::FileOriginWallpaper(
_background->data.id,
_background->data.accessHash),
QString());
checkChatBackground();
const auto tile = (background.id == Window::Theme::kInitialBackground);
@ -1436,13 +1444,47 @@ void MainWidget::setChatBackground(const Data::WallPaper &background) {
Window::Theme::Background()->notify(Update(Update::Type::Start, tile));
}
bool MainWidget::isReadyChatBackground(
const Data::WallPaper &background,
const QImage &image) const {
return !image.isNull()
|| !background.document
|| Window::Theme::GetWallPaperColor(background.slug);
}
void MainWidget::setReadyChatBackground(
const Data::WallPaper &background,
QImage &&image) {
using namespace Window::Theme;
if (image.isNull()
&& !background.document
&& background.thumb
&& background.thumb->loaded()) {
image = background.thumb->pixNoCache(Data::FileOrigin()).toImage();
}
const auto resetToDefault = image.isNull()
&& !background.document
&& !GetWallPaperColor(background.slug)
&& (background.id != kInitialBackground);
const auto ready = resetToDefault
? Data::WallPaper{ kDefaultBackground }
: background;
Background()->setImage(ready, std::move(image));
const auto tile = (ready.id == kInitialBackground);
Background()->setTile(tile);
Ui::ForceFullRepaint(this);
}
bool MainWidget::chatBackgroundLoading() {
return (_background != nullptr);
}
float64 MainWidget::chatBackgroundProgress() const {
if (_background) {
if (_background->generating.alive()) {
if (_background->generating) {
return 1.;
} else if (_background->data.document) {
return _background->data.document->progress();
@ -1454,7 +1496,7 @@ float64 MainWidget::chatBackgroundProgress() const {
}
void MainWidget::checkChatBackground() {
if (!_background || _background->generating.alive()) {
if (!_background || _background->generating) {
return;
}
const auto document = _background->data.document;
@ -1464,42 +1506,13 @@ void MainWidget::checkChatBackground() {
return;
}
if (!document) {
const auto &thumb = _background->data.thumb;
setGeneratedBackground(thumb
? thumb->pixNoCache(Data::FileOrigin()).toImage()
: QImage());
return;
}
auto [left, right] = base::make_binary_guard();
_background->generating = std::move(left);
crl::async([
this,
bytes = document->data(),
path = document->filepath(),
guard = std::move(right)
]() mutable {
auto format = QByteArray();
if (bytes.isEmpty()) {
QFile f(path);
if (f.size() <= App::kImageSizeLimit
&& f.open(QIODevice::ReadOnly)) {
bytes = f.readAll();
}
}
auto image = bytes.isEmpty()
? QImage()
: App::readImage(bytes, &format, false, nullptr);
crl::on_main([
this,
guard = std::move(guard),
image = std::move(image)
]() mutable {
if (!guard.alive()) {
return;
}
setGeneratedBackground(std::move(image));
});
_background->generating = Data::ReadImageAsync(document, [=](
QImage &&image) {
const auto background = base::take(_background);
const auto ready = image.isNull()
? Data::WallPaper{ Window::Theme::kDefaultBackground }
: background->data;
setChatBackground(ready, std::move(image));
});
}
@ -1507,26 +1520,6 @@ ImagePtr MainWidget::newBackgroundThumb() {
return _background ? _background->data.thumb : ImagePtr();
}
void MainWidget::setGeneratedBackground(QImage &&image) {
using namespace Window::Theme;
if (image.isNull()) {
Background()->setImage({ kDefaultBackground });
} else if (false
|| _background->data.id == kInitialBackground
|| _background->data.id == kDefaultBackground) {
Background()->setImage(_background->data);
} else {
Background()->setImage(
_background->data,
std::move(image));
}
const auto tile = (_background->data.id == kInitialBackground);
Background()->setTile(tile);
_background = nullptr;
crl::on_main(this, [=] { update(); });
}
void MainWidget::messageDataReceived(ChannelData *channel, MsgId msgId) {
_history->messageDataReceived(channel, msgId);
}

View File

@ -227,7 +227,9 @@ public:
QPixmap cachedBackground(const QRect &forRect, int &x, int &y);
void updateScrollColors();
void setChatBackground(const Data::WallPaper &background);
void setChatBackground(
const Data::WallPaper &background,
QImage &&image = QImage());
bool chatBackgroundLoading();
float64 chatBackgroundProgress() const;
void checkChatBackground();
@ -454,7 +456,12 @@ private:
void ensureFirstColumnResizeAreaCreated();
void ensureThirdColumnResizeAreaCreated();
void setGeneratedBackground(QImage &&image);
bool isReadyChatBackground(
const Data::WallPaper &background,
const QImage &image) const;
void setReadyChatBackground(
const Data::WallPaper &background,
QImage &&image);
not_null<Window::Controller*> _controller;
bool _started = false;

View File

@ -1840,7 +1840,7 @@ void MediaView::initThemePreview() {
Window::Theme::CurrentData current;
current.backgroundId = Window::Theme::Background()->id();
current.backgroundImage = Window::Theme::Background()->pixmap().toImage();
current.backgroundImage = Window::Theme::Background()->createCurrentImage();
current.backgroundTiled = Window::Theme::Background()->tile();
const auto path = _doc->location().name();

View File

@ -265,26 +265,35 @@ void BackgroundRow::updateImage() {
Painter p(&back);
PainterHighQualityEnabler hq(p);
const auto &pix = Window::Theme::Background()->pixmap();
const auto sx = (pix.width() > pix.height())
? ((pix.width() - pix.height()) / 2)
: 0;
const auto sy = (pix.height() > pix.width())
? ((pix.height() - pix.width()) / 2)
: 0;
const auto s = (pix.width() > pix.height())
? pix.height()
: pix.width();
p.drawPixmap(
0,
0,
st::settingsBackgroundThumb,
st::settingsBackgroundThumb,
pix,
sx,
sy,
s,
s);
if (const auto color = Window::Theme::Background()->color()) {
p.fillRect(
0,
0,
st::settingsBackgroundThumb,
st::settingsBackgroundThumb,
*color);
} else {
const auto &pix = Window::Theme::Background()->pixmap();
const auto sx = (pix.width() > pix.height())
? ((pix.width() - pix.height()) / 2)
: 0;
const auto sy = (pix.height() > pix.width())
? ((pix.height() - pix.width()) / 2)
: 0;
const auto s = (pix.width() > pix.height())
? pix.height()
: pix.width();
p.drawPixmap(
0,
0,
st::settingsBackgroundThumb,
st::settingsBackgroundThumb,
pix,
sx,
sy,
s,
s);
}
}
Images::prepareRound(back, ImageRoundRadius::Small);
_background = App::pixmapFromImageInPlace(std::move(back));

View File

@ -74,7 +74,7 @@ void CleanerObject::scheduleNext() {
return;
}
_weak.with([](CleanerObject &that) {
if (that._guard.alive()) {
if (that._guard) {
that.cleanNext();
}
});

View File

@ -175,7 +175,7 @@ void CompactorObject::fail() {
void CompactorObject::done(int64 till) {
const auto path = compactPath();
_database.with([=, good = std::move(_guard)](DatabaseObject &database) {
if (good.alive()) {
if (good) {
database.compactorDone(path, till);
}
});

View File

@ -247,7 +247,7 @@ void FileLoader::localLoaded(
const StorageImageSaved &result,
const QByteArray &imageFormat,
const QImage &imageData) {
_localLoading.kill();
_localLoading = nullptr;
if (result.data.isEmpty()) {
_localStatus = LocalStatus::NotFound;
start(true);
@ -383,7 +383,7 @@ void FileLoader::loadLocal(const Storage::Cache::Key &key) {
format = std::move(format),
guard = std::move(guard)
]() mutable {
if (!guard.alive()) {
if (!guard) {
return;
}
localLoaded(
@ -433,7 +433,7 @@ bool FileLoader::tryLoadLocal() {
return false;
} else if (_localStatus != LocalStatus::NotTried) {
return _finished;
} else if (_localLoading.alive()) {
} else if (_localLoading) {
_localStatus = LocalStatus::Loading;
return true;
}

View File

@ -4061,7 +4061,7 @@ bool readBackground() {
#ifndef OS_MAC_OLD
reader.setAutoTransform(true);
#endif // OS_MAC_OLD
if (reader.read(&image)) {
if (reader.read(&image) || Window::Theme::GetWallPaperColor(slug)) {
_backgroundCanWrite = false;
Window::Theme::Background()->setImage({
id,

View File

@ -72,7 +72,7 @@ DatabasePointer Databases::get(
if (const auto i = _map.find(path); i != end(_map)) {
auto &kept = i->second;
Assert(kept.destroying.alive());
kept.destroying.kill();
kept.destroying = nullptr;
kept.database->reconfigure(settings);
return DatabasePointer(this, kept.database);
}
@ -93,7 +93,7 @@ void Databases::destroy(Cache::Database *database) {
database->close();
database->waitForCleaner([=, guard = std::move(second)]() mutable {
crl::on_main([=, guard = std::move(guard)]{
if (!guard.alive()) {
if (!guard) {
return;
}
_map.erase(path);

View File

@ -278,7 +278,7 @@ AdminLog::OwnedItem GenerateCommentItem(
replyTo,
viaBotId,
unixtime(),
Auth().userId(),
history->session().userId(),
QString(),
TextWithEntities{ TextUtilities::Clean(data.comment) });
return AdminLog::OwnedItem(delegate, item);
@ -296,7 +296,7 @@ AdminLog::OwnedItem GenerateContactItem(
const auto message = MTP_message(
MTP_flags(flags),
MTP_int(id),
MTP_int(Auth().userId()),
MTP_int(history->session().userId()),
peerToMTP(history->peer->id),
MTPMessageFwdHeader(),
MTP_int(viaBotId),

View File

@ -476,7 +476,7 @@ void Templates::reload() {
void Templates::load() {
if (_reloadAfterRead) {
return;
} else if (_reading.alive() || _updates) {
} else if (_reading || _updates) {
_reloadAfterRead = true;
return;
}
@ -491,7 +491,7 @@ void Templates::load() {
result = std::move(result),
guard = std::move(guard)
]() mutable {
if (!guard.alive()) {
if (!guard) {
return;
}
setData(std::move(result.result));

View File

@ -927,7 +927,7 @@ void Instance::checkUniversalImages() {
if (_id != Universal->id()) {
_id = Universal->id();
_generating.kill();
_generating = nullptr;
_sprites.clear();
}
if (!Universal->ensureLoaded() && Universal->id() != 0) {
@ -952,7 +952,7 @@ void Instance::generateCache() {
image = universal->generate(size, index),
guard = std::move(guard)
]() mutable {
if (!guard.alive() || universal != Universal) {
if (!guard || universal != Universal) {
return;
}
pushSprite(std::move(image));

View File

@ -750,6 +750,11 @@ QPixmap Image::pixBlurredColoredNoCache(
return App::pixmapFromImageInPlace(prepareColored(add, img));
}
QImage Image::original() const {
checkSource();
return _data;
}
void Image::automaticLoad(Data::FileOrigin origin, const HistoryItem *item) {
if (!loaded()) {
_source->automaticLoad(origin, item);

View File

@ -102,6 +102,8 @@ public:
static ImagePtr Blank();
QImage original() const;
const QPixmap &pix(
Data::FileOrigin origin,
int32 w = 0,

View File

@ -411,7 +411,7 @@ void Widget::hideSlow() {
auto [left, right] = base::make_binary_guard();
_hidingDelayed = std::move(left);
App::CallDelayed(st::notifySlowHide, this, [=, guard = std::move(right)] {
if (guard.alive() && _hiding) {
if (guard && _hiding) {
hideFast();
}
});

View File

@ -78,6 +78,10 @@ void SectionWidget::PaintBackground(QWidget *widget, QPaintEvent *event) {
auto clip = event->rect();
auto fill = QRect(0, 0, widget->width(), App::main()->height());
if (const auto color = Window::Theme::Background()->color()) {
p.fillRect(fill, *color);
return;
}
auto fromy = App::main()->backgroundFromY();
auto x = 0, y = 0;
auto cached = App::main()->cachedBackground(fill, x, y);

View File

@ -24,7 +24,6 @@ constexpr auto kThemeFileSizeLimit = 5 * 1024 * 1024;
constexpr auto kThemeBackgroundSizeLimit = 4 * 1024 * 1024;
constexpr auto kBackgroundSizeLimit = 25 * 1024 * 1024;
constexpr auto kThemeSchemeSizeLimit = 1024 * 1024;
constexpr auto kMinimumTiledSize = 512;
constexpr auto kNightThemeFile = str_const(":/gui/night.tdesktop-theme");
struct Applying {
@ -327,8 +326,9 @@ bool loadTheme(const QByteArray &content, Cached &cache, Instance *out = nullptr
}
QImage prepareBackgroundImage(QImage &&image) {
if (image.format() != QImage::Format_ARGB32 && image.format() != QImage::Format_ARGB32_Premultiplied && image.format() != QImage::Format_RGB32) {
image = std::move(image).convertToFormat(QImage::Format_RGB32);
if (image.format() != QImage::Format_ARGB32_Premultiplied) {
image = std::move(image).convertToFormat(
QImage::Format_ARGB32_Premultiplied);
}
image.setDevicePixelRatio(cRetinaFactor());
return std::move(image);
@ -391,9 +391,9 @@ void ChatBackground::setImage(
&& !nightMode()
&& _themeAbsolutePath.isEmpty();
if (paper.id == kThemeBackground && _themeImage.isNull()) {
_paper = { kDefaultBackground };
setPaper({ kDefaultBackground });
} else {
_paper = paper;
setPaper(paper);
if (needResetAdjustable) {
// If we had a default color theme with non-default background,
// and we switch to default background we must somehow switch from
@ -410,18 +410,21 @@ void ChatBackground::setImage(
|| id() == details::kTestingEditorBackground) {
if (id() == details::kTestingDefaultBackground || image.isNull()) {
image.load(qsl(":/gui/art/bg.jpg"));
_paper = { details::kTestingDefaultBackground };
setPaper({ details::kTestingDefaultBackground });
}
setPreparedImage(std::move(image));
setPreparedImage(prepareBackgroundImage(std::move(image)));
} else {
if (id() == kInitialBackground) {
image.load(qsl(":/gui/art/bg_initial.jpg"));
const auto scale = cScale() * cIntRetinaFactor();
if (scale != 100) {
image = image.scaledToWidth(ConvertScale(image.width(), scale), Qt::SmoothTransformation);
image = image.scaledToWidth(
ConvertScale(image.width(), scale),
Qt::SmoothTransformation);
}
} else if (id() == kDefaultBackground || image.isNull()) {
_paper = { kDefaultBackground };
} else if (id() == kDefaultBackground
|| (!color() && image.isNull())) {
setPaper({ kDefaultBackground });
image.load(qsl(":/gui/art/bg.jpg"));
}
Local::writeBackground(
@ -429,9 +432,16 @@ void ChatBackground::setImage(
((id() == kDefaultBackground || id() == kInitialBackground)
? QImage()
: image));
setPreparedImage(prepareBackgroundImage(std::move(image)));
if (const auto fill = color()) {
if (adjustPaletteRequired()) {
adjustPaletteUsingColor(*fill);
}
} else {
setPreparedImage(prepareBackgroundImage(std::move(image)));
}
}
Assert(!_pixmap.isNull() && !_pixmapForTiled.isNull());
Assert((!_pixmap.isNull() && !_pixmapForTiled.isNull()) || color());
notify(BackgroundUpdate(BackgroundUpdate::Type::New, tile()));
if (needResetAdjustable) {
notify(BackgroundUpdate(BackgroundUpdate::Type::TestingTheme, tile()), true);
@ -440,33 +450,11 @@ void ChatBackground::setImage(
}
void ChatBackground::setPreparedImage(QImage &&image) {
image = std::move(image).convertToFormat(QImage::Format_ARGB32_Premultiplied);
Expects(image.format() == QImage::Format_ARGB32_Premultiplied);
image.setDevicePixelRatio(cRetinaFactor());
const auto adjustColors = [&] {
const auto usingThemeBackground = [&] {
return (id() == kThemeBackground)
|| (id() == details::kTestingThemeBackground);
};
const auto usingDefaultBackground = [&] {
return (id() == kDefaultBackground)
|| (id() == details::kTestingDefaultBackground);
};
const auto testingPalette = [&] {
const auto path = AreTestingTheme()
? GlobalApplying.pathAbsolute
: _themeAbsolutePath;
return IsPaletteTestingPath(path);
};
if (testingPalette()) {
return false;
} else if (isNonDefaultThemeOrBackground() || nightMode()) {
return !usingThemeBackground();
}
return !usingDefaultBackground();
}();
if (adjustColors) {
if (adjustPaletteRequired()) {
adjustPaletteUsingBackground(image);
}
@ -500,6 +488,35 @@ void ChatBackground::setPreparedImage(QImage &&image) {
}
}
void ChatBackground::setPaper(const Data::WallPaper &paper) {
_paper = paper;
_paperColor = GetWallPaperColor(_paper.slug);
}
bool ChatBackground::adjustPaletteRequired() {
const auto usingThemeBackground = [&] {
return (id() == kThemeBackground)
|| (id() == details::kTestingThemeBackground);
};
const auto usingDefaultBackground = [&] {
return (id() == kDefaultBackground)
|| (id() == details::kTestingDefaultBackground);
};
const auto testingPalette = [&] {
const auto path = AreTestingTheme()
? GlobalApplying.pathAbsolute
: _themeAbsolutePath;
return IsPaletteTestingPath(path);
};
if (testingPalette()) {
return false;
} else if (isNonDefaultThemeOrBackground() || nightMode()) {
return !usingThemeBackground();
}
return !usingDefaultBackground();
}
void ChatBackground::adjustPaletteUsingBackground(const QImage &img) {
Assert(img.format() == QImage::Format_ARGB32_Premultiplied);
@ -522,9 +539,13 @@ void ChatBackground::adjustPaletteUsingBackground(const QImage &img) {
}
}
auto bgColor = QColor(components[0], components[1], components[2]);
auto hue = bgColor.hslHueF();
auto saturation = bgColor.hslSaturationF();
adjustPaletteUsingColor(
QColor(components[0], components[1], components[2]));
}
void ChatBackground::adjustPaletteUsingColor(QColor color) {
auto hue = color.hslHueF();
auto saturation = color.hslSaturationF();
for (const auto &color : _adjustableColors) {
adjustColor(color.item, hue, saturation);
}
@ -534,6 +555,18 @@ WallPaperId ChatBackground::id() const {
return _paper.id;
}
QImage ChatBackground::createCurrentImage() const {
if (const auto fill = color()) {
auto result = QImage(
kMinimumTiledSize,
kMinimumTiledSize,
QImage::Format_ARGB32_Premultiplied);
result.fill(*fill);
return result;
}
return pixmap().toImage();
}
bool ChatBackground::tile() const {
return nightMode() ? _tileNightValue : _tileDayValue;
}
@ -683,21 +716,21 @@ void ChatBackground::setTestingDefaultTheme() {
void ChatBackground::keepApplied(const QString &path, bool write) {
setThemeAbsolutePath(path);
if (id() == details::kTestingEditorBackground) {
_paper = { kCustomBackground };
setPaper({ kCustomBackground });
_themeImage = QImage();
_themeTile = false;
if (write) {
writeNewBackgroundSettings();
}
} else if (id() == details::kTestingThemeBackground) {
_paper = { kThemeBackground };
_themeImage = _pixmap.toImage();
setPaper({ kThemeBackground });
_themeImage = prepareBackgroundImage(_pixmap.toImage());
_themeTile = tile();
if (write) {
writeNewBackgroundSettings();
}
} else if (id() == details::kTestingDefaultBackground) {
_paper = { kDefaultBackground };
setPaper({ kDefaultBackground });
_themeImage = QImage();
_themeTile = false;
if (write) {
@ -980,6 +1013,35 @@ bool IsPaletteTestingPath(const QString &path) {
return false;
}
std::optional<QColor> GetWallPaperColor(const QString &slug) {
if (slug.size() != 6) {
return {};
} else if (ranges::find_if(slug, [](QChar ch) {
return (ch < 'a' || ch > 'f')
&& (ch < 'A' || ch > 'F')
&& (ch < '0' || ch > '9');
}) != slug.end()) {
return {};
}
const auto component = [](const QString &text, int index) {
const auto decimal = [](QChar hex) {
const auto code = hex.unicode();
return (code >= '0' && code <= '9')
? int(code - '0')
: (code >= 'a' && code <= 'f')
? int(code - 'a' + 0x0a)
: int(code - 'A' + 0x0a);
};
index *= 2;
return decimal(text[index]) * 0x10 + decimal(text[index + 1]);
};
return QColor(
component(slug, 0),
component(slug, 1),
component(slug, 2),
255);
}
void ComputeBackgroundRects(QRect wholeFill, QSize imageSize, QRect &to, QRect &from) {
if (uint64(imageSize.width()) * wholeFill.height() > uint64(imageSize.height()) * wholeFill.width()) {
float64 pxsize = wholeFill.height() / float64(imageSize.height());

View File

@ -41,6 +41,8 @@ constexpr auto kCustomBackground = details::FromLegacyBackgroundId(-1);
constexpr auto kInitialBackground = details::FromLegacyBackgroundId(0);
constexpr auto kDefaultBackground = details::FromLegacyBackgroundId(105);
constexpr auto kMinimumTiledSize = 512;
struct Cached {
QByteArray colors;
QByteArray background;
@ -78,16 +80,18 @@ void ApplyDefaultWithPath(const QString &themePath);
bool ApplyEditedPalette(const QString &path, const QByteArray &content);
void KeepApplied();
QString NightThemePath();
bool IsNightMode();
[[nodiscard]] bool IsNightMode();
void SetNightModeValue(bool nightMode);
void ToggleNightMode();
void ToggleNightMode(const QString &themePath);
bool IsNonDefaultBackground();
[[nodiscard]] bool IsNonDefaultBackground();
void Revert();
bool LoadFromFile(const QString &file, Instance *out, QByteArray *outContent);
bool IsPaletteTestingPath(const QString &path);
[[nodiscard]] std::optional<QColor> GetWallPaperColor(const QString &slug);
struct BackgroundUpdate {
enum class Type {
New,
@ -121,7 +125,7 @@ public:
void setTileDayValue(bool tile);
void setTileNightValue(bool tile);
void setThemeAbsolutePath(const QString &path);
QString themeAbsolutePath() const;
[[nodiscard]] QString themeAbsolutePath() const;
void reset();
void setTestingTheme(Instance &&theme);
@ -129,16 +133,20 @@ public:
void setTestingDefaultTheme();
void revert();
WallPaperId id() const;
const QPixmap &pixmap() const {
[[nodiscard]] WallPaperId id() const;
[[nodiscard]] const QPixmap &pixmap() const {
return _pixmap;
}
const QPixmap &pixmapForTiled() const {
[[nodiscard]] const QPixmap &pixmapForTiled() const {
return _pixmapForTiled;
}
bool tile() const;
bool tileDay() const;
bool tileNight() const;
[[nodiscard]] std::optional<QColor> color() const {
return _paperColor;
}
[[nodiscard]] QImage createCurrentImage() const;
[[nodiscard]] bool tile() const;
[[nodiscard]] bool tileDay() const;
[[nodiscard]] bool tileNight() const;
private:
struct AdjustableColor {
@ -152,16 +160,19 @@ private:
void saveForRevert();
void setPreparedImage(QImage &&image);
void writeNewBackgroundSettings();
void setPaper(const Data::WallPaper &paper);
[[nodiscard]] bool adjustPaletteRequired();
void adjustPaletteUsingBackground(const QImage &img);
void adjustPaletteUsingColor(QColor color);
void restoreAdjustableColors();
void setNightModeValue(bool nightMode);
bool nightMode() const;
[[nodiscard]] bool nightMode() const;
void toggleNightMode(std::optional<QString> themePath);
void keepApplied(const QString &path, bool write);
bool isNonDefaultThemeOrBackground();
bool isNonDefaultBackground();
[[nodiscard]] bool isNonDefaultThemeOrBackground();
[[nodiscard]] bool isNonDefaultBackground();
friend bool IsNightMode();
friend void SetNightModeValue(bool nightMode);
@ -171,6 +182,7 @@ private:
friend bool IsNonDefaultBackground();
Data::WallPaper _paper = { details::kUninitializedBackground };
std::optional<QColor> _paperColor;
QPixmap _pixmap;
QPixmap _pixmapForTiled;
bool _nightMode = false;

View File

@ -319,8 +319,8 @@ void Editor::Inner::prepare() {
}
Fn<void()> Editor::Inner::exportCallback() {
return App::LambdaDelayed(st::defaultRippleAnimation.hideDuration, this, [this] {
auto background = Background()->pixmap().toImage();
return App::LambdaDelayed(st::defaultRippleAnimation.hideDuration, this, [=] {
auto background = Background()->createCurrentImage();
auto backgroundContent = QByteArray();
auto tiled = Background()->tile();
{