mirror of
https://github.com/vale981/tdesktop
synced 2025-03-06 02:01:40 -05:00
Use new HistoryWallPaper media type for wallpaper.
This commit is contained in:
parent
fd8e9dad92
commit
5ca12a73c3
24 changed files with 683 additions and 192 deletions
|
@ -92,7 +92,7 @@ msgServiceFont: semiboldFont;
|
|||
msgServiceNameFont: semiboldFont;
|
||||
msgServicePhotoWidth: 100px;
|
||||
msgDateFont: font(13px);
|
||||
msgMinWidth: 190px;
|
||||
msgMinWidth: 160px;
|
||||
msgPhotoSize: 33px;
|
||||
msgPhotoSkip: 40px;
|
||||
msgPadding: margins(13px, 7px, 13px, 8px);
|
||||
|
|
|
@ -1037,6 +1037,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_media_animation_title" = "Animated GIFs";
|
||||
"lng_media_size_limit" = "Limit by size";
|
||||
"lng_media_size_up_to" = "up to {size}";
|
||||
"lng_media_chat_background" = "Chat background";
|
||||
|
||||
"lng_emoji_category1" = "People";
|
||||
"lng_emoji_category2" = "Nature";
|
||||
|
|
|
@ -152,7 +152,7 @@ QImage PrepareScaledFromFull(
|
|||
if (patternBackground) {
|
||||
result = ColorizePattern(
|
||||
std::move(result),
|
||||
Window::Theme::PatternColor(*patternBackground));
|
||||
Data::PatternColor(*patternBackground));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -593,6 +593,7 @@ bool DocumentData::checkWallPaperProperties() {
|
|||
return false;
|
||||
}
|
||||
type = WallPaperDocument;
|
||||
validateGoodThumbnail();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -615,6 +616,10 @@ bool DocumentData::isWallPaper() const {
|
|||
return (type == WallPaperDocument);
|
||||
}
|
||||
|
||||
bool DocumentData::isPatternWallPaper() const {
|
||||
return isWallPaper() && hasMimeType(qstr("image/png"));
|
||||
}
|
||||
|
||||
bool DocumentData::hasThumbnail() const {
|
||||
return !_thumbnail->isNull();
|
||||
}
|
||||
|
@ -642,7 +647,7 @@ Image *DocumentData::goodThumbnail() const {
|
|||
}
|
||||
|
||||
void DocumentData::validateGoodThumbnail() {
|
||||
if (!isVideoFile() && !isAnimation()) {
|
||||
if (!isVideoFile() && !isAnimation() && !isWallPaper()) {
|
||||
_goodThumbnail = nullptr;
|
||||
} else if (!_goodThumbnail && hasRemoteLocation()) {
|
||||
_goodThumbnail = std::make_unique<Image>(
|
||||
|
|
|
@ -166,6 +166,7 @@ public:
|
|||
}
|
||||
bool checkWallPaperProperties();
|
||||
[[nodiscard]] bool isWallPaper() const;
|
||||
[[nodiscard]] bool isPatternWallPaper() const;
|
||||
|
||||
[[nodiscard]] bool hasThumbnail() const;
|
||||
void loadThumbnail(Data::FileOrigin origin);
|
||||
|
@ -342,6 +343,26 @@ protected:
|
|||
|
||||
};
|
||||
|
||||
class DocumentWrappedClickHandler : public DocumentClickHandler {
|
||||
public:
|
||||
DocumentWrappedClickHandler(
|
||||
ClickHandlerPtr wrapped,
|
||||
not_null<DocumentData*> document,
|
||||
FullMsgId context = FullMsgId())
|
||||
: DocumentClickHandler(document, context)
|
||||
, _wrapped(wrapped) {
|
||||
}
|
||||
|
||||
protected:
|
||||
void onClickImpl() const override {
|
||||
_wrapped->onClick({ Qt::LeftButton });
|
||||
}
|
||||
|
||||
private:
|
||||
ClickHandlerPtr _wrapped;
|
||||
|
||||
};
|
||||
|
||||
QString FileNameForSave(
|
||||
const QString &title,
|
||||
const QString &filter,
|
||||
|
|
|
@ -17,6 +17,41 @@ namespace Data {
|
|||
namespace {
|
||||
|
||||
constexpr auto kGoodThumbQuality = 87;
|
||||
constexpr auto kWallPaperSize = 960;
|
||||
|
||||
QImage Prepare(
|
||||
const QString &path,
|
||||
QByteArray data,
|
||||
bool isWallPaper) {
|
||||
if (!isWallPaper) {
|
||||
return Media::Clip::PrepareForSending(path, data).thumbnail;
|
||||
}
|
||||
const auto validateSize = [](QSize size) {
|
||||
return (size.width() + size.height()) < 10'000;
|
||||
};
|
||||
auto buffer = QBuffer(&data);
|
||||
auto file = QFile(path);
|
||||
auto device = data.isEmpty() ? static_cast<QIODevice*>(&file) : &buffer;
|
||||
auto reader = QImageReader(device);
|
||||
#ifndef OS_MAC_OLD
|
||||
reader.setAutoTransform(true);
|
||||
#endif // OS_MAC_OLD
|
||||
if (!reader.canRead() || !validateSize(reader.size())) {
|
||||
return QImage();
|
||||
}
|
||||
auto result = reader.read();
|
||||
if (!result.width() || !result.height()) {
|
||||
return QImage();
|
||||
}
|
||||
return (result.width() > kWallPaperSize
|
||||
|| result.height() > kWallPaperSize)
|
||||
? result.scaled(
|
||||
kWallPaperSize,
|
||||
kWallPaperSize,
|
||||
Qt::KeepAspectRatio,
|
||||
Qt::SmoothTransformation)
|
||||
: result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
@ -29,6 +64,7 @@ void GoodThumbSource::generate(base::binary_guard &&guard) {
|
|||
return;
|
||||
}
|
||||
const auto data = _document->data();
|
||||
const auto isWallPaper = _document->isWallPaper();
|
||||
auto location = _document->location().isEmpty()
|
||||
? nullptr
|
||||
: std::make_unique<FileLocation>(_document->location());
|
||||
|
@ -44,11 +80,14 @@ void GoodThumbSource::generate(base::binary_guard &&guard) {
|
|||
const auto filepath = (location && location->accessEnable())
|
||||
? location->name()
|
||||
: QString();
|
||||
auto result = Media::Clip::PrepareForSending(filepath, data);
|
||||
auto result = Prepare(filepath, data, isWallPaper);
|
||||
auto bytes = QByteArray();
|
||||
if (!result.thumbnail.isNull()) {
|
||||
QBuffer buffer(&bytes);
|
||||
result.thumbnail.save(&buffer, "JPG", kGoodThumbQuality);
|
||||
if (!result.isNull()) {
|
||||
auto buffer = QBuffer(&bytes);
|
||||
const auto format = (isWallPaper && result.hasAlphaChannel())
|
||||
? "PNG"
|
||||
: "JPG";
|
||||
result.save(&buffer, format, kGoodThumbQuality);
|
||||
}
|
||||
if (!filepath.isEmpty()) {
|
||||
location->accessDisable();
|
||||
|
@ -56,7 +95,7 @@ void GoodThumbSource::generate(base::binary_guard &&guard) {
|
|||
const auto bytesSize = bytes.size();
|
||||
ready(
|
||||
std::move(guard),
|
||||
std::move(result.thumbnail),
|
||||
std::move(result),
|
||||
bytesSize,
|
||||
std::move(bytes));
|
||||
});
|
||||
|
@ -119,7 +158,10 @@ void GoodThumbSource::load(
|
|||
guard = std::move(guard),
|
||||
value = std::move(value)
|
||||
]() mutable {
|
||||
ready(std::move(guard), App::readImage(value), value.size());
|
||||
ready(
|
||||
std::move(guard),
|
||||
App::readImage(value, nullptr, false),
|
||||
value.size());
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -1017,7 +1017,7 @@ WebPageData *MediaWebPage::webpage() const {
|
|||
|
||||
bool MediaWebPage::hasReplyPreview() const {
|
||||
if (const auto document = MediaWebPage::document()) {
|
||||
return document->hasThumbnail();
|
||||
return document->hasThumbnail() && !document->isPatternWallPaper();
|
||||
} else if (const auto photo = MediaWebPage::photo()) {
|
||||
return !photo->isNull();
|
||||
}
|
||||
|
|
|
@ -294,6 +294,7 @@ class DocumentClickHandler;
|
|||
class DocumentSaveClickHandler;
|
||||
class DocumentOpenClickHandler;
|
||||
class DocumentCancelClickHandler;
|
||||
class DocumentWrappedClickHandler;
|
||||
class GifOpenClickHandler;
|
||||
class VoiceSeekClickHandler;
|
||||
|
||||
|
|
|
@ -15,6 +15,8 @@ maxStickerSize: 256px;
|
|||
maxGifSize: 320px;
|
||||
maxVideoMessageSize: 240px;
|
||||
maxSignatureSize: 144px;
|
||||
maxWallPaperWidth: 160px;
|
||||
maxWallPaperHeight: 240px;
|
||||
|
||||
historyMinimalWidth: 380px;
|
||||
|
||||
|
|
|
@ -1503,13 +1503,48 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
|||
});
|
||||
}
|
||||
};
|
||||
|
||||
const auto addPhotoActions = [&](not_null<PhotoData*> photo) {
|
||||
_menu->addAction(lang(lng_context_save_image), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [=] {
|
||||
savePhotoToFile(photo);
|
||||
}));
|
||||
_menu->addAction(lang(lng_context_copy_image), [=] {
|
||||
copyContextImage(photo);
|
||||
});
|
||||
};
|
||||
const auto addDocumentActions = [&](not_null<DocumentData*> document) {
|
||||
if (document->loading()) {
|
||||
_menu->addAction(lang(lng_context_cancel_download), [=] {
|
||||
cancelContextDownload(document);
|
||||
});
|
||||
return;
|
||||
}
|
||||
const auto item = _dragStateItem;
|
||||
const auto itemId = item ? item->fullId() : FullMsgId();
|
||||
const auto lnkIsVideo = document->isVideoFile();
|
||||
const auto lnkIsVoice = document->isVoiceMessage();
|
||||
const auto lnkIsAudio = document->isAudioFile();
|
||||
if (document->loaded() && document->isGifv()) {
|
||||
if (!cAutoPlayGif()) {
|
||||
_menu->addAction(lang(lng_context_open_gif), [=] {
|
||||
openContextGif(itemId);
|
||||
});
|
||||
}
|
||||
_menu->addAction(lang(lng_context_save_gif), [=] {
|
||||
saveContextGif(itemId);
|
||||
});
|
||||
}
|
||||
if (!document->filepath(DocumentData::FilePathResolveChecked).isEmpty()) {
|
||||
_menu->addAction(lang((cPlatform() == dbipMac || cPlatform() == dbipMacOld) ? lng_context_show_in_finder : lng_context_show_in_folder), [=] {
|
||||
showContextInFolder(document);
|
||||
});
|
||||
}
|
||||
_menu->addAction(lang(lnkIsVideo ? lng_context_save_video : (lnkIsVoice ? lng_context_save_audio : (lnkIsAudio ? lng_context_save_audio_file : lng_context_save_file))), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [=] {
|
||||
saveDocumentToFile(itemId, document);
|
||||
}));
|
||||
};
|
||||
const auto link = ClickHandler::getActive();
|
||||
auto lnkPhoto = dynamic_cast<PhotoClickHandler*>(link.get());
|
||||
auto lnkDocument = dynamic_cast<DocumentClickHandler*>(link.get());
|
||||
auto lnkIsVideo = lnkDocument ? lnkDocument->document()->isVideoFile() : false;
|
||||
auto lnkIsVoice = lnkDocument ? lnkDocument->document()->isVoiceMessage() : false;
|
||||
auto lnkIsAudio = lnkDocument ? lnkDocument->document()->isAudioFile() : false;
|
||||
if (lnkPhoto || lnkDocument) {
|
||||
const auto item = _dragStateItem;
|
||||
const auto itemId = item ? item->fullId() : FullMsgId();
|
||||
|
@ -1522,39 +1557,9 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
|||
}
|
||||
addItemActions(item);
|
||||
if (lnkPhoto) {
|
||||
const auto photo = lnkPhoto->photo();
|
||||
_menu->addAction(lang(lng_context_save_image), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [=] {
|
||||
savePhotoToFile(photo);
|
||||
}));
|
||||
_menu->addAction(lang(lng_context_copy_image), [=] {
|
||||
copyContextImage(photo);
|
||||
});
|
||||
addPhotoActions(lnkPhoto->photo());
|
||||
} else {
|
||||
auto document = lnkDocument->document();
|
||||
if (document->loading()) {
|
||||
_menu->addAction(lang(lng_context_cancel_download), [=] {
|
||||
cancelContextDownload(document);
|
||||
});
|
||||
} else {
|
||||
if (document->loaded() && document->isGifv()) {
|
||||
if (!cAutoPlayGif()) {
|
||||
_menu->addAction(lang(lng_context_open_gif), [=] {
|
||||
openContextGif(itemId);
|
||||
});
|
||||
}
|
||||
_menu->addAction(lang(lng_context_save_gif), [=] {
|
||||
saveContextGif(itemId);
|
||||
});
|
||||
}
|
||||
if (!document->filepath(DocumentData::FilePathResolveChecked).isEmpty()) {
|
||||
_menu->addAction(lang((cPlatform() == dbipMac || cPlatform() == dbipMacOld) ? lng_context_show_in_finder : lng_context_show_in_folder), [=] {
|
||||
showContextInFolder(document);
|
||||
});
|
||||
}
|
||||
_menu->addAction(lang(lnkIsVideo ? lng_context_save_video : (lnkIsVoice ? lng_context_save_audio : (lnkIsAudio ? lng_context_save_audio_file : lng_context_save_file))), App::LambdaDelayed(st::defaultDropdownMenu.menu.ripple.hideDuration, this, [=] {
|
||||
saveDocumentToFile(itemId, document);
|
||||
}));
|
||||
}
|
||||
addDocumentActions(lnkDocument->document());
|
||||
}
|
||||
if (item && item->hasDirectLink() && isUponSelected != 2 && isUponSelected != -2) {
|
||||
_menu->addAction(lang(item->history()->peer->isMegagroup() ? lng_context_copy_link : lng_context_copy_post_link), [=] {
|
||||
|
|
|
@ -47,37 +47,44 @@ public:
|
|||
HistoryMedia(not_null<Element*> parent) : _parent(parent) {
|
||||
}
|
||||
|
||||
not_null<History*> history() const;
|
||||
[[nodiscard]] not_null<History*> history() const;
|
||||
|
||||
virtual TextWithEntities selectedText(TextSelection selection) const {
|
||||
[[nodiscard]] virtual TextWithEntities selectedText(
|
||||
TextSelection selection) const {
|
||||
return TextWithEntities();
|
||||
}
|
||||
|
||||
virtual bool isDisplayed() const;
|
||||
[[nodiscard]] virtual bool isDisplayed() const;
|
||||
virtual void updateNeedBubbleState() {
|
||||
}
|
||||
virtual bool hasTextForCopy() const {
|
||||
[[nodiscard]] virtual bool hasTextForCopy() const {
|
||||
return false;
|
||||
}
|
||||
virtual bool hideMessageText() const {
|
||||
[[nodiscard]] virtual bool hideMessageText() const {
|
||||
return true;
|
||||
}
|
||||
virtual bool allowsFastShare() const {
|
||||
[[nodiscard]] virtual bool allowsFastShare() const {
|
||||
return false;
|
||||
}
|
||||
virtual void refreshParentId(not_null<HistoryItem*> realParent) {
|
||||
}
|
||||
virtual void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const = 0;
|
||||
virtual PointState pointState(QPoint point) const;
|
||||
virtual TextState textState(QPoint point, StateRequest request) const = 0;
|
||||
virtual void draw(
|
||||
Painter &p,
|
||||
const QRect &r,
|
||||
TextSelection selection,
|
||||
TimeMs ms) const = 0;
|
||||
[[nodiscard]] virtual PointState pointState(QPoint point) const;
|
||||
[[nodiscard]] virtual TextState textState(
|
||||
QPoint point,
|
||||
StateRequest request) const = 0;
|
||||
virtual void updatePressed(QPoint point) {
|
||||
}
|
||||
|
||||
virtual Storage::SharedMediaTypesMask sharedMediaTypes() const;
|
||||
[[nodiscard]] virtual Storage::SharedMediaTypesMask sharedMediaTypes() const;
|
||||
|
||||
// if we are in selecting items mode perhaps we want to
|
||||
// toggle selection instead of activating the pressed link
|
||||
virtual bool toggleSelectionByHandlerClick(
|
||||
[[nodiscard]] virtual bool toggleSelectionByHandlerClick(
|
||||
const ClickHandlerPtr &p) const = 0;
|
||||
|
||||
// if we press and drag on this media should we drag the item
|
||||
|
@ -99,21 +106,22 @@ public:
|
|||
TextSelection selection) const;
|
||||
|
||||
// if we press and drag this link should we drag the item
|
||||
virtual bool dragItemByHandler(const ClickHandlerPtr &p) const = 0;
|
||||
[[nodiscard]] virtual bool dragItemByHandler(
|
||||
const ClickHandlerPtr &p) const = 0;
|
||||
|
||||
virtual void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) {
|
||||
}
|
||||
virtual void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) {
|
||||
}
|
||||
|
||||
virtual bool uploading() const {
|
||||
[[nodiscard]] virtual bool uploading() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual PhotoData *getPhoto() const {
|
||||
[[nodiscard]] virtual PhotoData *getPhoto() const {
|
||||
return nullptr;
|
||||
}
|
||||
virtual DocumentData *getDocument() const {
|
||||
[[nodiscard]] virtual DocumentData *getDocument() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -126,7 +134,7 @@ public:
|
|||
virtual void stopAnimation() {
|
||||
}
|
||||
|
||||
virtual QSize sizeForGrouping() const {
|
||||
[[nodiscard]] virtual QSize sizeForGrouping() const {
|
||||
Unexpected("Grouping method call.");
|
||||
}
|
||||
virtual void drawGrouped(
|
||||
|
@ -140,54 +148,62 @@ public:
|
|||
not_null<QPixmap*> cache) const {
|
||||
Unexpected("Grouping method call.");
|
||||
}
|
||||
virtual TextState getStateGrouped(
|
||||
[[nodiscard]] virtual TextState getStateGrouped(
|
||||
const QRect &geometry,
|
||||
QPoint point,
|
||||
StateRequest request) const;
|
||||
|
||||
virtual bool animating() const {
|
||||
[[nodiscard]] virtual bool animating() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual TextWithEntities getCaption() const {
|
||||
[[nodiscard]] virtual TextWithEntities getCaption() const {
|
||||
return TextWithEntities();
|
||||
}
|
||||
virtual bool needsBubble() const = 0;
|
||||
virtual bool customInfoLayout() const = 0;
|
||||
virtual QMargins bubbleMargins() const {
|
||||
[[nodiscard]] virtual bool needsBubble() const = 0;
|
||||
[[nodiscard]] virtual bool customInfoLayout() const = 0;
|
||||
[[nodiscard]] virtual QMargins bubbleMargins() const {
|
||||
return QMargins();
|
||||
}
|
||||
virtual bool hideForwardedFrom() const {
|
||||
[[nodiscard]] virtual bool hideForwardedFrom() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool overrideEditedDate() const {
|
||||
[[nodiscard]] virtual bool overrideEditedDate() const {
|
||||
return false;
|
||||
}
|
||||
virtual HistoryMessageEdited *displayedEditBadge() const {
|
||||
[[nodiscard]] virtual HistoryMessageEdited *displayedEditBadge() const {
|
||||
Unexpected("displayedEditBadge() on non-grouped media.");
|
||||
}
|
||||
|
||||
// An attach media in a web page can provide an
|
||||
// additional text to be displayed below the attach.
|
||||
// For example duration / progress for video messages.
|
||||
virtual QString additionalInfoString() const {
|
||||
[[nodiscard]] virtual QString additionalInfoString() const {
|
||||
return QString();
|
||||
}
|
||||
|
||||
void setInBubbleState(MediaInBubbleState state) {
|
||||
_inBubbleState = state;
|
||||
}
|
||||
MediaInBubbleState inBubbleState() const {
|
||||
[[nodiscard]] MediaInBubbleState inBubbleState() const {
|
||||
return _inBubbleState;
|
||||
}
|
||||
bool isBubbleTop() const {
|
||||
return (_inBubbleState == MediaInBubbleState::Top) || (_inBubbleState == MediaInBubbleState::None);
|
||||
[[nodiscard]] bool isBubbleTop() const {
|
||||
return (_inBubbleState == MediaInBubbleState::Top)
|
||||
|| (_inBubbleState == MediaInBubbleState::None);
|
||||
}
|
||||
bool isBubbleBottom() const {
|
||||
return (_inBubbleState == MediaInBubbleState::Bottom) || (_inBubbleState == MediaInBubbleState::None);
|
||||
[[nodiscard]] bool isBubbleBottom() const {
|
||||
return (_inBubbleState == MediaInBubbleState::Bottom)
|
||||
|| (_inBubbleState == MediaInBubbleState::None);
|
||||
}
|
||||
virtual bool skipBubbleTail() const {
|
||||
[[nodiscard]] virtual bool skipBubbleTail() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Sometimes webpages can force the bubble to fit their size instead of
|
||||
// allowing message text to be as wide as possible (like wallpapers).
|
||||
[[nodiscard]] virtual bool enforceBubbleWidth() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -196,7 +212,7 @@ public:
|
|||
// But the overloading click handler should be used only when media
|
||||
// is already loaded (not a photo or GIF waiting for load with auto
|
||||
// load being disabled - in such case media should handle the click).
|
||||
virtual bool isReadyForOpen() const {
|
||||
[[nodiscard]] virtual bool isReadyForOpen() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/media/history_media_document.h"
|
||||
#include "history/media/history_media_sticker.h"
|
||||
#include "history/media/history_media_video.h"
|
||||
#include "history/media/history_media_wall_paper.h"
|
||||
#include "styles/style_history.h"
|
||||
|
||||
int documentMaxStatusWidth(DocumentData *document) {
|
||||
|
@ -60,25 +61,27 @@ std::unique_ptr<HistoryMedia> CreateAttach(
|
|||
not_null<HistoryView::Element*> parent,
|
||||
DocumentData *document,
|
||||
PhotoData *photo,
|
||||
const std::vector<std::unique_ptr<Data::Media>> &collage) {
|
||||
const std::vector<std::unique_ptr<Data::Media>> &collage,
|
||||
const QString &webpageUrl) {
|
||||
if (!collage.empty()) {
|
||||
return std::make_unique<HistoryGroupedMedia>(parent, collage);
|
||||
} else if (document) {
|
||||
if (document->sticker()) {
|
||||
return std::make_unique<HistorySticker>(parent, document);
|
||||
} else if (document->isAnimation()) {
|
||||
return std::make_unique<HistoryGif>(
|
||||
parent,
|
||||
document);
|
||||
return std::make_unique<HistoryGif>(parent, document);
|
||||
} else if (document->isVideoFile()) {
|
||||
return std::make_unique<HistoryVideo>(
|
||||
parent,
|
||||
parent->data(),
|
||||
document);
|
||||
} else if (document->isWallPaper()) {
|
||||
return std::make_unique<HistoryWallPaper>(
|
||||
parent,
|
||||
document,
|
||||
webpageUrl);
|
||||
}
|
||||
return std::make_unique<HistoryDocument>(
|
||||
parent,
|
||||
document);
|
||||
return std::make_unique<HistoryDocument>(parent, document);
|
||||
} else if (photo) {
|
||||
return std::make_unique<HistoryPhoto>(
|
||||
parent,
|
||||
|
|
|
@ -32,5 +32,6 @@ std::unique_ptr<HistoryMedia> CreateAttach(
|
|||
not_null<HistoryView::Element*> parent,
|
||||
DocumentData *document,
|
||||
PhotoData *photo,
|
||||
const std::vector<std::unique_ptr<Data::Media>> &collage = {});
|
||||
const std::vector<std::unique_ptr<Data::Media>> &collage = {},
|
||||
const QString &webpageUrl = QString());
|
||||
int unitedLineHeight();
|
||||
|
|
|
@ -34,11 +34,6 @@ HistoryPhoto::HistoryPhoto(
|
|||
: HistoryFileMedia(parent, realParent)
|
||||
, _data(photo)
|
||||
, _caption(st::minPhotoSize - st::msgPadding.left() - st::msgPadding.right()) {
|
||||
const auto fullId = realParent->fullId();
|
||||
setLinks(
|
||||
std::make_shared<PhotoOpenClickHandler>(_data, fullId),
|
||||
std::make_shared<PhotoSaveClickHandler>(_data, fullId),
|
||||
std::make_shared<PhotoCancelClickHandler>(_data, fullId));
|
||||
_caption = createCaption(realParent);
|
||||
create(realParent->fullId());
|
||||
}
|
||||
|
|
277
Telegram/SourceFiles/history/media/history_media_wall_paper.cpp
Normal file
277
Telegram/SourceFiles/history/media/history_media_wall_paper.cpp
Normal file
|
@ -0,0 +1,277 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "history/media/history_media_wall_paper.h"
|
||||
|
||||
#include "layout.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/view/history_view_element.h"
|
||||
#include "history/view/history_view_cursor_state.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_file_origin.h"
|
||||
#include "base/qthelp_url.h"
|
||||
#include "window/themes/window_theme.h"
|
||||
#include "styles/style_history.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using TextState = HistoryView::TextState;
|
||||
|
||||
} // namespace
|
||||
|
||||
HistoryWallPaper::HistoryWallPaper(
|
||||
not_null<Element*> parent,
|
||||
not_null<DocumentData*> document,
|
||||
const QString &url)
|
||||
: HistoryFileMedia(parent, parent->data())
|
||||
, _data(document) {
|
||||
Expects(_data->hasThumbnail());
|
||||
|
||||
fillPatternFieldsFrom(url);
|
||||
|
||||
_data->thumbnail()->load(parent->data()->fullId());
|
||||
setDocumentLinks(_data, parent->data());
|
||||
setStatusSize(FileStatusSizeReady, _data->size, -1, 0);
|
||||
}
|
||||
|
||||
void HistoryWallPaper::fillPatternFieldsFrom(const QString &url) {
|
||||
const auto paramsPosition = url.indexOf('?');
|
||||
if (paramsPosition < 0) {
|
||||
return;
|
||||
}
|
||||
const auto paramsString = url.mid(paramsPosition + 1);
|
||||
const auto params = qthelp::url_parse_params(
|
||||
paramsString,
|
||||
qthelp::UrlParamNameTransform::ToLower);
|
||||
const auto kDefaultBackground = QColor(213, 223, 233);
|
||||
const auto paper = Data::DefaultWallPaper().withUrlParams(params);
|
||||
_intensity = paper.patternIntensity();
|
||||
_background = paper.backgroundColor().value_or(kDefaultBackground);
|
||||
}
|
||||
|
||||
QSize HistoryWallPaper::countOptimalSize() {
|
||||
auto tw = ConvertScale(_data->thumbnail()->width());
|
||||
auto th = ConvertScale(_data->thumbnail()->height());
|
||||
if (!tw || !th) {
|
||||
tw = th = 1;
|
||||
}
|
||||
th = (st::maxWallPaperWidth * th) / tw;
|
||||
tw = st::maxWallPaperWidth;
|
||||
|
||||
const auto maxWidth = tw;
|
||||
const auto minHeight = std::clamp(
|
||||
th,
|
||||
st::minPhotoSize,
|
||||
st::maxWallPaperHeight);
|
||||
return { maxWidth, minHeight };
|
||||
}
|
||||
|
||||
QSize HistoryWallPaper::countCurrentSize(int newWidth) {
|
||||
auto tw = ConvertScale(_data->thumbnail()->width());
|
||||
auto th = ConvertScale(_data->thumbnail()->height());
|
||||
if (!tw || !th) {
|
||||
tw = th = 1;
|
||||
}
|
||||
|
||||
// We use pix() for image copies, because we rely that backgrounds
|
||||
// are always displayed with the same dimensions (not pixSingle()).
|
||||
_pixw = maxWidth();// std::min(newWidth, maxWidth());
|
||||
_pixh = minHeight();// (_pixw * th / tw);
|
||||
|
||||
newWidth = _pixw;
|
||||
const auto newHeight = _pixh; /*std::clamp(
|
||||
_pixh,
|
||||
st::minPhotoSize,
|
||||
st::maxWallPaperHeight);*/
|
||||
return { newWidth, newHeight };
|
||||
}
|
||||
|
||||
void HistoryWallPaper::draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const {
|
||||
if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) return;
|
||||
|
||||
_data->automaticLoad(_realParent->fullId(), _parent->data());
|
||||
auto selected = (selection == FullSelection);
|
||||
auto loaded = _data->loaded();
|
||||
auto displayLoading = _data->displayLoading();
|
||||
|
||||
auto inWebPage = (_parent->media() != this);
|
||||
auto paintx = 0, painty = 0, paintw = width(), painth = height();
|
||||
|
||||
auto captionw = paintw - st::msgPadding.left() - st::msgPadding.right();
|
||||
|
||||
if (displayLoading) {
|
||||
ensureAnimation();
|
||||
if (!_animation->radial.animating()) {
|
||||
_animation->radial.start(_data->progress());
|
||||
}
|
||||
}
|
||||
bool radial = isRadialAnimation(ms);
|
||||
|
||||
auto rthumb = rtlrect(paintx, painty, paintw, painth, width());
|
||||
auto roundRadius = ImageRoundRadius::Small;
|
||||
auto roundCorners = RectPart::AllCorners;
|
||||
validateThumbnail();
|
||||
p.drawPixmap(rthumb.topLeft(), _thumbnail);
|
||||
if (selected) {
|
||||
App::complexOverlayRect(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();
|
||||
App::roundRect(p, rtlrect(statusX - st::msgDateImgPadding.x(), statusY - st::msgDateImgPadding.y(), statusW, statusH, width()), selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? DateSelectedCorners : 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.;
|
||||
QRect inner(rthumb.x() + (rthumb.width() - st::msgFileSize) / 2, rthumb.y() + (rthumb.height() - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize);
|
||||
p.setPen(Qt::NoPen);
|
||||
if (selected) {
|
||||
p.setBrush(st::msgDateImgBgSelected);
|
||||
} else if (isThumbAnimation(ms)) {
|
||||
auto over = _animation->a_thumbOver.current();
|
||||
p.setBrush(anim::brush(st::msgDateImgBg, st::msgDateImgBgOver, over));
|
||||
} else {
|
||||
auto over = ClickHandler::showAsActive(_data->loading() ? _cancell : _savel);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryWallPaper::validateThumbnail() const {
|
||||
if (_thumbnailGood > 0) {
|
||||
return;
|
||||
}
|
||||
const auto good = _data->goodThumbnail();
|
||||
if (good) {
|
||||
if (good->loaded()) {
|
||||
prepareThumbnailFrom(good, 1);
|
||||
return;
|
||||
} else {
|
||||
good->load({});
|
||||
}
|
||||
}
|
||||
if (_thumbnailGood >= 0) {
|
||||
return;
|
||||
}
|
||||
if (_data->thumbnail()->loaded()) {
|
||||
prepareThumbnailFrom(_data->thumbnail(), 0);
|
||||
} else if (const auto blurred = _data->thumbnailInline()) {
|
||||
if (_thumbnail.isNull()) {
|
||||
prepareThumbnailFrom(blurred, -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryWallPaper::prepareThumbnailFrom(
|
||||
not_null<Image*> image,
|
||||
int good) const {
|
||||
Expects(_thumbnailGood <= good);
|
||||
|
||||
const auto isPattern = _data->isPatternWallPaper();
|
||||
auto options = Images::Option::Smooth
|
||||
| (good >= 0 ? Images::Option(0) : Images::Option::Blurred)
|
||||
| (isPattern
|
||||
? Images::Option::TransparentBackground
|
||||
: Images::Option(0));
|
||||
auto original = image->original();
|
||||
auto tw = ConvertScale(_data->thumbnail()->width());
|
||||
auto th = ConvertScale(_data->thumbnail()->height());
|
||||
if (!tw || !th) {
|
||||
tw = th = 1;
|
||||
}
|
||||
original = Images::prepare(
|
||||
std::move(original),
|
||||
_pixw,
|
||||
(_pixw * th) / tw,
|
||||
options,
|
||||
_pixw,
|
||||
_pixh);
|
||||
if (isPattern) {
|
||||
original = Data::PreparePatternImage(
|
||||
std::move(original),
|
||||
_background,
|
||||
Data::PatternColor(_background),
|
||||
_intensity);
|
||||
}
|
||||
_thumbnail = App::pixmapFromImageInPlace(std::move(original));
|
||||
_thumbnailGood = good;
|
||||
}
|
||||
|
||||
TextState HistoryWallPaper::textState(QPoint point, StateRequest request) const {
|
||||
auto result = TextState(_parent);
|
||||
|
||||
if (width() < st::msgPadding.left() + st::msgPadding.right() + 1) {
|
||||
return result;
|
||||
}
|
||||
auto paintx = 0, painty = 0, paintw = width(), painth = height();
|
||||
auto bubble = _parent->hasBubble();
|
||||
if (QRect(paintx, painty, paintw, painth).contains(point)) {
|
||||
if (_data->uploading()) {
|
||||
result.link = _cancell;
|
||||
} else if (_data->loaded()) {
|
||||
result.link = _openl;
|
||||
} else if (_data->loading()) {
|
||||
result.link = _cancell;
|
||||
} else {
|
||||
result.link = _savel;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
float64 HistoryWallPaper::dataProgress() const {
|
||||
return _data->progress();
|
||||
}
|
||||
|
||||
bool HistoryWallPaper::dataFinished() const {
|
||||
return !_data->loading()
|
||||
&& (!_data->uploading() || _data->waitingForAlbum());
|
||||
}
|
||||
|
||||
bool HistoryWallPaper::dataLoaded() const {
|
||||
return _data->loaded();
|
||||
}
|
||||
|
||||
bool HistoryWallPaper::isReadyForOpen() const {
|
||||
return _data->loaded();
|
||||
}
|
||||
|
||||
QString HistoryWallPaper::additionalInfoString() const {
|
||||
// This will force message info (time) to be displayed below
|
||||
// this attachment in HistoryWebPage media.
|
||||
static auto result = QString(" ");
|
||||
return result;
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "history/media/history_media_file.h"
|
||||
|
||||
class HistoryWallPaper : public HistoryFileMedia {
|
||||
public:
|
||||
HistoryWallPaper(
|
||||
not_null<Element*> parent,
|
||||
not_null<DocumentData*> document,
|
||||
const QString &url = QString());
|
||||
|
||||
void draw(
|
||||
Painter &p,
|
||||
const QRect &clip,
|
||||
TextSelection selection,
|
||||
TimeMs ms) const override;
|
||||
TextState textState(QPoint point, StateRequest request) const override;
|
||||
|
||||
DocumentData *getDocument() const override {
|
||||
return _data;
|
||||
}
|
||||
|
||||
bool needsBubble() const override {
|
||||
return false;
|
||||
}
|
||||
bool customInfoLayout() const override {
|
||||
return false;
|
||||
}
|
||||
bool skipBubbleTail() const override {
|
||||
return true;
|
||||
}
|
||||
bool isReadyForOpen() const override;
|
||||
QString additionalInfoString() const override;
|
||||
|
||||
protected:
|
||||
float64 dataProgress() const override;
|
||||
bool dataFinished() const override;
|
||||
bool dataLoaded() const override;
|
||||
|
||||
private:
|
||||
QSize countOptimalSize() override;
|
||||
QSize countCurrentSize(int newWidth) override;
|
||||
|
||||
void fillPatternFieldsFrom(const QString &url);
|
||||
void validateThumbnail() const;
|
||||
void prepareThumbnailFrom(not_null<Image*> image, int good) const;
|
||||
|
||||
not_null<DocumentData*> _data;
|
||||
int _pixw = 1;
|
||||
int _pixh = 1;
|
||||
mutable QPixmap _thumbnail;
|
||||
mutable int _thumbnailGood = -1; // -1 inline, 0 thumbnail, 1 good
|
||||
QColor _background;
|
||||
int _intensity = 0;
|
||||
|
||||
};
|
|
@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
#include "layout.h"
|
||||
#include "core/click_handler_types.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "history/history_item_components.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history.h"
|
||||
|
@ -132,6 +133,12 @@ QSize HistoryWebPage::countOptimalSize() {
|
|||
_openl = previewOfHiddenUrl
|
||||
? std::make_shared<HiddenUrlClickHandler>(_data->url)
|
||||
: std::make_shared<UrlClickHandler>(_data->url, true);
|
||||
if (_data->document && _data->document->isWallPaper()) {
|
||||
_openl = std::make_shared<DocumentWrappedClickHandler>(
|
||||
std::move(_openl),
|
||||
_data->document,
|
||||
_parent->data()->fullId());
|
||||
}
|
||||
}
|
||||
|
||||
// init layout
|
||||
|
@ -169,7 +176,8 @@ QSize HistoryWebPage::countOptimalSize() {
|
|||
_parent,
|
||||
_data->document,
|
||||
_data->photo,
|
||||
_collage);
|
||||
_collage,
|
||||
_data->url);
|
||||
}
|
||||
|
||||
auto textFloatsAroundInfo = !_asArticle && !_attach && isBubbleBottom();
|
||||
|
@ -202,8 +210,8 @@ QSize HistoryWebPage::countOptimalSize() {
|
|||
title,
|
||||
Ui::WebpageTextTitleOptions());
|
||||
}
|
||||
if (!_siteNameWidth && !_data->siteName.isEmpty()) {
|
||||
_siteNameWidth = st::webPageTitleFont->width(_data->siteName);
|
||||
if (!_siteNameWidth && !displayedSiteName().isEmpty()) {
|
||||
_siteNameWidth = st::webPageTitleFont->width(displayedSiteName());
|
||||
}
|
||||
|
||||
// init dimensions
|
||||
|
@ -212,7 +220,7 @@ QSize HistoryWebPage::countOptimalSize() {
|
|||
auto maxWidth = skipBlockWidth;
|
||||
auto minHeight = 0;
|
||||
|
||||
auto siteNameHeight = _data->siteName.isEmpty() ? 0 : lineHeight;
|
||||
auto siteNameHeight = _siteNameWidth ? lineHeight : 0;
|
||||
auto titleMinHeight = _title.isEmpty() ? 0 : lineHeight;
|
||||
auto descMaxLines = isLogEntryOriginal() ? kMaxOriginalEntryLines : (3 + (siteNameHeight ? 0 : 1) + (titleMinHeight ? 0 : 1));
|
||||
auto descriptionMinHeight = _description.isEmpty() ? 0 : qMin(_description.minHeight(), descMaxLines * lineHeight);
|
||||
|
@ -223,7 +231,7 @@ QSize HistoryWebPage::countOptimalSize() {
|
|||
}
|
||||
|
||||
if (_siteNameWidth) {
|
||||
if (_title.isEmpty() && _description.isEmpty()) {
|
||||
if (_title.isEmpty() && _description.isEmpty() && textFloatsAroundInfo) {
|
||||
accumulate_max(maxWidth, _siteNameWidth + _parent->skipBlockWidth());
|
||||
} else {
|
||||
accumulate_max(maxWidth, _siteNameWidth + articlePhotoMaxWidth);
|
||||
|
@ -441,7 +449,7 @@ void HistoryWebPage::draw(Painter &p, const QRect &r, TextSelection selection, T
|
|||
if (_siteNameWidth) {
|
||||
p.setFont(st::webPageTitleFont);
|
||||
p.setPen(semibold);
|
||||
p.drawTextLeft(padding.left(), tshift, width(), (paintw >= _siteNameWidth) ? _data->siteName : st::webPageTitleFont->elided(_data->siteName, paintw));
|
||||
p.drawTextLeft(padding.left(), tshift, width(), (paintw >= _siteNameWidth) ? displayedSiteName() : st::webPageTitleFont->elided(displayedSiteName(), paintw));
|
||||
tshift += lineHeight;
|
||||
}
|
||||
if (_titleLines) {
|
||||
|
@ -595,19 +603,7 @@ TextState HistoryWebPage::textState(QPoint point, StateRequest request) const {
|
|||
auto attachTop = tshift - bubble.top();
|
||||
if (rtl()) attachLeft = width() - attachLeft - _attach->width();
|
||||
result = _attach->textState(point - QPoint(attachLeft, attachTop), request);
|
||||
|
||||
if (result.link && !_data->document && _data->photo && _collage.empty() && _attach->isReadyForOpen()) {
|
||||
if (_data->type == WebPageType::Profile
|
||||
|| _data->type == WebPageType::Video) {
|
||||
result.link = _openl;
|
||||
} else if (_data->type == WebPageType::Photo
|
||||
|| _data->siteName == qstr("Twitter")
|
||||
|| _data->siteName == qstr("Facebook")) {
|
||||
// leave photo link
|
||||
} else {
|
||||
result.link = _openl;
|
||||
}
|
||||
}
|
||||
result.link = replaceAttachLink(result.link);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -615,6 +611,30 @@ TextState HistoryWebPage::textState(QPoint point, StateRequest request) const {
|
|||
return result;
|
||||
}
|
||||
|
||||
ClickHandlerPtr HistoryWebPage::replaceAttachLink(
|
||||
const ClickHandlerPtr &link) const {
|
||||
if (!link || !_attach->isReadyForOpen() || !_collage.empty()) {
|
||||
return link;
|
||||
}
|
||||
if (_data->document) {
|
||||
if (_data->document->isWallPaper()) {
|
||||
return _openl;
|
||||
}
|
||||
} else if (_data->photo) {
|
||||
if (_data->type == WebPageType::Profile
|
||||
|| _data->type == WebPageType::Video) {
|
||||
return _openl;
|
||||
} else if (_data->type == WebPageType::Photo
|
||||
|| _data->siteName == qstr("Twitter")
|
||||
|| _data->siteName == qstr("Facebook")) {
|
||||
// leave photo link
|
||||
} else {
|
||||
return _openl;
|
||||
}
|
||||
}
|
||||
return link;
|
||||
}
|
||||
|
||||
TextSelection HistoryWebPage::adjustSelection(TextSelection selection, TextSelectType type) const {
|
||||
if (!_descriptionLines || selection.to <= _title.length()) {
|
||||
return _title.adjustSelection(selection, type);
|
||||
|
@ -639,6 +659,12 @@ void HistoryWebPage::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool p
|
|||
}
|
||||
}
|
||||
|
||||
bool HistoryWebPage::enforceBubbleWidth() const {
|
||||
return (_attach != nullptr)
|
||||
&& (_data->document != nullptr)
|
||||
&& _data->document->isWallPaper();
|
||||
}
|
||||
|
||||
void HistoryWebPage::playAnimation(bool autoplay) {
|
||||
if (_attach) {
|
||||
if (autoplay) {
|
||||
|
@ -699,6 +725,12 @@ int HistoryWebPage::bottomInfoPadding() const {
|
|||
return result;
|
||||
}
|
||||
|
||||
QString HistoryWebPage::displayedSiteName() const {
|
||||
return (_data->document && _data->document->isWallPaper())
|
||||
? lang(lng_media_chat_background)
|
||||
: _data->siteName;
|
||||
}
|
||||
|
||||
HistoryWebPage::~HistoryWebPage() {
|
||||
history()->owner().unregisterWebPageView(_data, _parent);
|
||||
}
|
||||
|
|
|
@ -74,6 +74,7 @@ public:
|
|||
bool allowsFastShare() const override {
|
||||
return true;
|
||||
}
|
||||
bool enforceBubbleWidth() const override;
|
||||
|
||||
HistoryMedia *attach() const {
|
||||
return _attach.get();
|
||||
|
@ -92,6 +93,9 @@ private:
|
|||
int bottomInfoPadding() const;
|
||||
bool isLogEntryOriginal() const;
|
||||
|
||||
QString displayedSiteName() const;
|
||||
ClickHandlerPtr replaceAttachLink(const ClickHandlerPtr &link) const;
|
||||
|
||||
not_null<WebPageData*> _data;
|
||||
std::vector<std::unique_ptr<Data::Media>> _collage;
|
||||
ClickHandlerPtr _openl;
|
||||
|
|
|
@ -272,7 +272,15 @@ QSize Message::performCountOptimalSize() {
|
|||
}
|
||||
if (mediaDisplayed) {
|
||||
// Parts don't participate in maxWidth() in case of media message.
|
||||
accumulate_max(maxWidth, media->maxWidth());
|
||||
if (media->enforceBubbleWidth()) {
|
||||
maxWidth = media->maxWidth();
|
||||
if (hasVisibleText() && maxWidth < plainMaxWidth()) {
|
||||
minHeight -= item->_text.minHeight();
|
||||
minHeight += item->_text.countHeight(maxWidth - st::msgPadding.left() - st::msgPadding.right());
|
||||
}
|
||||
} else {
|
||||
accumulate_max(maxWidth, media->maxWidth());
|
||||
}
|
||||
minHeight += media->minHeight();
|
||||
} else {
|
||||
// Count parts in maxWidth(), don't count them in minHeight().
|
||||
|
@ -1558,7 +1566,8 @@ QRect Message::countGeometry() const {
|
|||
accumulate_min(contentWidth, st::msgMaxWidth);
|
||||
if (mediaWidth < contentWidth) {
|
||||
const auto textualWidth = plainMaxWidth();
|
||||
if (mediaWidth < textualWidth) {
|
||||
if (mediaWidth < textualWidth
|
||||
&& (!media || !media->enforceBubbleWidth())) {
|
||||
accumulate_min(contentWidth, textualWidth);
|
||||
} else {
|
||||
contentWidth = mediaWidth;
|
||||
|
@ -1601,7 +1610,8 @@ int Message::resizeContentGetHeight(int newWidth) {
|
|||
media->resizeGetHeight(contentWidth);
|
||||
if (media->width() < contentWidth) {
|
||||
const auto textualWidth = plainMaxWidth();
|
||||
if (media->width() < textualWidth) {
|
||||
if (media->width() < textualWidth
|
||||
&& !media->enforceBubbleWidth()) {
|
||||
accumulate_min(contentWidth, textualWidth);
|
||||
} else {
|
||||
contentWidth = media->width();
|
||||
|
|
|
@ -52,7 +52,7 @@ constexpr auto kSavedBackgroundFormat = QImage::Format_ARGB32_Premultiplied;
|
|||
|
||||
constexpr auto kWallPaperLegacySerializeTagId = int32(-111);
|
||||
constexpr auto kWallPaperSerializeTagId = int32(-112);
|
||||
constexpr auto kWallPaperSidesLimit = 10000;
|
||||
constexpr auto kWallPaperSidesLimit = 10'000;
|
||||
|
||||
constexpr auto kSinglePeerTypeUser = qint32(1);
|
||||
constexpr auto kSinglePeerTypeChat = qint32(2);
|
||||
|
|
|
@ -325,8 +325,10 @@ QImage prepare(QImage img, int w, int h, Images::Options options, int outerw, in
|
|||
}
|
||||
{
|
||||
QPainter p(&result);
|
||||
if (w < outerw || h < outerh) {
|
||||
p.fillRect(0, 0, result.width(), result.height(), st::imageBg);
|
||||
if (!(options & Images::Option::TransparentBackground)) {
|
||||
if (w < outerw || h < outerh) {
|
||||
p.fillRect(0, 0, result.width(), result.height(), st::imageBg);
|
||||
}
|
||||
}
|
||||
p.drawImage((result.width() - img.width()) / (2 * cIntRetinaFactor()), (result.height() - img.height()) / (2 * cIntRetinaFactor()), img);
|
||||
}
|
||||
|
|
|
@ -104,63 +104,6 @@ std::optional<QColor> ColorFromString(const QString &string) {
|
|||
255);
|
||||
}
|
||||
|
||||
QImage PreparePatternImage(QImage image, QColor bg, QColor fg, int intensity) {
|
||||
if (image.format() != QImage::Format_ARGB32_Premultiplied) {
|
||||
image = std::move(image).convertToFormat(
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
}
|
||||
// Similar to ColorizePattern.
|
||||
// But here we set bg to all 'alpha=0' pixels and fg to opaque ones.
|
||||
|
||||
const auto width = image.width();
|
||||
const auto height = image.height();
|
||||
const auto alpha = anim::interpolate(
|
||||
0,
|
||||
255,
|
||||
fg.alphaF() * std::clamp(intensity / 100., 0., 1.));
|
||||
if (!alpha) {
|
||||
image.fill(bg);
|
||||
return image;
|
||||
}
|
||||
fg.setAlpha(255);
|
||||
const auto patternBg = anim::shifted(bg);
|
||||
const auto patternFg = anim::shifted(fg);
|
||||
|
||||
const auto resultBytesPerPixel = (image.depth() >> 3);
|
||||
constexpr auto resultIntsPerPixel = 1;
|
||||
const auto resultIntsPerLine = (image.bytesPerLine() >> 2);
|
||||
const auto resultIntsAdded = resultIntsPerLine - width * resultIntsPerPixel;
|
||||
auto resultInts = reinterpret_cast<uint32*>(image.bits());
|
||||
Assert(resultIntsAdded >= 0);
|
||||
Assert(image.depth() == static_cast<int>((resultIntsPerPixel * sizeof(uint32)) << 3));
|
||||
Assert(image.bytesPerLine() == (resultIntsPerLine << 2));
|
||||
|
||||
const auto maskBytesPerPixel = (image.depth() >> 3);
|
||||
const auto maskBytesPerLine = image.bytesPerLine();
|
||||
const auto maskBytesAdded = maskBytesPerLine - width * maskBytesPerPixel;
|
||||
|
||||
// We want to read the last byte of four available.
|
||||
// This is the difference with style::colorizeImage.
|
||||
auto maskBytes = image.constBits() + (maskBytesPerPixel - 1);
|
||||
Assert(maskBytesAdded >= 0);
|
||||
Assert(image.depth() == (maskBytesPerPixel << 3));
|
||||
for (auto y = 0; y != height; ++y) {
|
||||
for (auto x = 0; x != width; ++x) {
|
||||
const auto maskOpacity = static_cast<anim::ShiftedMultiplier>(
|
||||
*maskBytes) + 1;
|
||||
const auto fgOpacity = (maskOpacity * alpha) >> 8;
|
||||
const auto bgOpacity = 256 - fgOpacity;
|
||||
*resultInts = anim::unshifted(
|
||||
patternBg * bgOpacity + patternFg * fgOpacity);
|
||||
maskBytes += maskBytesPerPixel;
|
||||
resultInts += resultIntsPerPixel;
|
||||
}
|
||||
maskBytes += maskBytesAdded;
|
||||
resultInts += resultIntsAdded;
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
WallPaper::WallPaper(WallPaperId id) : _id(id) {
|
||||
|
@ -258,7 +201,7 @@ WallPaper WallPaper::withUrlParams(
|
|||
if (const auto string = params.value("intensity"); !string.isEmpty()) {
|
||||
auto ok = false;
|
||||
const auto intensity = string.toInt(&ok);
|
||||
if (ok && base::in_range(intensity, 0, 100)) {
|
||||
if (ok && base::in_range(intensity, 0, 101)) {
|
||||
result._intensity = intensity;
|
||||
}
|
||||
}
|
||||
|
@ -442,6 +385,81 @@ bool IsDefaultWallPaper(const WallPaper &paper) {
|
|||
return (paper.id() == kDefaultBackground);
|
||||
}
|
||||
|
||||
QColor PatternColor(QColor background) {
|
||||
const auto hue = background.hueF();
|
||||
const auto saturation = background.saturationF();
|
||||
const auto value = background.valueF();
|
||||
return QColor::fromHsvF(
|
||||
hue,
|
||||
std::min(1.0, saturation + 0.05 + 0.1 * (1. - saturation)),
|
||||
(value > 0.5
|
||||
? std::max(0., value * 0.65)
|
||||
: std::max(0., std::min(1., 1. - value * 0.65))),
|
||||
0.4
|
||||
).toRgb();
|
||||
}
|
||||
|
||||
QImage PreparePatternImage(
|
||||
QImage image,
|
||||
QColor bg,
|
||||
QColor fg,
|
||||
int intensity) {
|
||||
if (image.format() != QImage::Format_ARGB32_Premultiplied) {
|
||||
image = std::move(image).convertToFormat(
|
||||
QImage::Format_ARGB32_Premultiplied);
|
||||
}
|
||||
// Similar to ColorizePattern.
|
||||
// But here we set bg to all 'alpha=0' pixels and fg to opaque ones.
|
||||
|
||||
const auto width = image.width();
|
||||
const auto height = image.height();
|
||||
const auto alpha = anim::interpolate(
|
||||
0,
|
||||
255,
|
||||
fg.alphaF() * std::clamp(intensity / 100., 0., 1.));
|
||||
if (!alpha) {
|
||||
image.fill(bg);
|
||||
return image;
|
||||
}
|
||||
fg.setAlpha(255);
|
||||
const auto patternBg = anim::shifted(bg);
|
||||
const auto patternFg = anim::shifted(fg);
|
||||
|
||||
const auto resultBytesPerPixel = (image.depth() >> 3);
|
||||
constexpr auto resultIntsPerPixel = 1;
|
||||
const auto resultIntsPerLine = (image.bytesPerLine() >> 2);
|
||||
const auto resultIntsAdded = resultIntsPerLine - width * resultIntsPerPixel;
|
||||
auto resultInts = reinterpret_cast<uint32*>(image.bits());
|
||||
Assert(resultIntsAdded >= 0);
|
||||
Assert(image.depth() == static_cast<int>((resultIntsPerPixel * sizeof(uint32)) << 3));
|
||||
Assert(image.bytesPerLine() == (resultIntsPerLine << 2));
|
||||
|
||||
const auto maskBytesPerPixel = (image.depth() >> 3);
|
||||
const auto maskBytesPerLine = image.bytesPerLine();
|
||||
const auto maskBytesAdded = maskBytesPerLine - width * maskBytesPerPixel;
|
||||
|
||||
// We want to read the last byte of four available.
|
||||
// This is the difference with style::colorizeImage.
|
||||
auto maskBytes = image.constBits() + (maskBytesPerPixel - 1);
|
||||
Assert(maskBytesAdded >= 0);
|
||||
Assert(image.depth() == (maskBytesPerPixel << 3));
|
||||
for (auto y = 0; y != height; ++y) {
|
||||
for (auto x = 0; x != width; ++x) {
|
||||
const auto maskOpacity = static_cast<anim::ShiftedMultiplier>(
|
||||
*maskBytes) + 1;
|
||||
const auto fgOpacity = (maskOpacity * alpha) >> 8;
|
||||
const auto bgOpacity = 256 - fgOpacity;
|
||||
*resultInts = anim::unshifted(
|
||||
patternBg * bgOpacity + patternFg * fgOpacity);
|
||||
maskBytes += maskBytesPerPixel;
|
||||
resultInts += resultIntsPerPixel;
|
||||
}
|
||||
maskBytes += maskBytesAdded;
|
||||
resultInts += resultIntsAdded;
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
namespace details {
|
||||
|
||||
WallPaper UninitializedWallPaper() {
|
||||
|
@ -907,7 +925,7 @@ void ChatBackground::set(const Data::WallPaper &paper, QImage image) {
|
|||
Data::PreparePatternImage(
|
||||
image,
|
||||
*fill,
|
||||
PatternColor(*fill),
|
||||
Data::PatternColor(*fill),
|
||||
_paper.patternIntensity()));
|
||||
setPreparedImage(std::move(image), std::move(prepared));
|
||||
} else {
|
||||
|
@ -1592,19 +1610,5 @@ bool ReadPaletteValues(const QByteArray &content, Fn<bool(QLatin1String name, QL
|
|||
return true;
|
||||
}
|
||||
|
||||
QColor PatternColor(QColor background) {
|
||||
const auto hue = background.hueF();
|
||||
const auto saturation = background.saturationF();
|
||||
const auto value = background.valueF();
|
||||
return QColor::fromHsvF(
|
||||
hue,
|
||||
std::min(1.0, saturation + 0.05 + 0.1 * (1. - saturation)),
|
||||
(value > 0.5
|
||||
? std::max(0., value * 0.65)
|
||||
: std::max(0., std::min(1., 1. - value * 0.65))),
|
||||
0.4
|
||||
).toRgb();
|
||||
}
|
||||
|
||||
} // namespace Theme
|
||||
} // namespace Window
|
||||
|
|
|
@ -81,6 +81,13 @@ private:
|
|||
[[nodiscard]] WallPaper DefaultWallPaper();
|
||||
[[nodiscard]] bool IsDefaultWallPaper(const WallPaper &paper);
|
||||
|
||||
QColor PatternColor(QColor background);
|
||||
QImage PreparePatternImage(
|
||||
QImage image,
|
||||
QColor bg,
|
||||
QColor fg,
|
||||
int intensity);
|
||||
|
||||
namespace details {
|
||||
|
||||
[[nodiscard]] WallPaper UninitializedWallPaper();
|
||||
|
@ -147,8 +154,6 @@ void Revert();
|
|||
bool LoadFromFile(const QString &file, Instance *out, QByteArray *outContent);
|
||||
bool IsPaletteTestingPath(const QString &path);
|
||||
|
||||
QColor PatternColor(QColor background);
|
||||
|
||||
struct BackgroundUpdate {
|
||||
enum class Type {
|
||||
New,
|
||||
|
|
|
@ -275,6 +275,8 @@
|
|||
<(src_loc)/history/media/history_media_sticker.cpp
|
||||
<(src_loc)/history/media/history_media_video.h
|
||||
<(src_loc)/history/media/history_media_video.cpp
|
||||
<(src_loc)/history/media/history_media_wall_paper.h
|
||||
<(src_loc)/history/media/history_media_wall_paper.cpp
|
||||
<(src_loc)/history/media/history_media_web_page.h
|
||||
<(src_loc)/history/media/history_media_web_page.cpp
|
||||
<(src_loc)/history/view/history_view_context_menu.cpp
|
||||
|
|
Loading…
Add table
Reference in a new issue