mirror of https://github.com/procxx/kepka.git
Handle t.me/bg links with wallpapers / colors.
This commit is contained in:
parent
e59a68cd68
commit
1894b8fcf7
|
@ -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_reverting#other" = "Reverting to the old theme in {count} seconds.";
|
||||||
"lng_theme_keep_changes" = "Keep changes";
|
"lng_theme_keep_changes" = "Keep changes";
|
||||||
"lng_theme_revert" = "Revert";
|
"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_ask" = "Ask download path for each file";
|
||||||
"lng_download_path" = "Download path";
|
"lng_download_path" = "Download path";
|
||||||
|
|
|
@ -40,6 +40,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "window/notifications_manager.h"
|
#include "window/notifications_manager.h"
|
||||||
#include "window/window_lock_widgets.h"
|
#include "window/window_lock_widgets.h"
|
||||||
#include "window/window_controller.h"
|
#include "window/window_controller.h"
|
||||||
|
#include "window/themes/window_theme.h"
|
||||||
#include "inline_bots/inline_bot_result.h"
|
#include "inline_bots/inline_bot_result.h"
|
||||||
#include "chat_helpers/message_field.h"
|
#include "chat_helpers/message_field.h"
|
||||||
#include "chat_helpers/stickers.h"
|
#include "chat_helpers/stickers.h"
|
||||||
|
@ -864,6 +865,52 @@ void ApiWrap::requestFakeChatListMessage(
|
||||||
}).send();
|
}).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) {
|
void ApiWrap::requestFullPeer(not_null<PeerData*> peer) {
|
||||||
if (_fullPeerRequests.contains(peer)) {
|
if (_fullPeerRequests.contains(peer)) {
|
||||||
return;
|
return;
|
||||||
|
@ -2829,8 +2876,11 @@ void ApiWrap::refreshFileReference(
|
||||||
request(
|
request(
|
||||||
MTPmessages_GetSavedGifs(MTP_int(0)),
|
MTPmessages_GetSavedGifs(MTP_int(0)),
|
||||||
[] { crl::on_main([] { Local::writeSavedGifs(); }); });
|
[] { crl::on_main([] { Local::writeSavedGifs(); }); });
|
||||||
}, [&](Data::FileOriginWallpapers data) {
|
}, [&](Data::FileOriginWallpaper data) {
|
||||||
request(MTPaccount_GetWallPapers(MTP_int(0)));
|
request(MTPaccount_GetWallPaper(
|
||||||
|
MTP_inputWallPaper(
|
||||||
|
MTP_long(data.paperId),
|
||||||
|
MTP_long(data.accessHash))));
|
||||||
}, [&](std::nullopt_t) {
|
}, [&](std::nullopt_t) {
|
||||||
fail();
|
fail();
|
||||||
});
|
});
|
||||||
|
|
|
@ -26,6 +26,7 @@ class mtpFileLoader;
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
struct UpdatedFileReferences;
|
struct UpdatedFileReferences;
|
||||||
|
struct WallPaper;
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
||||||
namespace InlineBots {
|
namespace InlineBots {
|
||||||
|
@ -92,6 +93,11 @@ public:
|
||||||
//void changeDialogUnreadMark(not_null<Data::Feed*> feed, bool unread); // #feed
|
//void changeDialogUnreadMark(not_null<Data::Feed*> feed, bool unread); // #feed
|
||||||
void requestFakeChatListMessage(not_null<History*> history);
|
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 requestFullPeer(not_null<PeerData*> peer);
|
||||||
void requestPeer(not_null<PeerData*> peer);
|
void requestPeer(not_null<PeerData*> peer);
|
||||||
void requestPeers(const QList<PeerData*> &peers);
|
void requestPeers(const QList<PeerData*> &peers);
|
||||||
|
@ -775,4 +781,9 @@ private:
|
||||||
base::flat_map<FullMsgId, mtpRequestId> _pollCloseRequestIds;
|
base::flat_map<FullMsgId, mtpRequestId> _pollCloseRequestIds;
|
||||||
base::flat_map<FullMsgId, mtpRequestId> _pollReloadRequestIds;
|
base::flat_map<FullMsgId, mtpRequestId> _pollReloadRequestIds;
|
||||||
|
|
||||||
|
mtpRequestId _wallPaperRequestId = 0;
|
||||||
|
QString _wallPaperSlug;
|
||||||
|
Fn<void(const Data::WallPaper &)> _wallPaperDone;
|
||||||
|
Fn<void(const RPCError &)> _wallPaperFail;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,7 +21,9 @@ public:
|
||||||
~binary_guard();
|
~binary_guard();
|
||||||
|
|
||||||
bool alive() const;
|
bool alive() const;
|
||||||
void kill();
|
|
||||||
|
binary_guard &operator=(std::nullptr_t);
|
||||||
|
explicit operator bool() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void destroy();
|
void destroy();
|
||||||
|
@ -44,15 +46,20 @@ inline binary_guard &binary_guard::operator=(binary_guard &&other) {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline binary_guard::~binary_guard() {
|
inline binary_guard &binary_guard::operator=(std::nullptr_t) {
|
||||||
destroy();
|
destroy();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline binary_guard::operator bool() const {
|
||||||
|
return alive();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool binary_guard::alive() const {
|
inline bool binary_guard::alive() const {
|
||||||
return _bothAlive && _bothAlive->load();
|
return _bothAlive && _bothAlive->load();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void binary_guard::kill() {
|
inline binary_guard::~binary_guard() {
|
||||||
destroy();
|
destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -301,11 +301,11 @@ void ConcurrentTimer::cancelAndSchedule(int timeout) {
|
||||||
runner = _runner,
|
runner = _runner,
|
||||||
guard = std::move(guards.second)
|
guard = std::move(guards.second)
|
||||||
]() mutable {
|
]() mutable {
|
||||||
if (!guard.alive()) {
|
if (!guard) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
runner([=, guard = std::move(guard)] {
|
runner([=, guard = std::move(guard)] {
|
||||||
if (!guard.alive()) {
|
if (!guard) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
timerEvent();
|
timerEvent();
|
||||||
|
|
|
@ -13,14 +13,93 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "window/themes/window_theme.h"
|
#include "window/themes/window_theme.h"
|
||||||
#include "ui/effects/round_checkbox.h"
|
#include "ui/effects/round_checkbox.h"
|
||||||
#include "ui/image/image.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 "auth_session.h"
|
||||||
|
#include "apiwrap.h"
|
||||||
#include "data/data_session.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_overview.h"
|
||||||
|
#include "styles/style_history.h"
|
||||||
#include "styles/style_boxes.h"
|
#include "styles/style_boxes.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr auto kBackgroundsInRow = 3;
|
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
|
} // namespace
|
||||||
|
|
||||||
|
@ -202,3 +281,251 @@ void BackgroundBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
BackgroundBox::Inner::~Inner() = default;
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -7,7 +7,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "base/binary_guard.h"
|
||||||
#include "boxes/abstract_box.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 {
|
namespace Ui {
|
||||||
class RoundCheckbox;
|
class RoundCheckbox;
|
||||||
|
@ -27,3 +32,49 @@ private:
|
||||||
QPointer<Inner> _inner;
|
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;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
|
@ -44,7 +44,7 @@ QString tryConvertUrlToLocal(QString url) {
|
||||||
return qsl("tg://msg_url?") + shareUrlMatch->captured(1);
|
return qsl("tg://msg_url?") + shareUrlMatch->captured(1);
|
||||||
} else if (auto confirmPhoneMatch = regex_match(qsl("^confirmphone/?\\?(.+)"), query, matchOptions)) {
|
} else if (auto confirmPhoneMatch = regex_match(qsl("^confirmphone/?\\?(.+)"), query, matchOptions)) {
|
||||||
return qsl("tg://confirmphone?") + confirmPhoneMatch->captured(1);
|
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.
|
// We need to show our t.me page, not the url directly.
|
||||||
//
|
//
|
||||||
|
@ -55,10 +55,13 @@ QString tryConvertUrlToLocal(QString url) {
|
||||||
// return previewedUrl;
|
// return previewedUrl;
|
||||||
//}
|
//}
|
||||||
return url;
|
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);
|
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);
|
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)) {
|
} else if (auto usernameMatch = regex_match(qsl("^([a-zA-Z0-9\\.\\_]+)(/?\\?|/?$|/(\\d+)/?(?:\\?|$))"), query, matchOptions)) {
|
||||||
auto params = query.mid(usernameMatch->captured(0).size()).toString();
|
auto params = query.mid(usernameMatch->captured(0).size()).toString();
|
||||||
auto postParam = QString();
|
auto postParam = QString();
|
||||||
|
|
|
@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "core/update_checker.h"
|
#include "core/update_checker.h"
|
||||||
#include "boxes/confirm_phone_box.h"
|
#include "boxes/confirm_phone_box.h"
|
||||||
|
#include "boxes/background_box.h"
|
||||||
#include "boxes/confirm_box.h"
|
#include "boxes/confirm_box.h"
|
||||||
#include "boxes/share_box.h"
|
#include "boxes/share_box.h"
|
||||||
#include "boxes/connection_box.h"
|
#include "boxes/connection_box.h"
|
||||||
|
@ -162,11 +163,23 @@ bool ShowPassport(const Match &match, const QVariant &context) {
|
||||||
qthelp::UrlParamNameTransform::ToLower));
|
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) {
|
bool ResolveUsername(const Match &match, const QVariant &context) {
|
||||||
if (!AuthSession::Exists()) {
|
if (!AuthSession::Exists()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
auto params = url_parse_params(
|
const auto params = url_parse_params(
|
||||||
match->captured(1),
|
match->captured(1),
|
||||||
qthelp::UrlParamNameTransform::ToLower);
|
qthelp::UrlParamNameTransform::ToLower);
|
||||||
const auto domain = params.value(qsl("domain"));
|
const auto domain = params.value(qsl("domain"));
|
||||||
|
@ -280,6 +293,10 @@ const std::vector<LocalUrlHandler> &LocalUrlHandlers() {
|
||||||
qsl("^passport/?\\?(.+)(#|$)"),
|
qsl("^passport/?\\?(.+)(#|$)"),
|
||||||
ShowPassport
|
ShowPassport
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
qsl("^bg/?\\?(.+)(#|$)"),
|
||||||
|
ShowWallPaper
|
||||||
|
},
|
||||||
{
|
{
|
||||||
qsl("^resolve/?\\?(.+)(#|$)"),
|
qsl("^resolve/?\\?(.+)(#|$)"),
|
||||||
ResolveUsername
|
ResolveUsername
|
||||||
|
|
|
@ -382,7 +382,8 @@ void DocumentOpenClickHandler::Open(
|
||||||
if (data->status != FileReady) return;
|
if (data->status != FileReady) return;
|
||||||
|
|
||||||
QString filename;
|
QString filename;
|
||||||
if (!data->saveToCache()) {
|
if (!data->saveToCache()
|
||||||
|
|| (location.isEmpty() || (!data->data().isEmpty()))) {
|
||||||
filename = documentSaveFilename(data);
|
filename = documentSaveFilename(data);
|
||||||
if (filename.isEmpty()) return;
|
if (filename.isEmpty()) return;
|
||||||
}
|
}
|
||||||
|
@ -575,6 +576,9 @@ void DocumentData::setattributes(const QVector<MTPDocumentAttribute> &attributes
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DocumentData::checkWallPaperProperties() {
|
bool DocumentData::checkWallPaperProperties() {
|
||||||
|
if (type == WallPaperDocument) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (type != FileDocument
|
if (type != FileDocument
|
||||||
|| !thumb
|
|| !thumb
|
||||||
|| !dimensions.width()
|
|| !dimensions.width()
|
||||||
|
@ -1301,6 +1305,8 @@ uint8 DocumentData::cacheTag() const {
|
||||||
return Data::kVideoMessageCacheTag;
|
return Data::kVideoMessageCacheTag;
|
||||||
} else if (isAnimation()) {
|
} else if (isAnimation()) {
|
||||||
return Data::kAnimationCacheTag;
|
return Data::kAnimationCacheTag;
|
||||||
|
} else if (type == WallPaperDocument) {
|
||||||
|
return Data::kImageCacheTag;
|
||||||
}
|
}
|
||||||
return 0;
|
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());
|
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
|
} // namespace Data
|
||||||
|
|
|
@ -340,5 +340,8 @@ namespace Data {
|
||||||
QString FileExtension(const QString &filepath);
|
QString FileExtension(const QString &filepath);
|
||||||
bool IsValidMediaFile(const QString &filepath);
|
bool IsValidMediaFile(const QString &filepath);
|
||||||
bool IsExecutableName(const QString &filepath);
|
bool IsExecutableName(const QString &filepath);
|
||||||
|
base::binary_guard ReadImageAsync(
|
||||||
|
not_null<DocumentData*> document,
|
||||||
|
FnMut<void(QImage&&)> done);
|
||||||
|
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
|
@ -25,7 +25,7 @@ GoodThumbSource::GoodThumbSource(not_null<DocumentData*> document)
|
||||||
}
|
}
|
||||||
|
|
||||||
void GoodThumbSource::generate(base::binary_guard &&guard) {
|
void GoodThumbSource::generate(base::binary_guard &&guard) {
|
||||||
if (!guard.alive()) {
|
if (!guard) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto data = _document->data();
|
const auto data = _document->data();
|
||||||
|
@ -74,7 +74,7 @@ void GoodThumbSource::ready(
|
||||||
image = std::move(image),
|
image = std::move(image),
|
||||||
bytes = std::move(bytesForCache)
|
bytes = std::move(bytesForCache)
|
||||||
]() mutable {
|
]() mutable {
|
||||||
if (!guard.alive()) {
|
if (!guard) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (image.isNull()) {
|
if (image.isNull()) {
|
||||||
|
@ -162,7 +162,7 @@ bool GoodThumbSource::displayLoading() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GoodThumbSource::cancel() {
|
void GoodThumbSource::cancel() {
|
||||||
_loading.kill();
|
_loading = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
float64 GoodThumbSource::progress() {
|
float64 GoodThumbSource::progress() {
|
||||||
|
|
|
@ -178,12 +178,6 @@ struct FileReferenceAccumulator {
|
||||||
}, [](const MTPDmessages_savedGifsNotModified &data) {
|
}, [](const MTPDmessages_savedGifsNotModified &data) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
void push(const MTPaccount_WallPapers &data) {
|
|
||||||
data.match([&](const MTPDaccount_wallPapers &data) {
|
|
||||||
push(data.vwallpapers);
|
|
||||||
}, [](const MTPDaccount_wallPapersNotModified &) {
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdatedFileReferences result;
|
UpdatedFileReferences result;
|
||||||
};
|
};
|
||||||
|
@ -248,7 +242,7 @@ UpdatedFileReferences GetFileReferences(const MTPmessages_SavedGifs &data) {
|
||||||
return GetFileReferencesHelper(data);
|
return GetFileReferencesHelper(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdatedFileReferences GetFileReferences(const MTPaccount_WallPapers &data) {
|
UpdatedFileReferences GetFileReferences(const MTPWallPaper &data) {
|
||||||
return GetFileReferencesHelper(data);
|
return GetFileReferencesHelper(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,9 +60,17 @@ struct FileOriginSavedGifs {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FileOriginWallpapers {
|
struct FileOriginWallpaper {
|
||||||
inline bool operator<(const FileOriginWallpapers &) const {
|
FileOriginWallpaper(uint64 paperId, uint64 accessHash)
|
||||||
return false;
|
: 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,
|
FileOriginPeerPhoto,
|
||||||
FileOriginStickerSet,
|
FileOriginStickerSet,
|
||||||
FileOriginSavedGifs,
|
FileOriginSavedGifs,
|
||||||
FileOriginWallpapers>;
|
FileOriginWallpaper>;
|
||||||
|
|
||||||
FileOrigin() = default;
|
FileOrigin() = default;
|
||||||
FileOrigin(FileOriginMessage data) : data(data) {
|
FileOrigin(FileOriginMessage data) : data(data) {
|
||||||
|
@ -86,7 +94,7 @@ struct FileOrigin {
|
||||||
}
|
}
|
||||||
FileOrigin(FileOriginSavedGifs data) : data(data) {
|
FileOrigin(FileOriginSavedGifs data) : data(data) {
|
||||||
}
|
}
|
||||||
FileOrigin(FileOriginWallpapers data) : data(data) {
|
FileOrigin(FileOriginWallpaper data) : data(data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
explicit operator bool() const {
|
explicit operator bool() const {
|
||||||
|
@ -130,6 +138,6 @@ UpdatedFileReferences GetFileReferences(
|
||||||
const MTPmessages_FavedStickers &data);
|
const MTPmessages_FavedStickers &data);
|
||||||
UpdatedFileReferences GetFileReferences(const MTPmessages_StickerSet &data);
|
UpdatedFileReferences GetFileReferences(const MTPmessages_StickerSet &data);
|
||||||
UpdatedFileReferences GetFileReferences(const MTPmessages_SavedGifs &data);
|
UpdatedFileReferences GetFileReferences(const MTPmessages_SavedGifs &data);
|
||||||
UpdatedFileReferences GetFileReferences(const MTPaccount_WallPapers &data);
|
UpdatedFileReferences GetFileReferences(const MTPWallPaper &data);
|
||||||
|
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
|
@ -133,7 +133,7 @@ WebPageType ParseWebPageType(const MTPDwebPage &page) {
|
||||||
if (type == qstr("photo")) return WebPageType::Photo;
|
if (type == qstr("photo")) return WebPageType::Photo;
|
||||||
if (type == qstr("video")) return WebPageType::Video;
|
if (type == qstr("video")) return WebPageType::Video;
|
||||||
if (type == qstr("profile")) return WebPageType::Profile;
|
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()
|
return page.has_cached_page()
|
||||||
? WebPageType::ArticleWithIV
|
? WebPageType::ArticleWithIV
|
||||||
: WebPageType::Article;
|
: WebPageType::Article;
|
||||||
|
@ -218,7 +218,7 @@ bool WebPageData::applyChanges(
|
||||||
pendingTill = newPendingTill;
|
pendingTill = newPendingTill;
|
||||||
++version;
|
++version;
|
||||||
|
|
||||||
if (type == WebPageType::WallPaper) {
|
if (type == WebPageType::WallPaper && document) {
|
||||||
document->checkWallPaperProperties();
|
document->checkWallPaperProperties();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -926,7 +926,9 @@ bool MainWidget::sendMessageFail(const RPCError &error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWidget::onCacheBackground() {
|
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 &bg = Window::Theme::Background()->pixmapForTiled();
|
||||||
|
|
||||||
auto result = QImage(_willCacheFor.width() * cIntRetinaFactor(), _willCacheFor.height() * cIntRetinaFactor(), QImage::Format_RGB32);
|
auto result = QImage(_willCacheFor.width() * cIntRetinaFactor(), _willCacheFor.height() * cIntRetinaFactor(), QImage::Format_RGB32);
|
||||||
|
@ -1418,17 +1420,23 @@ void MainWidget::updateScrollColors() {
|
||||||
_history->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 = std::make_unique<SettingBackground>();
|
||||||
_background->data = background;
|
_background->data = background;
|
||||||
if (_background->data.document) {
|
_background->data.document->save(
|
||||||
_background->data.document->save(
|
Data::FileOriginWallpaper(
|
||||||
Data::FileOriginWallpapers(),
|
_background->data.id,
|
||||||
QString());
|
_background->data.accessHash),
|
||||||
} else if (_background->data.thumb) {
|
QString());
|
||||||
_background->data.thumb->loadEvenCancelled(
|
|
||||||
Data::FileOriginWallpapers());
|
|
||||||
}
|
|
||||||
checkChatBackground();
|
checkChatBackground();
|
||||||
|
|
||||||
const auto tile = (background.id == Window::Theme::kInitialBackground);
|
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));
|
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() {
|
bool MainWidget::chatBackgroundLoading() {
|
||||||
return (_background != nullptr);
|
return (_background != nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
float64 MainWidget::chatBackgroundProgress() const {
|
float64 MainWidget::chatBackgroundProgress() const {
|
||||||
if (_background) {
|
if (_background) {
|
||||||
if (_background->generating.alive()) {
|
if (_background->generating) {
|
||||||
return 1.;
|
return 1.;
|
||||||
} else if (_background->data.document) {
|
} else if (_background->data.document) {
|
||||||
return _background->data.document->progress();
|
return _background->data.document->progress();
|
||||||
|
@ -1454,7 +1496,7 @@ float64 MainWidget::chatBackgroundProgress() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWidget::checkChatBackground() {
|
void MainWidget::checkChatBackground() {
|
||||||
if (!_background || _background->generating.alive()) {
|
if (!_background || _background->generating) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto document = _background->data.document;
|
const auto document = _background->data.document;
|
||||||
|
@ -1464,42 +1506,13 @@ void MainWidget::checkChatBackground() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!document) {
|
_background->generating = Data::ReadImageAsync(document, [=](
|
||||||
const auto &thumb = _background->data.thumb;
|
QImage &&image) {
|
||||||
setGeneratedBackground(thumb
|
const auto background = base::take(_background);
|
||||||
? thumb->pixNoCache(Data::FileOrigin()).toImage()
|
const auto ready = image.isNull()
|
||||||
: QImage());
|
? Data::WallPaper{ Window::Theme::kDefaultBackground }
|
||||||
return;
|
: background->data;
|
||||||
}
|
setChatBackground(ready, std::move(image));
|
||||||
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));
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1507,26 +1520,6 @@ ImagePtr MainWidget::newBackgroundThumb() {
|
||||||
return _background ? _background->data.thumb : ImagePtr();
|
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) {
|
void MainWidget::messageDataReceived(ChannelData *channel, MsgId msgId) {
|
||||||
_history->messageDataReceived(channel, msgId);
|
_history->messageDataReceived(channel, msgId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -227,7 +227,9 @@ public:
|
||||||
QPixmap cachedBackground(const QRect &forRect, int &x, int &y);
|
QPixmap cachedBackground(const QRect &forRect, int &x, int &y);
|
||||||
void updateScrollColors();
|
void updateScrollColors();
|
||||||
|
|
||||||
void setChatBackground(const Data::WallPaper &background);
|
void setChatBackground(
|
||||||
|
const Data::WallPaper &background,
|
||||||
|
QImage &&image = QImage());
|
||||||
bool chatBackgroundLoading();
|
bool chatBackgroundLoading();
|
||||||
float64 chatBackgroundProgress() const;
|
float64 chatBackgroundProgress() const;
|
||||||
void checkChatBackground();
|
void checkChatBackground();
|
||||||
|
@ -454,7 +456,12 @@ private:
|
||||||
void ensureFirstColumnResizeAreaCreated();
|
void ensureFirstColumnResizeAreaCreated();
|
||||||
void ensureThirdColumnResizeAreaCreated();
|
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;
|
not_null<Window::Controller*> _controller;
|
||||||
bool _started = false;
|
bool _started = false;
|
||||||
|
|
|
@ -1840,7 +1840,7 @@ void MediaView::initThemePreview() {
|
||||||
|
|
||||||
Window::Theme::CurrentData current;
|
Window::Theme::CurrentData current;
|
||||||
current.backgroundId = Window::Theme::Background()->id();
|
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();
|
current.backgroundTiled = Window::Theme::Background()->tile();
|
||||||
|
|
||||||
const auto path = _doc->location().name();
|
const auto path = _doc->location().name();
|
||||||
|
|
|
@ -265,26 +265,35 @@ void BackgroundRow::updateImage() {
|
||||||
Painter p(&back);
|
Painter p(&back);
|
||||||
PainterHighQualityEnabler hq(p);
|
PainterHighQualityEnabler hq(p);
|
||||||
|
|
||||||
const auto &pix = Window::Theme::Background()->pixmap();
|
if (const auto color = Window::Theme::Background()->color()) {
|
||||||
const auto sx = (pix.width() > pix.height())
|
p.fillRect(
|
||||||
? ((pix.width() - pix.height()) / 2)
|
0,
|
||||||
: 0;
|
0,
|
||||||
const auto sy = (pix.height() > pix.width())
|
st::settingsBackgroundThumb,
|
||||||
? ((pix.height() - pix.width()) / 2)
|
st::settingsBackgroundThumb,
|
||||||
: 0;
|
*color);
|
||||||
const auto s = (pix.width() > pix.height())
|
} else {
|
||||||
? pix.height()
|
const auto &pix = Window::Theme::Background()->pixmap();
|
||||||
: pix.width();
|
const auto sx = (pix.width() > pix.height())
|
||||||
p.drawPixmap(
|
? ((pix.width() - pix.height()) / 2)
|
||||||
0,
|
: 0;
|
||||||
0,
|
const auto sy = (pix.height() > pix.width())
|
||||||
st::settingsBackgroundThumb,
|
? ((pix.height() - pix.width()) / 2)
|
||||||
st::settingsBackgroundThumb,
|
: 0;
|
||||||
pix,
|
const auto s = (pix.width() > pix.height())
|
||||||
sx,
|
? pix.height()
|
||||||
sy,
|
: pix.width();
|
||||||
s,
|
p.drawPixmap(
|
||||||
s);
|
0,
|
||||||
|
0,
|
||||||
|
st::settingsBackgroundThumb,
|
||||||
|
st::settingsBackgroundThumb,
|
||||||
|
pix,
|
||||||
|
sx,
|
||||||
|
sy,
|
||||||
|
s,
|
||||||
|
s);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Images::prepareRound(back, ImageRoundRadius::Small);
|
Images::prepareRound(back, ImageRoundRadius::Small);
|
||||||
_background = App::pixmapFromImageInPlace(std::move(back));
|
_background = App::pixmapFromImageInPlace(std::move(back));
|
||||||
|
|
|
@ -74,7 +74,7 @@ void CleanerObject::scheduleNext() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_weak.with([](CleanerObject &that) {
|
_weak.with([](CleanerObject &that) {
|
||||||
if (that._guard.alive()) {
|
if (that._guard) {
|
||||||
that.cleanNext();
|
that.cleanNext();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -175,7 +175,7 @@ void CompactorObject::fail() {
|
||||||
void CompactorObject::done(int64 till) {
|
void CompactorObject::done(int64 till) {
|
||||||
const auto path = compactPath();
|
const auto path = compactPath();
|
||||||
_database.with([=, good = std::move(_guard)](DatabaseObject &database) {
|
_database.with([=, good = std::move(_guard)](DatabaseObject &database) {
|
||||||
if (good.alive()) {
|
if (good) {
|
||||||
database.compactorDone(path, till);
|
database.compactorDone(path, till);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -247,7 +247,7 @@ void FileLoader::localLoaded(
|
||||||
const StorageImageSaved &result,
|
const StorageImageSaved &result,
|
||||||
const QByteArray &imageFormat,
|
const QByteArray &imageFormat,
|
||||||
const QImage &imageData) {
|
const QImage &imageData) {
|
||||||
_localLoading.kill();
|
_localLoading = nullptr;
|
||||||
if (result.data.isEmpty()) {
|
if (result.data.isEmpty()) {
|
||||||
_localStatus = LocalStatus::NotFound;
|
_localStatus = LocalStatus::NotFound;
|
||||||
start(true);
|
start(true);
|
||||||
|
@ -383,7 +383,7 @@ void FileLoader::loadLocal(const Storage::Cache::Key &key) {
|
||||||
format = std::move(format),
|
format = std::move(format),
|
||||||
guard = std::move(guard)
|
guard = std::move(guard)
|
||||||
]() mutable {
|
]() mutable {
|
||||||
if (!guard.alive()) {
|
if (!guard) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
localLoaded(
|
localLoaded(
|
||||||
|
@ -433,7 +433,7 @@ bool FileLoader::tryLoadLocal() {
|
||||||
return false;
|
return false;
|
||||||
} else if (_localStatus != LocalStatus::NotTried) {
|
} else if (_localStatus != LocalStatus::NotTried) {
|
||||||
return _finished;
|
return _finished;
|
||||||
} else if (_localLoading.alive()) {
|
} else if (_localLoading) {
|
||||||
_localStatus = LocalStatus::Loading;
|
_localStatus = LocalStatus::Loading;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4061,7 +4061,7 @@ bool readBackground() {
|
||||||
#ifndef OS_MAC_OLD
|
#ifndef OS_MAC_OLD
|
||||||
reader.setAutoTransform(true);
|
reader.setAutoTransform(true);
|
||||||
#endif // OS_MAC_OLD
|
#endif // OS_MAC_OLD
|
||||||
if (reader.read(&image)) {
|
if (reader.read(&image) || Window::Theme::GetWallPaperColor(slug)) {
|
||||||
_backgroundCanWrite = false;
|
_backgroundCanWrite = false;
|
||||||
Window::Theme::Background()->setImage({
|
Window::Theme::Background()->setImage({
|
||||||
id,
|
id,
|
||||||
|
|
|
@ -72,7 +72,7 @@ DatabasePointer Databases::get(
|
||||||
if (const auto i = _map.find(path); i != end(_map)) {
|
if (const auto i = _map.find(path); i != end(_map)) {
|
||||||
auto &kept = i->second;
|
auto &kept = i->second;
|
||||||
Assert(kept.destroying.alive());
|
Assert(kept.destroying.alive());
|
||||||
kept.destroying.kill();
|
kept.destroying = nullptr;
|
||||||
kept.database->reconfigure(settings);
|
kept.database->reconfigure(settings);
|
||||||
return DatabasePointer(this, kept.database);
|
return DatabasePointer(this, kept.database);
|
||||||
}
|
}
|
||||||
|
@ -93,7 +93,7 @@ void Databases::destroy(Cache::Database *database) {
|
||||||
database->close();
|
database->close();
|
||||||
database->waitForCleaner([=, guard = std::move(second)]() mutable {
|
database->waitForCleaner([=, guard = std::move(second)]() mutable {
|
||||||
crl::on_main([=, guard = std::move(guard)]{
|
crl::on_main([=, guard = std::move(guard)]{
|
||||||
if (!guard.alive()) {
|
if (!guard) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_map.erase(path);
|
_map.erase(path);
|
||||||
|
|
|
@ -278,7 +278,7 @@ AdminLog::OwnedItem GenerateCommentItem(
|
||||||
replyTo,
|
replyTo,
|
||||||
viaBotId,
|
viaBotId,
|
||||||
unixtime(),
|
unixtime(),
|
||||||
Auth().userId(),
|
history->session().userId(),
|
||||||
QString(),
|
QString(),
|
||||||
TextWithEntities{ TextUtilities::Clean(data.comment) });
|
TextWithEntities{ TextUtilities::Clean(data.comment) });
|
||||||
return AdminLog::OwnedItem(delegate, item);
|
return AdminLog::OwnedItem(delegate, item);
|
||||||
|
@ -296,7 +296,7 @@ AdminLog::OwnedItem GenerateContactItem(
|
||||||
const auto message = MTP_message(
|
const auto message = MTP_message(
|
||||||
MTP_flags(flags),
|
MTP_flags(flags),
|
||||||
MTP_int(id),
|
MTP_int(id),
|
||||||
MTP_int(Auth().userId()),
|
MTP_int(history->session().userId()),
|
||||||
peerToMTP(history->peer->id),
|
peerToMTP(history->peer->id),
|
||||||
MTPMessageFwdHeader(),
|
MTPMessageFwdHeader(),
|
||||||
MTP_int(viaBotId),
|
MTP_int(viaBotId),
|
||||||
|
|
|
@ -476,7 +476,7 @@ void Templates::reload() {
|
||||||
void Templates::load() {
|
void Templates::load() {
|
||||||
if (_reloadAfterRead) {
|
if (_reloadAfterRead) {
|
||||||
return;
|
return;
|
||||||
} else if (_reading.alive() || _updates) {
|
} else if (_reading || _updates) {
|
||||||
_reloadAfterRead = true;
|
_reloadAfterRead = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -491,7 +491,7 @@ void Templates::load() {
|
||||||
result = std::move(result),
|
result = std::move(result),
|
||||||
guard = std::move(guard)
|
guard = std::move(guard)
|
||||||
]() mutable {
|
]() mutable {
|
||||||
if (!guard.alive()) {
|
if (!guard) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setData(std::move(result.result));
|
setData(std::move(result.result));
|
||||||
|
|
|
@ -927,7 +927,7 @@ void Instance::checkUniversalImages() {
|
||||||
|
|
||||||
if (_id != Universal->id()) {
|
if (_id != Universal->id()) {
|
||||||
_id = Universal->id();
|
_id = Universal->id();
|
||||||
_generating.kill();
|
_generating = nullptr;
|
||||||
_sprites.clear();
|
_sprites.clear();
|
||||||
}
|
}
|
||||||
if (!Universal->ensureLoaded() && Universal->id() != 0) {
|
if (!Universal->ensureLoaded() && Universal->id() != 0) {
|
||||||
|
@ -952,7 +952,7 @@ void Instance::generateCache() {
|
||||||
image = universal->generate(size, index),
|
image = universal->generate(size, index),
|
||||||
guard = std::move(guard)
|
guard = std::move(guard)
|
||||||
]() mutable {
|
]() mutable {
|
||||||
if (!guard.alive() || universal != Universal) {
|
if (!guard || universal != Universal) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pushSprite(std::move(image));
|
pushSprite(std::move(image));
|
||||||
|
|
|
@ -750,6 +750,11 @@ QPixmap Image::pixBlurredColoredNoCache(
|
||||||
return App::pixmapFromImageInPlace(prepareColored(add, img));
|
return App::pixmapFromImageInPlace(prepareColored(add, img));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QImage Image::original() const {
|
||||||
|
checkSource();
|
||||||
|
return _data;
|
||||||
|
}
|
||||||
|
|
||||||
void Image::automaticLoad(Data::FileOrigin origin, const HistoryItem *item) {
|
void Image::automaticLoad(Data::FileOrigin origin, const HistoryItem *item) {
|
||||||
if (!loaded()) {
|
if (!loaded()) {
|
||||||
_source->automaticLoad(origin, item);
|
_source->automaticLoad(origin, item);
|
||||||
|
|
|
@ -102,6 +102,8 @@ public:
|
||||||
|
|
||||||
static ImagePtr Blank();
|
static ImagePtr Blank();
|
||||||
|
|
||||||
|
QImage original() const;
|
||||||
|
|
||||||
const QPixmap &pix(
|
const QPixmap &pix(
|
||||||
Data::FileOrigin origin,
|
Data::FileOrigin origin,
|
||||||
int32 w = 0,
|
int32 w = 0,
|
||||||
|
|
|
@ -411,7 +411,7 @@ void Widget::hideSlow() {
|
||||||
auto [left, right] = base::make_binary_guard();
|
auto [left, right] = base::make_binary_guard();
|
||||||
_hidingDelayed = std::move(left);
|
_hidingDelayed = std::move(left);
|
||||||
App::CallDelayed(st::notifySlowHide, this, [=, guard = std::move(right)] {
|
App::CallDelayed(st::notifySlowHide, this, [=, guard = std::move(right)] {
|
||||||
if (guard.alive() && _hiding) {
|
if (guard && _hiding) {
|
||||||
hideFast();
|
hideFast();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -78,6 +78,10 @@ void SectionWidget::PaintBackground(QWidget *widget, QPaintEvent *event) {
|
||||||
|
|
||||||
auto clip = event->rect();
|
auto clip = event->rect();
|
||||||
auto fill = QRect(0, 0, widget->width(), App::main()->height());
|
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 fromy = App::main()->backgroundFromY();
|
||||||
auto x = 0, y = 0;
|
auto x = 0, y = 0;
|
||||||
auto cached = App::main()->cachedBackground(fill, x, y);
|
auto cached = App::main()->cachedBackground(fill, x, y);
|
||||||
|
|
|
@ -24,7 +24,6 @@ constexpr auto kThemeFileSizeLimit = 5 * 1024 * 1024;
|
||||||
constexpr auto kThemeBackgroundSizeLimit = 4 * 1024 * 1024;
|
constexpr auto kThemeBackgroundSizeLimit = 4 * 1024 * 1024;
|
||||||
constexpr auto kBackgroundSizeLimit = 25 * 1024 * 1024;
|
constexpr auto kBackgroundSizeLimit = 25 * 1024 * 1024;
|
||||||
constexpr auto kThemeSchemeSizeLimit = 1024 * 1024;
|
constexpr auto kThemeSchemeSizeLimit = 1024 * 1024;
|
||||||
constexpr auto kMinimumTiledSize = 512;
|
|
||||||
constexpr auto kNightThemeFile = str_const(":/gui/night.tdesktop-theme");
|
constexpr auto kNightThemeFile = str_const(":/gui/night.tdesktop-theme");
|
||||||
|
|
||||||
struct Applying {
|
struct Applying {
|
||||||
|
@ -327,8 +326,9 @@ bool loadTheme(const QByteArray &content, Cached &cache, Instance *out = nullptr
|
||||||
}
|
}
|
||||||
|
|
||||||
QImage prepareBackgroundImage(QImage &&image) {
|
QImage prepareBackgroundImage(QImage &&image) {
|
||||||
if (image.format() != QImage::Format_ARGB32 && image.format() != QImage::Format_ARGB32_Premultiplied && image.format() != QImage::Format_RGB32) {
|
if (image.format() != QImage::Format_ARGB32_Premultiplied) {
|
||||||
image = std::move(image).convertToFormat(QImage::Format_RGB32);
|
image = std::move(image).convertToFormat(
|
||||||
|
QImage::Format_ARGB32_Premultiplied);
|
||||||
}
|
}
|
||||||
image.setDevicePixelRatio(cRetinaFactor());
|
image.setDevicePixelRatio(cRetinaFactor());
|
||||||
return std::move(image);
|
return std::move(image);
|
||||||
|
@ -391,9 +391,9 @@ void ChatBackground::setImage(
|
||||||
&& !nightMode()
|
&& !nightMode()
|
||||||
&& _themeAbsolutePath.isEmpty();
|
&& _themeAbsolutePath.isEmpty();
|
||||||
if (paper.id == kThemeBackground && _themeImage.isNull()) {
|
if (paper.id == kThemeBackground && _themeImage.isNull()) {
|
||||||
_paper = { kDefaultBackground };
|
setPaper({ kDefaultBackground });
|
||||||
} else {
|
} else {
|
||||||
_paper = paper;
|
setPaper(paper);
|
||||||
if (needResetAdjustable) {
|
if (needResetAdjustable) {
|
||||||
// If we had a default color theme with non-default background,
|
// If we had a default color theme with non-default background,
|
||||||
// and we switch to default background we must somehow switch from
|
// and we switch to default background we must somehow switch from
|
||||||
|
@ -410,18 +410,21 @@ void ChatBackground::setImage(
|
||||||
|| id() == details::kTestingEditorBackground) {
|
|| id() == details::kTestingEditorBackground) {
|
||||||
if (id() == details::kTestingDefaultBackground || image.isNull()) {
|
if (id() == details::kTestingDefaultBackground || image.isNull()) {
|
||||||
image.load(qsl(":/gui/art/bg.jpg"));
|
image.load(qsl(":/gui/art/bg.jpg"));
|
||||||
_paper = { details::kTestingDefaultBackground };
|
setPaper({ details::kTestingDefaultBackground });
|
||||||
}
|
}
|
||||||
setPreparedImage(std::move(image));
|
setPreparedImage(prepareBackgroundImage(std::move(image)));
|
||||||
} else {
|
} else {
|
||||||
if (id() == kInitialBackground) {
|
if (id() == kInitialBackground) {
|
||||||
image.load(qsl(":/gui/art/bg_initial.jpg"));
|
image.load(qsl(":/gui/art/bg_initial.jpg"));
|
||||||
const auto scale = cScale() * cIntRetinaFactor();
|
const auto scale = cScale() * cIntRetinaFactor();
|
||||||
if (scale != 100) {
|
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()) {
|
} else if (id() == kDefaultBackground
|
||||||
_paper = { kDefaultBackground };
|
|| (!color() && image.isNull())) {
|
||||||
|
setPaper({ kDefaultBackground });
|
||||||
image.load(qsl(":/gui/art/bg.jpg"));
|
image.load(qsl(":/gui/art/bg.jpg"));
|
||||||
}
|
}
|
||||||
Local::writeBackground(
|
Local::writeBackground(
|
||||||
|
@ -429,9 +432,16 @@ void ChatBackground::setImage(
|
||||||
((id() == kDefaultBackground || id() == kInitialBackground)
|
((id() == kDefaultBackground || id() == kInitialBackground)
|
||||||
? QImage()
|
? QImage()
|
||||||
: image));
|
: 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()));
|
notify(BackgroundUpdate(BackgroundUpdate::Type::New, tile()));
|
||||||
if (needResetAdjustable) {
|
if (needResetAdjustable) {
|
||||||
notify(BackgroundUpdate(BackgroundUpdate::Type::TestingTheme, tile()), true);
|
notify(BackgroundUpdate(BackgroundUpdate::Type::TestingTheme, tile()), true);
|
||||||
|
@ -440,33 +450,11 @@ void ChatBackground::setImage(
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatBackground::setPreparedImage(QImage &&image) {
|
void ChatBackground::setPreparedImage(QImage &&image) {
|
||||||
image = std::move(image).convertToFormat(QImage::Format_ARGB32_Premultiplied);
|
Expects(image.format() == QImage::Format_ARGB32_Premultiplied);
|
||||||
|
|
||||||
image.setDevicePixelRatio(cRetinaFactor());
|
image.setDevicePixelRatio(cRetinaFactor());
|
||||||
|
|
||||||
const auto adjustColors = [&] {
|
if (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();
|
|
||||||
}();
|
|
||||||
if (adjustColors) {
|
|
||||||
adjustPaletteUsingBackground(image);
|
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) {
|
void ChatBackground::adjustPaletteUsingBackground(const QImage &img) {
|
||||||
Assert(img.format() == QImage::Format_ARGB32_Premultiplied);
|
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]);
|
adjustPaletteUsingColor(
|
||||||
auto hue = bgColor.hslHueF();
|
QColor(components[0], components[1], components[2]));
|
||||||
auto saturation = bgColor.hslSaturationF();
|
}
|
||||||
|
|
||||||
|
void ChatBackground::adjustPaletteUsingColor(QColor color) {
|
||||||
|
auto hue = color.hslHueF();
|
||||||
|
auto saturation = color.hslSaturationF();
|
||||||
for (const auto &color : _adjustableColors) {
|
for (const auto &color : _adjustableColors) {
|
||||||
adjustColor(color.item, hue, saturation);
|
adjustColor(color.item, hue, saturation);
|
||||||
}
|
}
|
||||||
|
@ -534,6 +555,18 @@ WallPaperId ChatBackground::id() const {
|
||||||
return _paper.id;
|
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 {
|
bool ChatBackground::tile() const {
|
||||||
return nightMode() ? _tileNightValue : _tileDayValue;
|
return nightMode() ? _tileNightValue : _tileDayValue;
|
||||||
}
|
}
|
||||||
|
@ -683,21 +716,21 @@ void ChatBackground::setTestingDefaultTheme() {
|
||||||
void ChatBackground::keepApplied(const QString &path, bool write) {
|
void ChatBackground::keepApplied(const QString &path, bool write) {
|
||||||
setThemeAbsolutePath(path);
|
setThemeAbsolutePath(path);
|
||||||
if (id() == details::kTestingEditorBackground) {
|
if (id() == details::kTestingEditorBackground) {
|
||||||
_paper = { kCustomBackground };
|
setPaper({ kCustomBackground });
|
||||||
_themeImage = QImage();
|
_themeImage = QImage();
|
||||||
_themeTile = false;
|
_themeTile = false;
|
||||||
if (write) {
|
if (write) {
|
||||||
writeNewBackgroundSettings();
|
writeNewBackgroundSettings();
|
||||||
}
|
}
|
||||||
} else if (id() == details::kTestingThemeBackground) {
|
} else if (id() == details::kTestingThemeBackground) {
|
||||||
_paper = { kThemeBackground };
|
setPaper({ kThemeBackground });
|
||||||
_themeImage = _pixmap.toImage();
|
_themeImage = prepareBackgroundImage(_pixmap.toImage());
|
||||||
_themeTile = tile();
|
_themeTile = tile();
|
||||||
if (write) {
|
if (write) {
|
||||||
writeNewBackgroundSettings();
|
writeNewBackgroundSettings();
|
||||||
}
|
}
|
||||||
} else if (id() == details::kTestingDefaultBackground) {
|
} else if (id() == details::kTestingDefaultBackground) {
|
||||||
_paper = { kDefaultBackground };
|
setPaper({ kDefaultBackground });
|
||||||
_themeImage = QImage();
|
_themeImage = QImage();
|
||||||
_themeTile = false;
|
_themeTile = false;
|
||||||
if (write) {
|
if (write) {
|
||||||
|
@ -980,6 +1013,35 @@ bool IsPaletteTestingPath(const QString &path) {
|
||||||
return false;
|
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) {
|
void ComputeBackgroundRects(QRect wholeFill, QSize imageSize, QRect &to, QRect &from) {
|
||||||
if (uint64(imageSize.width()) * wholeFill.height() > uint64(imageSize.height()) * wholeFill.width()) {
|
if (uint64(imageSize.width()) * wholeFill.height() > uint64(imageSize.height()) * wholeFill.width()) {
|
||||||
float64 pxsize = wholeFill.height() / float64(imageSize.height());
|
float64 pxsize = wholeFill.height() / float64(imageSize.height());
|
||||||
|
|
|
@ -41,6 +41,8 @@ constexpr auto kCustomBackground = details::FromLegacyBackgroundId(-1);
|
||||||
constexpr auto kInitialBackground = details::FromLegacyBackgroundId(0);
|
constexpr auto kInitialBackground = details::FromLegacyBackgroundId(0);
|
||||||
constexpr auto kDefaultBackground = details::FromLegacyBackgroundId(105);
|
constexpr auto kDefaultBackground = details::FromLegacyBackgroundId(105);
|
||||||
|
|
||||||
|
constexpr auto kMinimumTiledSize = 512;
|
||||||
|
|
||||||
struct Cached {
|
struct Cached {
|
||||||
QByteArray colors;
|
QByteArray colors;
|
||||||
QByteArray background;
|
QByteArray background;
|
||||||
|
@ -78,16 +80,18 @@ void ApplyDefaultWithPath(const QString &themePath);
|
||||||
bool ApplyEditedPalette(const QString &path, const QByteArray &content);
|
bool ApplyEditedPalette(const QString &path, const QByteArray &content);
|
||||||
void KeepApplied();
|
void KeepApplied();
|
||||||
QString NightThemePath();
|
QString NightThemePath();
|
||||||
bool IsNightMode();
|
[[nodiscard]] bool IsNightMode();
|
||||||
void SetNightModeValue(bool nightMode);
|
void SetNightModeValue(bool nightMode);
|
||||||
void ToggleNightMode();
|
void ToggleNightMode();
|
||||||
void ToggleNightMode(const QString &themePath);
|
void ToggleNightMode(const QString &themePath);
|
||||||
bool IsNonDefaultBackground();
|
[[nodiscard]] bool IsNonDefaultBackground();
|
||||||
void Revert();
|
void Revert();
|
||||||
|
|
||||||
bool LoadFromFile(const QString &file, Instance *out, QByteArray *outContent);
|
bool LoadFromFile(const QString &file, Instance *out, QByteArray *outContent);
|
||||||
bool IsPaletteTestingPath(const QString &path);
|
bool IsPaletteTestingPath(const QString &path);
|
||||||
|
|
||||||
|
[[nodiscard]] std::optional<QColor> GetWallPaperColor(const QString &slug);
|
||||||
|
|
||||||
struct BackgroundUpdate {
|
struct BackgroundUpdate {
|
||||||
enum class Type {
|
enum class Type {
|
||||||
New,
|
New,
|
||||||
|
@ -121,7 +125,7 @@ public:
|
||||||
void setTileDayValue(bool tile);
|
void setTileDayValue(bool tile);
|
||||||
void setTileNightValue(bool tile);
|
void setTileNightValue(bool tile);
|
||||||
void setThemeAbsolutePath(const QString &path);
|
void setThemeAbsolutePath(const QString &path);
|
||||||
QString themeAbsolutePath() const;
|
[[nodiscard]] QString themeAbsolutePath() const;
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
void setTestingTheme(Instance &&theme);
|
void setTestingTheme(Instance &&theme);
|
||||||
|
@ -129,16 +133,20 @@ public:
|
||||||
void setTestingDefaultTheme();
|
void setTestingDefaultTheme();
|
||||||
void revert();
|
void revert();
|
||||||
|
|
||||||
WallPaperId id() const;
|
[[nodiscard]] WallPaperId id() const;
|
||||||
const QPixmap &pixmap() const {
|
[[nodiscard]] const QPixmap &pixmap() const {
|
||||||
return _pixmap;
|
return _pixmap;
|
||||||
}
|
}
|
||||||
const QPixmap &pixmapForTiled() const {
|
[[nodiscard]] const QPixmap &pixmapForTiled() const {
|
||||||
return _pixmapForTiled;
|
return _pixmapForTiled;
|
||||||
}
|
}
|
||||||
bool tile() const;
|
[[nodiscard]] std::optional<QColor> color() const {
|
||||||
bool tileDay() const;
|
return _paperColor;
|
||||||
bool tileNight() const;
|
}
|
||||||
|
[[nodiscard]] QImage createCurrentImage() const;
|
||||||
|
[[nodiscard]] bool tile() const;
|
||||||
|
[[nodiscard]] bool tileDay() const;
|
||||||
|
[[nodiscard]] bool tileNight() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct AdjustableColor {
|
struct AdjustableColor {
|
||||||
|
@ -152,16 +160,19 @@ private:
|
||||||
void saveForRevert();
|
void saveForRevert();
|
||||||
void setPreparedImage(QImage &&image);
|
void setPreparedImage(QImage &&image);
|
||||||
void writeNewBackgroundSettings();
|
void writeNewBackgroundSettings();
|
||||||
|
void setPaper(const Data::WallPaper &paper);
|
||||||
|
|
||||||
|
[[nodiscard]] bool adjustPaletteRequired();
|
||||||
void adjustPaletteUsingBackground(const QImage &img);
|
void adjustPaletteUsingBackground(const QImage &img);
|
||||||
|
void adjustPaletteUsingColor(QColor color);
|
||||||
void restoreAdjustableColors();
|
void restoreAdjustableColors();
|
||||||
|
|
||||||
void setNightModeValue(bool nightMode);
|
void setNightModeValue(bool nightMode);
|
||||||
bool nightMode() const;
|
[[nodiscard]] bool nightMode() const;
|
||||||
void toggleNightMode(std::optional<QString> themePath);
|
void toggleNightMode(std::optional<QString> themePath);
|
||||||
void keepApplied(const QString &path, bool write);
|
void keepApplied(const QString &path, bool write);
|
||||||
bool isNonDefaultThemeOrBackground();
|
[[nodiscard]] bool isNonDefaultThemeOrBackground();
|
||||||
bool isNonDefaultBackground();
|
[[nodiscard]] bool isNonDefaultBackground();
|
||||||
|
|
||||||
friend bool IsNightMode();
|
friend bool IsNightMode();
|
||||||
friend void SetNightModeValue(bool nightMode);
|
friend void SetNightModeValue(bool nightMode);
|
||||||
|
@ -171,6 +182,7 @@ private:
|
||||||
friend bool IsNonDefaultBackground();
|
friend bool IsNonDefaultBackground();
|
||||||
|
|
||||||
Data::WallPaper _paper = { details::kUninitializedBackground };
|
Data::WallPaper _paper = { details::kUninitializedBackground };
|
||||||
|
std::optional<QColor> _paperColor;
|
||||||
QPixmap _pixmap;
|
QPixmap _pixmap;
|
||||||
QPixmap _pixmapForTiled;
|
QPixmap _pixmapForTiled;
|
||||||
bool _nightMode = false;
|
bool _nightMode = false;
|
||||||
|
|
|
@ -319,8 +319,8 @@ void Editor::Inner::prepare() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Fn<void()> Editor::Inner::exportCallback() {
|
Fn<void()> Editor::Inner::exportCallback() {
|
||||||
return App::LambdaDelayed(st::defaultRippleAnimation.hideDuration, this, [this] {
|
return App::LambdaDelayed(st::defaultRippleAnimation.hideDuration, this, [=] {
|
||||||
auto background = Background()->pixmap().toImage();
|
auto background = Background()->createCurrentImage();
|
||||||
auto backgroundContent = QByteArray();
|
auto backgroundContent = QByteArray();
|
||||||
auto tiled = Background()->tile();
|
auto tiled = Background()->tile();
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue