Generate previews for cloud theme links.

This commit is contained in:
John Preston 2019-09-09 14:56:05 +03:00
parent 6d29dc3b36
commit ee5423762a
20 changed files with 193 additions and 80 deletions

View File

@ -1149,6 +1149,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_media_size_limit" = "Limit by size"; "lng_media_size_limit" = "Limit by size";
"lng_media_size_up_to" = "up to {size}"; "lng_media_size_up_to" = "up to {size}";
"lng_media_chat_background" = "Chat background"; "lng_media_chat_background" = "Chat background";
"lng_media_color_theme" = "Color theme";
"lng_emoji_category1" = "People"; "lng_emoji_category1" = "People";
"lng_emoji_category2" = "Nature"; "lng_emoji_category2" = "Nature";

View File

@ -650,6 +650,7 @@ void DocumentData::validateGoodThumbnail() {
if (!isVideoFile() if (!isVideoFile()
&& !isAnimation() && !isAnimation()
&& !isWallPaper() && !isWallPaper()
&& !isTheme()
&& (!sticker() || !sticker()->animated)) { && (!sticker() || !sticker()->animated)) {
_goodThumbnail = nullptr; _goodThumbnail = nullptr;
} else if (!_goodThumbnail && hasRemoteLocation()) { } else if (!_goodThumbnail && hasRemoteLocation()) {
@ -1470,7 +1471,8 @@ bool DocumentData::isGifv() const {
bool DocumentData::isTheme() const { bool DocumentData::isTheme() const {
return return
_filename.endsWith( _mimeString == qstr("application/x-tgtheme-tdesktop")
|| _filename.endsWith(
qstr(".tdesktop-theme"), qstr(".tdesktop-theme"),
Qt::CaseInsensitive) Qt::CaseInsensitive)
|| _filename.endsWith( || _filename.endsWith(

View File

@ -10,8 +10,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_document.h" #include "data/data_document.h"
#include "data/data_file_origin.h" #include "data/data_file_origin.h"
#include "data/data_cloud_themes.h"
#include "media/clip/media_clip_reader.h" #include "media/clip/media_clip_reader.h"
#include "lottie/lottie_animation.h" #include "lottie/lottie_animation.h"
#include "window/themes/window_theme_preview.h"
#include "main/main_session.h" #include "main/main_session.h"
#include <QtCore/QBuffer> #include <QtCore/QBuffer>
@ -27,6 +29,7 @@ enum class FileType {
Video, Video,
AnimatedSticker, AnimatedSticker,
WallPaper, WallPaper,
Theme,
}; };
QImage Prepare( QImage Prepare(
@ -37,6 +40,8 @@ QImage Prepare(
return ::Media::Clip::PrepareForSending(path, data).thumbnail; return ::Media::Clip::PrepareForSending(path, data).thumbnail;
} else if (type == FileType::AnimatedSticker) { } else if (type == FileType::AnimatedSticker) {
return Lottie::ReadThumbnail(Lottie::ReadContent(data, path)); return Lottie::ReadThumbnail(Lottie::ReadContent(data, path));
} else if (type == FileType::Theme) {
return Window::Theme::GeneratePreview(data, path);
} }
const auto validateSize = [](QSize size) { const auto validateSize = [](QSize size) {
return (size.width() + size.height()) < 10'000; return (size.width() + size.height()) < 10'000;
@ -78,6 +83,8 @@ void GoodThumbSource::generate(base::binary_guard &&guard) {
const auto data = _document->data(); const auto data = _document->data();
const auto type = _document->isWallPaper() const auto type = _document->isWallPaper()
? FileType::WallPaper ? FileType::WallPaper
: _document->isTheme()
? FileType::Theme
: _document->sticker() : _document->sticker()
? FileType::AnimatedSticker ? FileType::AnimatedSticker
: FileType::Video; : FileType::Video;
@ -143,13 +150,13 @@ void GoodThumbSource::ready(
_document->goodThumbnailCacheKey(), _document->goodThumbnailCacheKey(),
Storage::Cache::Database::TaggedValue{ Storage::Cache::Database::TaggedValue{
std::move(bytes), std::move(bytes),
Data::kImageCacheTag }); kImageCacheTag });
} }
Auth().downloaderTaskFinished().notify(); Auth().downloaderTaskFinished().notify();
}); });
} }
void GoodThumbSource::load(Data::FileOrigin origin) { void GoodThumbSource::load(FileOrigin origin) {
if (loading() || _empty) { if (loading() || _empty) {
return; return;
} }
@ -178,7 +185,7 @@ void GoodThumbSource::load(Data::FileOrigin origin) {
std::move(callback)); std::move(callback));
} }
void GoodThumbSource::loadEvenCancelled(Data::FileOrigin origin) { void GoodThumbSource::loadEvenCancelled(FileOrigin origin) {
_empty = false; _empty = false;
load(origin); load(origin);
} }
@ -193,7 +200,7 @@ void GoodThumbSource::unload() {
} }
void GoodThumbSource::automaticLoad( void GoodThumbSource::automaticLoad(
Data::FileOrigin origin, FileOrigin origin,
const HistoryItem *item) { const HistoryItem *item) {
} }
@ -235,7 +242,7 @@ void GoodThumbSource::setDelayedStorageLocation(
const StorageImageLocation &location) { const StorageImageLocation &location) {
} }
void GoodThumbSource::performDelayedLoad(Data::FileOrigin origin) { void GoodThumbSource::performDelayedLoad(FileOrigin origin) {
} }
bool GoodThumbSource::isDelayedStorageImage() const { bool GoodThumbSource::isDelayedStorageImage() const {

View File

@ -17,13 +17,13 @@ class GoodThumbSource : public Images::Source {
public: public:
explicit GoodThumbSource(not_null<DocumentData*> document); explicit GoodThumbSource(not_null<DocumentData*> document);
void load(Data::FileOrigin origin) override; void load(FileOrigin origin) override;
void loadEvenCancelled(Data::FileOrigin origin) override; void loadEvenCancelled(FileOrigin origin) override;
QImage takeLoaded() override; QImage takeLoaded() override;
void unload() override; void unload() override;
void automaticLoad( void automaticLoad(
Data::FileOrigin origin, FileOrigin origin,
const HistoryItem *item) override; const HistoryItem *item) override;
void automaticLoadSettingsChanged() override; void automaticLoadSettingsChanged() override;
@ -38,7 +38,7 @@ public:
std::optional<Storage::Cache::Key> cacheKey() override; std::optional<Storage::Cache::Key> cacheKey() override;
void setDelayedStorageLocation( void setDelayedStorageLocation(
const StorageImageLocation &location) override; const StorageImageLocation &location) override;
void performDelayedLoad(Data::FileOrigin origin) override; void performDelayedLoad(FileOrigin origin) override;
bool isDelayedStorageImage() const override; bool isDelayedStorageImage() const override;
void setImageBytes(const QByteArray &bytes) override; void setImageBytes(const QByteArray &bytes) override;

View File

@ -51,6 +51,9 @@ struct FileReferenceAccumulator {
if (const auto document = data.vdocument()) { if (const auto document = data.vdocument()) {
push(*document); push(*document);
} }
if (const auto documents = data.vdocuments()) {
push(*documents);
}
if (const auto photo = data.vphoto()) { if (const auto photo = data.vphoto()) {
push(*photo); push(*photo);
} }

View File

@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/media/history_view_call.h" #include "history/view/media/history_view_call.h"
#include "history/view/media/history_view_web_page.h" #include "history/view/media/history_view_web_page.h"
#include "history/view/media/history_view_poll.h" #include "history/view/media/history_view_poll.h"
#include "history/view/media/history_view_theme_document.h"
#include "ui/image/image.h" #include "ui/image/image.h"
#include "ui/image/image_source.h" #include "ui/image/image_source.h"
#include "ui/text_options.h" #include "ui/text_options.h"
@ -779,6 +780,10 @@ std::unique_ptr<HistoryView::Media> MediaFile::createView(
message, message,
realParent, realParent,
_document); _document);
} else if (_document->isTheme() && _document->hasThumbnail()) {
return std::make_unique<HistoryView::ThemeDocument>(
message,
_document);
} }
return std::make_unique<HistoryView::Document>(message, _document); return std::make_unique<HistoryView::Document>(message, _document);
} }

View File

@ -2762,6 +2762,17 @@ void Session::webpageApplyFields(
const auto pendingTill = TimeId(0); const auto pendingTill = TimeId(0);
const auto photo = data.vphoto(); const auto photo = data.vphoto();
const auto document = data.vdocument(); const auto document = data.vdocument();
const auto lookupThemeDocument = [&]() -> DocumentData* {
if (const auto documents = data.vdocuments()) {
for (const auto &document : documents->v) {
const auto processed = processDocument(document);
if (processed->isTheme()) {
return processed;
}
}
}
return nullptr;
};
webpageApplyFields( webpageApplyFields(
page, page,
ParseWebPageType(data), ParseWebPageType(data),
@ -2771,7 +2782,7 @@ void Session::webpageApplyFields(
qs(data.vtitle().value_or_empty()), qs(data.vtitle().value_or_empty()),
description, description,
photo ? processPhoto(*photo).get() : nullptr, photo ? processPhoto(*photo).get() : nullptr,
document ? processDocument(*document).get() : nullptr, document ? processDocument(*document).get() : lookupThemeDocument(),
WebPageCollage(data), WebPageCollage(data),
data.vduration().value_or_empty(), data.vduration().value_or_empty(),
qs(data.vauthor().value_or_empty()), qs(data.vauthor().value_or_empty()),

View File

@ -139,6 +139,8 @@ WebPageType ParseWebPageType(const MTPDwebPage &page) {
return WebPageType::Profile; return WebPageType::Profile;
} else if (type == qstr("telegram_background")) { } else if (type == qstr("telegram_background")) {
return WebPageType::WallPaper; return WebPageType::WallPaper;
} else if (type == qstr("telegram_theme")) {
return WebPageType::Theme;
} else if (page.vcached_page()) { } else if (page.vcached_page()) {
return WebPageType::ArticleWithIV; return WebPageType::ArticleWithIV;
} else { } else {

View File

@ -15,6 +15,7 @@ enum class WebPageType {
Video, Video,
Profile, Profile,
WallPaper, WallPaper,
Theme,
Article, Article,
ArticleWithIV, ArticleWithIV,
}; };

View File

@ -18,6 +18,7 @@ maxVideoMessageSize: 240px;
maxSignatureSize: 144px; maxSignatureSize: 144px;
maxWallPaperWidth: 160px; maxWallPaperWidth: 160px;
maxWallPaperHeight: 240px; maxWallPaperHeight: 240px;
historyThemeSize: size(309px, 200px);
historyMinimalWidth: 380px; historyMinimalWidth: 380px;

View File

@ -16,7 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/media/history_view_document.h" #include "history/view/media/history_view_document.h"
#include "history/view/media/history_view_sticker.h" #include "history/view/media/history_view_sticker.h"
#include "history/view/media/history_view_video.h" #include "history/view/media/history_view_video.h"
#include "history/view/media/history_view_wall_paper.h" #include "history/view/media/history_view_theme_document.h"
#include "styles/style_history.h" #include "styles/style_history.h"
namespace HistoryView { namespace HistoryView {
@ -80,8 +80,8 @@ std::unique_ptr<Media> CreateAttach(
parent, parent,
parent->data(), parent->data(),
document); document);
} else if (document->isWallPaper()) { } else if (document->isWallPaper() || document->isTheme()) {
return std::make_unique<WallPaper>( return std::make_unique<ThemeDocument>(
parent, parent,
document, document,
webpageUrl); webpageUrl);

View File

@ -5,7 +5,7 @@ the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link: For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#include "history/view/media/history_view_wall_paper.h" #include "history/view/media/history_view_theme_document.h"
#include "layout.h" #include "layout.h"
#include "history/history_item.h" #include "history/history_item.h"
@ -19,22 +19,24 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace HistoryView { namespace HistoryView {
WallPaper::WallPaper( ThemeDocument::ThemeDocument(
not_null<Element*> parent, not_null<Element*> parent,
not_null<DocumentData*> document, not_null<DocumentData*> document,
const QString &url) const QString &url)
: File(parent, parent->data()) : File(parent, parent->data())
, _data(document) { , _data(document) {
Expects(_data->hasThumbnail()); Expects(_data->hasThumbnail() || _data->isTheme());
fillPatternFieldsFrom(url); if (_data->isWallPaper()) {
fillPatternFieldsFrom(url);
}
_data->thumbnail()->load(parent->data()->fullId()); _data->loadThumbnail(_parent->data()->fullId());
setDocumentLinks(_data, parent->data()); setDocumentLinks(_data, parent->data());
setStatusSize(FileStatusSizeReady, _data->size, -1, 0); setStatusSize(FileStatusSizeReady, _data->size, -1, 0);
} }
void WallPaper::fillPatternFieldsFrom(const QString &url) { void ThemeDocument::fillPatternFieldsFrom(const QString &url) {
const auto paramsPosition = url.indexOf('?'); const auto paramsPosition = url.indexOf('?');
if (paramsPosition < 0) { if (paramsPosition < 0) {
return; return;
@ -49,7 +51,10 @@ void WallPaper::fillPatternFieldsFrom(const QString &url) {
_background = paper.backgroundColor().value_or(kDefaultBackground); _background = paper.backgroundColor().value_or(kDefaultBackground);
} }
QSize WallPaper::countOptimalSize() { QSize ThemeDocument::countOptimalSize() {
if (_data->isTheme()) {
return st::historyThemeSize;
}
auto tw = ConvertScale(_data->thumbnail()->width()); auto tw = ConvertScale(_data->thumbnail()->width());
auto th = ConvertScale(_data->thumbnail()->height()); auto th = ConvertScale(_data->thumbnail()->height());
if (!tw || !th) { if (!tw || !th) {
@ -66,7 +71,12 @@ QSize WallPaper::countOptimalSize() {
return { maxWidth, minHeight }; return { maxWidth, minHeight };
} }
QSize WallPaper::countCurrentSize(int newWidth) { QSize ThemeDocument::countCurrentSize(int newWidth) {
if (_data->isTheme()) {
_pixw = st::historyThemeSize.width();
_pixh = st::historyThemeSize.height();
return st::historyThemeSize;
}
auto tw = ConvertScale(_data->thumbnail()->width()); auto tw = ConvertScale(_data->thumbnail()->width());
auto th = ConvertScale(_data->thumbnail()->height()); auto th = ConvertScale(_data->thumbnail()->height());
if (!tw || !th) { if (!tw || !th) {
@ -86,7 +96,7 @@ QSize WallPaper::countCurrentSize(int newWidth) {
return { newWidth, newHeight }; return { newWidth, newHeight };
} }
void WallPaper::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms) const { void ThemeDocument::draw(Painter &p, const QRect &r, TextSelection selection, crl::time ms) const {
if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return; if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return;
_data->automaticLoad(_realParent->fullId(), _parent->data()); _data->automaticLoad(_realParent->fullId(), _parent->data());
@ -166,7 +176,7 @@ void WallPaper::draw(Painter &p, const QRect &r, TextSelection selection, crl::t
} }
} }
void WallPaper::validateThumbnail() const { void ThemeDocument::validateThumbnail() const {
if (_thumbnailGood > 0) { if (_thumbnailGood > 0) {
return; return;
} }
@ -179,7 +189,7 @@ void WallPaper::validateThumbnail() const {
good->load({}); good->load({});
} }
} }
if (_thumbnailGood >= 0) { if (_thumbnailGood >= 0 || !_data->thumbnail()) {
return; return;
} }
if (_data->thumbnail()->loaded()) { if (_data->thumbnail()->loaded()) {
@ -191,11 +201,12 @@ void WallPaper::validateThumbnail() const {
} }
} }
void WallPaper::prepareThumbnailFrom( void ThemeDocument::prepareThumbnailFrom(
not_null<Image*> image, not_null<Image*> image,
int good) const { int good) const {
Expects(_thumbnailGood <= good); Expects(_thumbnailGood <= good);
const auto isTheme = _data->isTheme();
const auto isPattern = _data->isPatternWallPaper(); const auto isPattern = _data->isPatternWallPaper();
auto options = Images::Option::Smooth auto options = Images::Option::Smooth
| (good >= 0 ? Images::Option(0) : Images::Option::Blurred) | (good >= 0 ? Images::Option(0) : Images::Option::Blurred)
@ -203,8 +214,8 @@ void WallPaper::prepareThumbnailFrom(
? Images::Option::TransparentBackground ? Images::Option::TransparentBackground
: Images::Option(0)); : Images::Option(0));
auto original = image->original(); auto original = image->original();
auto tw = ConvertScale(_data->thumbnail()->width()); auto tw = isTheme ? _pixw : ConvertScale(_data->thumbnail()->width());
auto th = ConvertScale(_data->thumbnail()->height()); auto th = isTheme ? _pixh : ConvertScale(_data->thumbnail()->height());
if (!tw || !th) { if (!tw || !th) {
tw = th = 1; tw = th = 1;
} }
@ -226,7 +237,7 @@ void WallPaper::prepareThumbnailFrom(
_thumbnailGood = good; _thumbnailGood = good;
} }
TextState WallPaper::textState(QPoint point, StateRequest request) const { TextState ThemeDocument::textState(QPoint point, StateRequest request) const {
auto result = TextState(_parent); auto result = TextState(_parent);
if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) { if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) {
@ -248,24 +259,24 @@ TextState WallPaper::textState(QPoint point, StateRequest request) const {
return result; return result;
} }
float64 WallPaper::dataProgress() const { float64 ThemeDocument::dataProgress() const {
return _data->progress(); return _data->progress();
} }
bool WallPaper::dataFinished() const { bool ThemeDocument::dataFinished() const {
return !_data->loading() return !_data->loading()
&& (!_data->uploading() || _data->waitingForAlbum()); && (!_data->uploading() || _data->waitingForAlbum());
} }
bool WallPaper::dataLoaded() const { bool ThemeDocument::dataLoaded() const {
return _data->loaded(); return _data->loaded();
} }
bool WallPaper::isReadyForOpen() const { bool ThemeDocument::isReadyForOpen() const {
return _data->loaded(); return _data->loaded();
} }
QString WallPaper::additionalInfoString() const { QString ThemeDocument::additionalInfoString() const {
// This will force message info (time) to be displayed below // This will force message info (time) to be displayed below
// this attachment in WebPage media. // this attachment in WebPage media.
static auto result = QString(" "); static auto result = QString(" ");

View File

@ -11,9 +11,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace HistoryView { namespace HistoryView {
class WallPaper : public File { class ThemeDocument : public File {
public: public:
WallPaper( ThemeDocument(
not_null<Element*> parent, not_null<Element*> parent,
not_null<DocumentData*> document, not_null<DocumentData*> document,
const QString &url = QString()); const QString &url = QString());
@ -59,6 +59,8 @@ private:
int _pixh = 1; int _pixh = 1;
mutable QPixmap _thumbnail; mutable QPixmap _thumbnail;
mutable int _thumbnailGood = -1; // -1 inline, 0 thumbnail, 1 good mutable int _thumbnailGood = -1; // -1 inline, 0 thumbnail, 1 good
// For wallpaper documents.
QColor _background; QColor _background;
int _intensity = 0; int _intensity = 0;

View File

@ -129,7 +129,9 @@ QSize WebPage::countOptimalSize() {
_openl = previewOfHiddenUrl _openl = previewOfHiddenUrl
? std::make_shared<HiddenUrlClickHandler>(_data->url) ? std::make_shared<HiddenUrlClickHandler>(_data->url)
: std::make_shared<UrlClickHandler>(_data->url, true); : std::make_shared<UrlClickHandler>(_data->url, true);
if (_data->document && _data->document->isWallPaper()) { if (_data->document
&& (_data->document->isWallPaper()
|| _data->document->isTheme())) {
_openl = std::make_shared<DocumentWrappedClickHandler>( _openl = std::make_shared<DocumentWrappedClickHandler>(
std::move(_openl), std::move(_openl),
_data->document, _data->document,
@ -652,7 +654,7 @@ ClickHandlerPtr WebPage::replaceAttachLink(
return link; return link;
} }
if (_data->document) { if (_data->document) {
if (_data->document->isWallPaper()) { if (_data->document->isWallPaper() || _data->document->isTheme()) {
return _openl; return _openl;
} }
} else if (_data->photo) { } else if (_data->photo) {
@ -674,7 +676,7 @@ TextSelection WebPage::adjustSelection(TextSelection selection, TextSelectType t
if ((!_titleLines && !_descriptionLines) || selection.to <= _siteName.length()) { if ((!_titleLines && !_descriptionLines) || selection.to <= _siteName.length()) {
return _siteName.adjustSelection(selection, type); return _siteName.adjustSelection(selection, type);
} }
auto titleSelection = _title.adjustSelection(toTitleSelection(selection), type); auto titleSelection = _title.adjustSelection(toTitleSelection(selection), type);
if ((!_siteNameLines && !_descriptionLines) || (selection.from >= _siteName.length() && selection.to <= _description.length())) { if ((!_siteNameLines && !_descriptionLines) || (selection.from >= _siteName.length() && selection.to <= _description.length())) {
return fromTitleSelection(titleSelection); return fromTitleSelection(titleSelection);
@ -708,7 +710,7 @@ void WebPage::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed)
bool WebPage::enforceBubbleWidth() const { bool WebPage::enforceBubbleWidth() const {
return (_attach != nullptr) return (_attach != nullptr)
&& (_data->document != nullptr) && (_data->document != nullptr)
&& _data->document->isWallPaper(); && (_data->document->isWallPaper() || _data->document->isTheme());
} }
void WebPage::playAnimation(bool autoplay) { void WebPage::playAnimation(bool autoplay) {
@ -779,6 +781,8 @@ int WebPage::bottomInfoPadding() const {
QString WebPage::displayedSiteName() const { QString WebPage::displayedSiteName() const {
return (_data->document && _data->document->isWallPaper()) return (_data->document && _data->document->isWallPaper())
? tr::lng_media_chat_background(tr::now) ? tr::lng_media_chat_background(tr::now)
: (_data->document && _data->document->isWallPaper())
? tr::lng_media_color_theme(tr::now)
: _data->siteName; : _data->siteName;
} }

View File

@ -2251,7 +2251,12 @@ void OverlayWidget::initThemePreview() {
const auto id = _themePreviewId = rand_value<uint64>(); const auto id = _themePreviewId = rand_value<uint64>();
const auto weak = make_weak(this); const auto weak = make_weak(this);
crl::async([=, data = std::move(current)]() mutable { crl::async([=, data = std::move(current)]() mutable {
auto preview = GeneratePreview(bytes, path, fields, std::move(data)); auto preview = GeneratePreview(
bytes,
path,
fields,
std::move(data),
Window::Theme::PreviewType::Extended);
crl::on_main(weak, [=, result = std::move(preview)]() mutable { crl::on_main(weak, [=, result = std::move(preview)]() mutable {
if (id != _themePreviewId) { if (id != _themePreviewId) {
return; return;

View File

@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "storage/file_download.h" #include "storage/file_download.h"
#include "storage/storage_media_prepare.h" #include "storage/storage_media_prepare.h"
#include "window/themes/window_theme_preview.h"
#include "mainwidget.h" #include "mainwidget.h"
#include "mainwindow.h" #include "mainwindow.h"
#include "main/main_session.h" #include "main/main_session.h"
@ -78,13 +79,6 @@ PreparedFileThumbnail PrepareFileThumbnail(QImage &&original) {
return result; return result;
} }
PreparedFileThumbnail PrepareAnimatedStickerThumbnail(
const QString &file,
const QByteArray &bytes) {
return PrepareFileThumbnail(
Lottie::ReadThumbnail(Lottie::ReadContent(bytes, file)));
}
bool FileThumbnailUploadRequired(const QString &filemime, int32 filesize) { bool FileThumbnailUploadRequired(const QString &filemime, int32 filesize) {
constexpr auto kThumbnailUploadBySize = 5 * 1024 * 1024; constexpr auto kThumbnailUploadBySize = 5 * 1024 * 1024;
const auto kThumbnailKnownMimes = { const auto kThumbnailKnownMimes = {
@ -818,6 +812,15 @@ void FileLoadTask::process() {
} }
thumbnail = PrepareFileThumbnail(std::move(video->thumbnail)); thumbnail = PrepareFileThumbnail(std::move(video->thumbnail));
} else if (filemime == qstr("application/x-tdesktop-theme")
|| filemime == qstr("application/x-tgtheme-tdesktop")) {
goodThumbnail = Window::Theme::GeneratePreview(_content, _filepath);
if (!goodThumbnail.isNull()) {
QBuffer buffer(&goodThumbnailBytes);
goodThumbnail.save(&buffer, "JPG", kThumbnailQuality);
thumbnail = PrepareFileThumbnail(base::duplicate(goodThumbnail));
}
} }
} }

View File

@ -465,15 +465,26 @@ SendMediaReady PrepareThemeMedia(
PreparedPhotoThumbs thumbnails; PreparedPhotoThumbs thumbnails;
QVector<MTPPhotoSize> sizes; QVector<MTPPhotoSize> sizes;
//const auto push = [&](const char *type, QImage &&image) { auto thumbnail = GeneratePreview(content, QString()).scaled(
// sizes.push_back(MTP_photoSize( 320,
// MTP_string(type), 320,
// MTP_fileLocationToBeDeprecated(MTP_long(0), MTP_int(0)), Qt::KeepAspectRatio,
// MTP_int(image.width()), Qt::SmoothTransformation);
// MTP_int(image.height()), MTP_int(0))); auto thumbnailBytes = QByteArray();
// thumbnails.emplace(type[0], std::move(image)); {
//}; QBuffer buffer(&thumbnailBytes);
//push("s", scaled(320)); thumbnail.save(&buffer, "JPG", 87);
}
const auto push = [&](const char *type, QImage &&image) {
sizes.push_back(MTP_photoSize(
MTP_string(type),
MTP_fileLocationToBeDeprecated(MTP_long(0), MTP_int(0)),
MTP_int(image.width()),
MTP_int(image.height()), MTP_int(0)));
thumbnails.emplace(type[0], std::move(image));
};
push("s", std::move(thumbnail));
const auto filename = File::NameFromUserString(name) const auto filename = File::NameFromUserString(name)
+ qsl(".tdesktop-theme"); + qsl(".tdesktop-theme");
@ -506,7 +517,7 @@ SendMediaReady PrepareThemeMedia(
MTP_photoEmpty(MTP_long(0)), MTP_photoEmpty(MTP_long(0)),
thumbnails, thumbnails,
document, document,
QByteArray(), thumbnailBytes,
0); 0);
} }
@ -520,7 +531,7 @@ Fn<void()> SavePreparedTheme(
Fn<void(SaveErrorType,QString)> fail) { Fn<void(SaveErrorType,QString)> fail) {
Expects(window->account().sessionExists()); Expects(window->account().sessionExists());
using Storage::UploadedDocument; using Storage::UploadedThumbDocument;
struct State { struct State {
FullMsgId id; FullMsgId id;
bool generating = false; bool generating = false;
@ -539,6 +550,7 @@ Fn<void()> SavePreparedTheme(
const auto creating = !fields.id const auto creating = !fields.id
|| (fields.createdBy != session->userId()); || (fields.createdBy != session->userId());
const auto oldDocumentId = creating ? 0 : fields.documentId;
const auto changed = (parsed.background != originalParsed.background) const auto changed = (parsed.background != originalParsed.background)
|| (parsed.tiled != originalParsed.tiled) || (parsed.tiled != originalParsed.tiled)
|| PaletteChanged(parsed.palette, originalParsed.palette, fields); || PaletteChanged(parsed.palette, originalParsed.palette, fields);
@ -602,11 +614,11 @@ Fn<void()> SavePreparedTheme(
}).send(); }).send();
}; };
const auto uploadTheme = [=](const UploadedDocument &data) { const auto uploadTheme = [=](const UploadedThumbDocument &data) {
state->requestId = api->request(MTPaccount_UploadTheme( state->requestId = api->request(MTPaccount_UploadTheme(
MTP_flags(0), MTP_flags(MTPaccount_UploadTheme::Flag::f_thumb),
data.file, data.file,
MTPInputFile(), // thumb data.thumb,
MTP_string(state->filename), MTP_string(state->filename),
MTP_string("application/x-tgtheme-tdesktop") MTP_string("application/x-tgtheme-tdesktop")
)).done([=](const MTPDocument &result) { )).done([=](const MTPDocument &result) {
@ -625,10 +637,10 @@ Fn<void()> SavePreparedTheme(
state->filename = media.filename; state->filename = media.filename;
state->themeContent = theme; state->themeContent = theme;
session->uploader().documentReady( session->uploader().thumbDocumentReady(
) | rpl::filter([=](const UploadedDocument &data) { ) | rpl::filter([=](const UploadedThumbDocument &data) {
return data.fullId == state->id; return data.fullId == state->id;
}) | rpl::start_with_next([=](const UploadedDocument &data) { }) | rpl::start_with_next([=](const UploadedThumbDocument &data) {
uploadTheme(data); uploadTheme(data);
}, state->lifetime); }, state->lifetime);

View File

@ -83,9 +83,12 @@ QString fillLetters(const QString &name) {
class Generator { class Generator {
public: public:
Generator(const Instance &theme, CurrentData &&current); Generator(
const Instance &theme,
CurrentData &&current,
PreviewType type);
QImage generate(); [[nodiscard]] QImage generate();
private: private:
enum class Status { enum class Status {
@ -131,6 +134,7 @@ private:
Ui::Text::String replyText = { st::msgMinWidth }; Ui::Text::String replyText = { st::msgMinWidth };
}; };
[[nodiscard]] bool extended() const;
void prepare(); void prepare();
void addRow(QString name, int peerIndex, QString date, QString text); void addRow(QString name, int peerIndex, QString date, QString text);
@ -162,7 +166,8 @@ private:
const Instance &_theme; const Instance &_theme;
const style::palette &_palette; const style::palette &_palette;
CurrentData _current; const CurrentData _current;
const PreviewType _type;
Painter *_p = nullptr; Painter *_p = nullptr;
QRect _rect; QRect _rect;
@ -188,10 +193,19 @@ private:
}; };
bool Generator::extended() const {
return (_type == PreviewType::Extended);
}
void Generator::prepare() { void Generator::prepare() {
_rect = QRect(0, 0, st::themePreviewMargin.left() + st::themePreviewSize.width() + st::themePreviewMargin.right(), st::themePreviewMargin.top() + st::themePreviewSize.height() + st::themePreviewMargin.bottom()); const auto size = extended()
_inner = _rect.marginsRemoved(st::themePreviewMargin); ? QRect(
_body = _inner.marginsRemoved(QMargins(0, Platform::PreviewTitleHeight(), 0, 0)); QPoint(),
st::themePreviewSize).marginsAdded(st::themePreviewMargin).size()
: st::themePreviewSize;
_rect = QRect(QPoint(), size);
_inner = extended() ? _rect.marginsRemoved(st::themePreviewMargin) : _rect;
_body = extended() ? _inner.marginsRemoved(QMargins(0, Platform::PreviewTitleHeight(), 0, 0)) : _inner;
_dialogs = QRect(_body.x(), _body.y(), st::themePreviewDialogsWidth, _body.height()); _dialogs = QRect(_body.x(), _body.y(), st::themePreviewDialogsWidth, _body.height());
_dialogsList = _dialogs.marginsRemoved(QMargins(0, st::dialogsFilterPadding.y() + st::dialogsMenuToggle.height + st::dialogsFilterPadding.y(), 0, st::dialogsPadding.y())); _dialogsList = _dialogs.marginsRemoved(QMargins(0, st::dialogsFilterPadding.y() + st::dialogsMenuToggle.height + st::dialogsFilterPadding.y(), 0, st::dialogsPadding.y()));
_topBar = QRect(_dialogs.x() + _dialogs.width(), _dialogs.y(), _body.width() - _dialogs.width(), st::topBarHeight); _topBar = QRect(_dialogs.x() + _dialogs.width(), _dialogs.y(), _body.width() - _dialogs.width(), st::topBarHeight);
@ -339,10 +353,14 @@ void Generator::generateData() {
_bubbles.back().replyText.setText(st::messageTextStyle, "Mark Twain said that " + QString() + QChar(9757) + QChar(55356) + QChar(57339), Ui::DialogTextOptions()); _bubbles.back().replyText.setText(st::messageTextStyle, "Mark Twain said that " + QString() + QChar(9757) + QChar(55356) + QChar(57339), Ui::DialogTextOptions());
} }
Generator::Generator(const Instance &theme, CurrentData &&current) Generator::Generator(
const Instance &theme,
CurrentData &&current,
PreviewType type)
: _theme(theme) : _theme(theme)
, _palette(_theme.palette) , _palette(_theme.palette)
, _current(std::move(current)) { , _current(std::move(current))
, _type(type) {
} }
QImage Generator::generate() { QImage Generator::generate() {
@ -368,7 +386,9 @@ QImage Generator::generate() {
paintDialogs(); paintDialogs();
paintHistoryShadows(); paintHistoryShadows();
} }
Platform::PreviewWindowFramePaint(result, _palette, _body, _rect.width()); if (extended()) {
Platform::PreviewWindowFramePaint(result, _palette, _body, _rect.width());
}
return result; return result;
} }
@ -944,18 +964,32 @@ std::unique_ptr<Preview> GeneratePreview(
const QByteArray &bytes, const QByteArray &bytes,
const QString &filepath, const QString &filepath,
const Data::CloudTheme &cloud, const Data::CloudTheme &cloud,
CurrentData &&data) { CurrentData &&data,
PreviewType type) {
auto result = PreviewFromFile(bytes, filepath, cloud); auto result = PreviewFromFile(bytes, filepath, cloud);
if (!result) { if (!result) {
return nullptr; return nullptr;
} }
result->preview = Generator( result->preview = Generator(
result->instance, result->instance,
std::move(data) std::move(data),
type
).generate(); ).generate();
return result; return result;
} }
QImage GeneratePreview(
const QByteArray &bytes,
const QString &filepath) {
const auto preview = GeneratePreview(
bytes,
filepath,
Data::CloudTheme(),
CurrentData{ Data::ThemeWallPaper().id() },
PreviewType::Normal);
return preview ? preview->preview : QImage();
}
int DefaultPreviewTitleHeight() { int DefaultPreviewTitleHeight() {
return st::titleHeight; return st::titleHeight;
} }

View File

@ -17,11 +17,16 @@ namespace Window {
namespace Theme { namespace Theme {
struct CurrentData { struct CurrentData {
int32 backgroundId = 0; WallPaperId backgroundId = 0;
QImage backgroundImage; QImage backgroundImage;
bool backgroundTiled = false; bool backgroundTiled = false;
}; };
enum class PreviewType {
Normal,
Extended,
};
[[nodiscard]] QString CachedThemePath(uint64 documentId); [[nodiscard]] QString CachedThemePath(uint64 documentId);
std::unique_ptr<Preview> PreviewFromFile( std::unique_ptr<Preview> PreviewFromFile(
@ -32,7 +37,11 @@ std::unique_ptr<Preview> GeneratePreview(
const QByteArray &bytes, const QByteArray &bytes,
const QString &filepath, const QString &filepath,
const Data::CloudTheme &cloud, const Data::CloudTheme &cloud,
CurrentData &&data); CurrentData &&data,
PreviewType type);
QImage GeneratePreview(
const QByteArray &bytes,
const QString &filepath);
int DefaultPreviewTitleHeight(); int DefaultPreviewTitleHeight();
void DefaultPreviewWindowFramePaint( void DefaultPreviewWindowFramePaint(

View File

@ -316,10 +316,10 @@
<(src_loc)/history/view/media/history_view_poll.cpp <(src_loc)/history/view/media/history_view_poll.cpp
<(src_loc)/history/view/media/history_view_sticker.h <(src_loc)/history/view/media/history_view_sticker.h
<(src_loc)/history/view/media/history_view_sticker.cpp <(src_loc)/history/view/media/history_view_sticker.cpp
<(src_loc)/history/view/media/history_view_theme_document.h
<(src_loc)/history/view/media/history_view_theme_document.cpp
<(src_loc)/history/view/media/history_view_video.h <(src_loc)/history/view/media/history_view_video.h
<(src_loc)/history/view/media/history_view_video.cpp <(src_loc)/history/view/media/history_view_video.cpp
<(src_loc)/history/view/media/history_view_wall_paper.h
<(src_loc)/history/view/media/history_view_wall_paper.cpp
<(src_loc)/history/view/media/history_view_web_page.h <(src_loc)/history/view/media/history_view_web_page.h
<(src_loc)/history/view/media/history_view_web_page.cpp <(src_loc)/history/view/media/history_view_web_page.cpp
<(src_loc)/history/view/history_view_compose_controls.cpp <(src_loc)/history/view/history_view_compose_controls.cpp