Show color / gradient wallpapers in WebPage previews.
This commit is contained in:
parent
436d7b9d82
commit
1bc5277d51
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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> ¶ms)
|
||||
: 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 {
|
||||
|
|
|
@ -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> ¶ms);
|
||||
~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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue