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