Show color / gradient wallpapers in WebPage previews.

This commit is contained in:
John Preston 2021-08-13 18:11:28 +03:00
parent 436d7b9d82
commit 1bc5277d51
6 changed files with 171 additions and 83 deletions

View File

@ -127,11 +127,12 @@ constexpr auto kVersion = 1;
if (!count || count > kMaxColors || view.size() != count * 7 - 1) {
return {};
}
const auto separator = QChar(count > 2 ? '~' : '-');
auto result = std::vector<QColor>();
result.reserve(count);
for (auto i = 0; i != count; ++i) {
if (i + 1 < count && view[i * 7 + 6] != separator) {
if (i + 1 < count
&& view[i * 7 + 6] != '~'
&& (count > 2 || view[i * 7 + 6] != '-')) {
return {};
} else if (const auto parsed = ColorFromString(view.mid(i * 7, 6))) {
result.push_back(*parsed);
@ -268,6 +269,9 @@ QString WallPaper::shareUrl(not_null<Main::Session*> session) const {
params.push_back("intensity=" + QString::number(_intensity));
}
}
if (_rotation && backgroundColors().size() == 2) {
params.push_back("rotation=" + QString::number(_rotation));
}
auto mode = QStringList();
if (_blurred) {
mode.push_back("blur");
@ -355,6 +359,12 @@ WallPaper WallPaper::withUrlParams(
if (result._backgroundColors.empty()) {
result._backgroundColors = ColorsFromString(params.value("bg_color"));
}
if (result._backgroundColors.empty()) {
result._backgroundColors = ColorsFromString(params.value("gradient"));
}
if (result._backgroundColors.empty()) {
result._backgroundColors = ColorsFromString(params.value("color"));
}
if (const auto string = params.value("intensity"); !string.isEmpty()) {
auto ok = false;
const auto intensity = string.toInt(&ok);

View File

@ -58,9 +58,15 @@ void File::setLinks(
void File::refreshParentId(not_null<HistoryItem*> realParent) {
const auto contextId = realParent->fullId();
_openl->setMessageId(contextId);
_savel->setMessageId(contextId);
_cancell->setMessageId(contextId);
if (_openl) {
_openl->setMessageId(contextId);
}
if (_savel) {
_savel->setMessageId(contextId);
}
if (_cancell) {
_cancell->setMessageId(contextId);
}
}
void File::setStatusSize(int newSize, int fullSize, int duration, qint64 realDuration) const {

View File

@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/text/format_values.h"
#include "data/data_document.h"
#include "data/data_wall_paper.h"
#include "history/view/history_view_element.h"
#include "history/view/media/history_view_media_grouped.h"
#include "history/view/media/history_view_photo.h"
@ -78,7 +79,7 @@ std::unique_ptr<Media> CreateAttach(
return std::make_unique<ThemeDocument>(
parent,
document,
webpageUrl);
ThemeDocument::ParamsFromUrl(webpageUrl));
}
return std::make_unique<Document>(parent, parent->data(), document);
} else if (photo) {
@ -86,6 +87,8 @@ std::unique_ptr<Media> CreateAttach(
parent,
parent->data(),
photo);
} else if (const auto params = ThemeDocument::ParamsFromUrl(webpageUrl)) {
return std::make_unique<ThemeDocument>(parent, nullptr, params);
}
return nullptr;
}

View File

@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_file_origin.h"
#include "data/data_wall_paper.h"
#include "base/qthelp_url.h"
#include "core/local_url_handlers.h"
#include "ui/text/format_values.h"
#include "ui/cached_round_corners.h"
#include "ui/ui_utility.h"
@ -28,19 +29,48 @@ namespace HistoryView {
ThemeDocument::ThemeDocument(
not_null<Element*> parent,
not_null<DocumentData*> document,
const QString &url)
DocumentData *document)
: ThemeDocument(parent, document, std::nullopt) {
}
ThemeDocument::ThemeDocument(
not_null<Element*> parent,
DocumentData *document,
const std::optional<Data::WallPaper> &params)
: File(parent, parent->data())
, _data(document) {
Expects(_data->hasThumbnail() || _data->isTheme());
Expects(params.has_value() || _data->hasThumbnail() || _data->isTheme());
if (_data->isWallPaper()) {
fillPatternFieldsFrom(url);
if (params) {
_background = params->backgroundColors();
_patternOpacity = params->patternOpacity();
_gradientRotation = params->gradientRotation();
}
const auto fullId = _parent->data()->fullId();
if (_data) {
_data->loadThumbnail(fullId);
setDocumentLinks(_data, parent->data());
setStatusSize(Ui::FileStatusSizeReady, _data->size, -1, 0);
} else {
class EmptyFileClickHandler final : public FileClickHandler {
public:
using FileClickHandler::FileClickHandler;
_data->loadThumbnail(_parent->data()->fullId());
setDocumentLinks(_data, parent->data());
setStatusSize(Ui::FileStatusSizeReady, _data->size, -1, 0);
private:
void onClickImpl() const override {
}
};
// We could open BackgroundPreviewBox here, but right now
// WebPage that created ThemeDocument as its attachment does it.
//
// So just provide a non-null click handler for this hack to work.
setLinks(
std::make_shared<EmptyFileClickHandler>(fullId),
nullptr,
nullptr);
}
}
ThemeDocument::~ThemeDocument() {
@ -50,23 +80,27 @@ ThemeDocument::~ThemeDocument() {
}
}
void ThemeDocument::fillPatternFieldsFrom(const QString &url) {
const auto paramsPosition = url.indexOf('?');
std::optional<Data::WallPaper> ThemeDocument::ParamsFromUrl(
const QString &url) {
const auto local = Core::TryConvertUrlToLocal(url);
const auto paramsPosition = local.indexOf('?');
if (paramsPosition < 0) {
return;
return std::nullopt;
}
const auto paramsString = url.mid(paramsPosition + 1);
const auto paramsString = local.mid(paramsPosition + 1);
const auto params = qthelp::url_parse_params(
paramsString,
qthelp::UrlParamNameTransform::ToLower);
const auto paper = Data::DefaultWallPaper().withUrlParams(params);
_background = paper.backgroundColors();
_patternOpacity = paper.patternOpacity();
_gradientRotation = paper.gradientRotation();
auto paper = Data::DefaultWallPaper().withUrlParams(params);
return paper.backgroundColors().empty()
? std::nullopt
: std::make_optional(std::move(paper));
}
QSize ThemeDocument::countOptimalSize() {
if (_data->isTheme()) {
if (!_data) {
return { st::maxWallPaperWidth, st::maxWallPaperHeight };
} else if (_data->isTheme()) {
return st::historyThemeSize;
}
const auto &location = _data->thumbnailLocation();
@ -87,7 +121,11 @@ QSize ThemeDocument::countOptimalSize() {
}
QSize ThemeDocument::countCurrentSize(int newWidth) {
if (_data->isTheme()) {
if (!_data) {
_pixw = st::maxWallPaperWidth;
_pixh = st::maxWallPaperHeight;
return { _pixw, _pixh };
} else if (_data->isTheme()) {
_pixw = st::historyThemeSize.width();
_pixh = st::historyThemeSize.height();
return st::historyThemeSize;
@ -117,10 +155,12 @@ void ThemeDocument::draw(Painter &p, const QRect &r, TextSelection selection, cr
ensureDataMediaCreated();
_dataMedia->automaticLoad(_realParent->fullId(), _parent->data());
if (_data) {
_dataMedia->automaticLoad(_realParent->fullId(), _parent->data());
}
auto selected = (selection == FullSelection);
auto loaded = dataLoaded();
auto displayLoading = _data->displayLoading();
auto displayLoading = _data && _data->displayLoading();
auto paintx = 0, painty = 0, paintw = width(), painth = height();
@ -141,59 +181,60 @@ void ThemeDocument::draw(Painter &p, const QRect &r, TextSelection selection, cr
Ui::FillComplexOverlayRect(p, rthumb, roundRadius, roundCorners);
}
auto statusX = paintx + st::msgDateImgDelta + st::msgDateImgPadding.x();
auto statusY = painty + st::msgDateImgDelta + st::msgDateImgPadding.y();
auto statusW = st::normalFont->width(_statusText) + 2 * st::msgDateImgPadding.x();
auto statusH = st::normalFont->height + 2 * st::msgDateImgPadding.y();
Ui::FillRoundRect(p, style::rtlrect(statusX - st::msgDateImgPadding.x(), statusY - st::msgDateImgPadding.y(), statusW, statusH, width()), selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? Ui::DateSelectedCorners : Ui::DateCorners);
p.setFont(st::normalFont);
p.setPen(st::msgDateImgFg);
p.drawTextLeft(statusX, statusY, width(), _statusText, statusW - 2 * st::msgDateImgPadding.x());
if (radial || (!loaded && !_data->loading())) {
const auto radialOpacity = (radial && loaded && !_data->uploading())
? _animation->radial.opacity() :
1.;
const auto innerSize = st::msgFileLayout.thumbSize;
QRect inner(rthumb.x() + (rthumb.width() - innerSize) / 2, rthumb.y() + (rthumb.height() - innerSize) / 2, innerSize, innerSize);
p.setPen(Qt::NoPen);
if (selected) {
p.setBrush(st::msgDateImgBgSelected);
} else if (isThumbAnimation()) {
auto over = _animation->a_thumbOver.value(1.);
p.setBrush(anim::brush(st::msgDateImgBg, st::msgDateImgBgOver, over));
} else {
auto over = ClickHandler::showAsActive(_data->loading() ? _cancell : _openl);
p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg);
}
p.setOpacity(radialOpacity * p.opacity());
{
PainterHighQualityEnabler hq(p);
p.drawEllipse(inner);
}
p.setOpacity(radialOpacity);
auto icon = ([radial, this, selected]() -> const style::icon* {
if (radial || _data->loading()) {
return &(selected ? st::historyFileThumbCancelSelected : st::historyFileThumbCancel);
if (_data) {
auto statusX = paintx + st::msgDateImgDelta + st::msgDateImgPadding.x();
auto statusY = painty + st::msgDateImgDelta + st::msgDateImgPadding.y();
auto statusW = st::normalFont->width(_statusText) + 2 * st::msgDateImgPadding.x();
auto statusH = st::normalFont->height + 2 * st::msgDateImgPadding.y();
Ui::FillRoundRect(p, style::rtlrect(statusX - st::msgDateImgPadding.x(), statusY - st::msgDateImgPadding.y(), statusW, statusH, width()), selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? Ui::DateSelectedCorners : Ui::DateCorners);
p.setFont(st::normalFont);
p.setPen(st::msgDateImgFg);
p.drawTextLeft(statusX, statusY, width(), _statusText, statusW - 2 * st::msgDateImgPadding.x());
if (radial || (!loaded && !_data->loading())) {
const auto radialOpacity = (radial && loaded && !_data->uploading())
? _animation->radial.opacity() :
1.;
const auto innerSize = st::msgFileLayout.thumbSize;
QRect inner(rthumb.x() + (rthumb.width() - innerSize) / 2, rthumb.y() + (rthumb.height() - innerSize) / 2, innerSize, innerSize);
p.setPen(Qt::NoPen);
if (selected) {
p.setBrush(st::msgDateImgBgSelected);
} else if (isThumbAnimation()) {
auto over = _animation->a_thumbOver.value(1.);
p.setBrush(anim::brush(st::msgDateImgBg, st::msgDateImgBgOver, over));
} else {
auto over = ClickHandler::showAsActive(_data->loading() ? _cancell : _openl);
p.setBrush(over ? st::msgDateImgBgOver : st::msgDateImgBg);
}
p.setOpacity(radialOpacity * p.opacity());
{
PainterHighQualityEnabler hq(p);
p.drawEllipse(inner);
}
p.setOpacity(radialOpacity);
auto icon = ([radial, this, selected]() -> const style::icon* {
if (radial || _data->loading()) {
return &(selected ? st::historyFileThumbCancelSelected : st::historyFileThumbCancel);
}
return &(selected ? st::historyFileThumbDownloadSelected : st::historyFileThumbDownload);
})();
if (icon) {
icon->paintInCenter(p, inner);
}
p.setOpacity(1);
if (radial) {
QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine)));
_animation->radial.draw(p, rinner, st::msgFileRadialLine, selected ? st::historyFileThumbRadialFgSelected : st::historyFileThumbRadialFg);
}
return &(selected ? st::historyFileThumbDownloadSelected : st::historyFileThumbDownload);
})();
if (icon) {
icon->paintInCenter(p, inner);
}
p.setOpacity(1);
if (radial) {
QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine)));
_animation->radial.draw(p, rinner, st::msgFileRadialLine, selected ? st::historyFileThumbRadialFgSelected : st::historyFileThumbRadialFg);
}
}
}
void ThemeDocument::ensureDataMediaCreated() const {
if (_dataMedia) {
if (_dataMedia || !_data) {
return;
}
_dataMedia = _data->createMediaView();
@ -205,7 +246,7 @@ void ThemeDocument::ensureDataMediaCreated() const {
}
bool ThemeDocument::checkGoodThumbnail() const {
return !_data->hasThumbnail() || !_data->isPatternWallPaper();
return _data && (!_data->hasThumbnail() || !_data->isPatternWallPaper());
}
void ThemeDocument::validateThumbnail() const {
@ -222,6 +263,10 @@ void ThemeDocument::validateThumbnail() const {
if (_thumbnailGood >= 0) {
return;
}
if (!_data) {
generateThumbnail();
return;
}
ensureDataMediaCreated();
if (const auto normal = _dataMedia->thumbnail()) {
prepareThumbnailFrom(normal, 0);
@ -232,9 +277,20 @@ void ThemeDocument::validateThumbnail() const {
}
}
void ThemeDocument::generateThumbnail() const {
_thumbnail = Ui::PixmapFromImage(Data::GenerateWallPaper(
QSize(_pixw, _pixh) * cIntRetinaFactor(),
_background,
_gradientRotation,
_patternOpacity));
_thumbnail.setDevicePixelRatio(cRetinaFactor());
_thumbnailGood = 1;
}
void ThemeDocument::prepareThumbnailFrom(
not_null<Image*> image,
int good) const {
Expects(_data != nullptr);
Expects(_thumbnailGood <= good);
const auto isTheme = _data->isTheme();
@ -277,7 +333,9 @@ TextState ThemeDocument::textState(QPoint point, StateRequest request) const {
}
auto paintx = 0, painty = 0, paintw = width(), painth = height();
if (QRect(paintx, painty, paintw, painth).contains(point)) {
if (_data->uploading()) {
if (!_data) {
result.link = _openl;
} else if (_data->uploading()) {
result.link = _cancell;
} else if (dataLoaded()) {
result.link = _openl;
@ -292,22 +350,23 @@ TextState ThemeDocument::textState(QPoint point, StateRequest request) const {
float64 ThemeDocument::dataProgress() const {
ensureDataMediaCreated();
return _dataMedia->progress();
return _data ? _dataMedia->progress() : 1.;
}
bool ThemeDocument::dataFinished() const {
return !_data->loading()
&& (!_data->uploading() || _data->waitingForAlbum());
return !_data
|| (!_data->loading()
&& (!_data->uploading() || _data->waitingForAlbum()));
}
bool ThemeDocument::dataLoaded() const {
ensureDataMediaCreated();
return _dataMedia->loaded();
return !_data || _dataMedia->loaded();
}
bool ThemeDocument::isReadyForOpen() const {
ensureDataMediaCreated();
return _dataMedia->loaded();
return !_data || _dataMedia->loaded();
}
QString ThemeDocument::additionalInfoString() const {

View File

@ -13,16 +13,18 @@ class Image;
namespace Data {
class DocumentMedia;
class WallPaper;
} // namespace Data
namespace HistoryView {
class ThemeDocument final : public File {
public:
ThemeDocument(not_null<Element*> parent, DocumentData *document);
ThemeDocument(
not_null<Element*> parent,
not_null<DocumentData*> document,
const QString &url = QString());
DocumentData *document,
const std::optional<Data::WallPaper> &params);
~ThemeDocument();
void draw(
@ -51,6 +53,9 @@ public:
bool hasHeavyPart() const override;
void unloadHeavyPart() override;
[[nodiscard]] static std::optional<Data::WallPaper> ParamsFromUrl(
const QString &url);
protected:
float64 dataProgress() const override;
bool dataFinished() const override;
@ -64,9 +69,10 @@ private:
[[nodiscard]] bool checkGoodThumbnail() const;
void validateThumbnail() const;
void prepareThumbnailFrom(not_null<Image*> image, int good) const;
void generateThumbnail() const;
void ensureDataMediaCreated() const;
const not_null<DocumentData*> _data;
DocumentData *_data = nullptr;
int _pixw = 1;
int _pixh = 1;
mutable QPixmap _thumbnail;

View File

@ -16,12 +16,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/history_view_element.h"
#include "history/view/history_view_cursor_state.h"
#include "history/view/media/history_view_media_common.h"
#include "history/view/media/history_view_theme_document.h"
#include "ui/image/image.h"
#include "ui/text/text_options.h"
#include "ui/text/format_values.h"
#include "ui/cached_round_corners.h"
#include "layout/layout_selection.h" // FullSelection
#include "data/data_session.h"
#include "data/data_wall_paper.h"
#include "data/data_media_types.h"
#include "data/data_web_page.h"
#include "data/data_photo.h"
@ -707,6 +709,8 @@ ClickHandlerPtr WebPage::replaceAttachLink(
} else {
return _openl;
}
} else if (ThemeDocument::ParamsFromUrl(_data->url).has_value()) {
return _openl;
}
return link;
}