Moved History[Media] classes to history_media_types module.

This commit is contained in:
John Preston 2016-09-28 13:15:03 +03:00
parent d277b0d4bb
commit 538ffb9727
33 changed files with 1704 additions and 1340 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 177 KiB

After

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 241 KiB

After

Width:  |  Height:  |  Size: 241 KiB

View File

@ -1232,7 +1232,7 @@ introErrLabelTextStyle: textStyle(defaultTextStyle) {
mediaPadding: margins(0px, 0px, 0px, 0px);//1px, 1px, 1px, 1px);//2px, 2px, 2px, 2px);
mediaCaptionSkip: 5px;
mediaHeaderSkip: 5px;
mediaInBubbleSkip: 5px;
mediaThumbSize: 48px;
mediaNameTop: 3px;
mediaDetailsShift: 3px;
@ -2285,7 +2285,6 @@ webPageLeft: 10px;
webPageBar: 2px;
webPageTitleFont: semiboldFont;
webPageDescriptionFont: normalFont;
webPagePhotoSkip: 5px;
webPagePhotoSize: 100px;
webPagePhotoDelta: 8px;

View File

@ -31,6 +31,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "data/data_abstract_structure.h"
#include "history/history_service_layout.h"
#include "history/history_location_manager.h"
#include "history/history_media_types.h"
#include "media/media_audio.h"
#include "inline_bots/inline_bot_layout_item.h"
#include "application.h"
@ -1523,7 +1524,7 @@ namespace {
}
GameData *feedGame(const MTPDgame &game, GameData *convert) {
return App::gameSet(game.vid.v, convert, game.vaccess_hash.v, qs(game.vshort_name), qs(game.vtitle), qs(game.vdescription), qs(game.vurl), App::feedPhoto(game.vphoto), game.has_document() ? App::feedDocument(game.vdocument) : nullptr);
return App::gameSet(game.vid.v, convert, game.vaccess_hash.v, qs(game.vshort_name), qs(game.vtitle), qs(game.vdescription), App::feedPhoto(game.vphoto), game.has_document() ? App::feedDocument(game.vdocument) : nullptr);
}
UserData *curUser() {
@ -1847,7 +1848,7 @@ namespace {
return i.value();
}
GameData *gameSet(const GameId &game, GameData *convert, const uint64 &accessHash, const QString &shortName, const QString &title, const QString &description, const QString &url, PhotoData *photo, DocumentData *document) {
GameData *gameSet(const GameId &game, GameData *convert, const uint64 &accessHash, const QString &shortName, const QString &title, const QString &description, PhotoData *photo, DocumentData *document) {
if (convert) {
if (convert->id != game) {
auto i = gamesData.find(convert->id);
@ -1856,12 +1857,11 @@ namespace {
}
convert->id = game;
}
if (convert->url.isEmpty() && !url.isEmpty()) {
if (convert->shortName.isEmpty() && !shortName.isEmpty()) {
convert->accessHash = accessHash;
convert->shortName = shortName;
convert->title = title;
convert->description = description;
convert->url = url;
convert->photo = photo;
convert->document = document;
if (App::main()) App::main()->gameUpdated(convert);
@ -1873,18 +1873,17 @@ namespace {
if (convert) {
result = convert;
} else {
result = new GameData(game, accessHash, shortName, title, description, url, photo, document);
result = new GameData(game, accessHash, shortName, title, description, photo, document);
}
gamesData.insert(game, result);
} else {
result = i.value();
if (result != convert) {
if (result->url.isEmpty() && !url.isEmpty()) {
if (result->shortName.isEmpty() && !shortName.isEmpty()) {
result->accessHash = accessHash;
result->shortName = shortName;
result->title = title;
result->description = description;
result->url = url;
result->photo = photo;
result->document = document;
if (App::main()) App::main()->gameUpdated(result);

View File

@ -44,6 +44,9 @@ using GifItems = QHash<Media::Clip::Reader*, HistoryItem*>;
using PhotosData = QHash<PhotoId, PhotoData*>;
using DocumentsData = QHash<DocumentId, DocumentData*>;
struct LocationCoords;
struct LocationData;
namespace App {
AppClass *app();
MainWindow *wnd();
@ -154,7 +157,7 @@ namespace App {
WebPageData *webPage(const WebPageId &webPage);
WebPageData *webPageSet(const WebPageId &webPage, WebPageData *convert, const QString &type, const QString &url, const QString &displayUrl, const QString &siteName, const QString &title, const QString &description, PhotoData *photo, DocumentData *doc, int32 duration, const QString &author, int32 pendingTill);
GameData *game(const GameId &game);
GameData *gameSet(const GameId &game, GameData *convert, const uint64 &accessHash, const QString &shortName, const QString &title, const QString &description, const QString &url, PhotoData *photo, DocumentData *doc);
GameData *gameSet(const GameId &game, GameData *convert, const uint64 &accessHash, const QString &shortName, const QString &title, const QString &description, PhotoData *photo, DocumentData *doc);
LocationData *location(const LocationCoords &coords);
void forgetMedia();

View File

@ -25,6 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "localstorage.h"
#include "mainwidget.h"
#include "photosendbox.h"
#include "history/history_media_types.h"
PhotoSendBox::PhotoSendBox(const FileLoadResultPtr &file) : AbstractBox(st::boxWideWidth)
, _file(file)

View File

@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "stdafx.h"
#include "history.h"
#include "history/history_media_types.h"
#include "dialogs/dialogs_indexed_list.h"
#include "styles/style_dialogs.h"
#include "data/data_drafts.h"

View File

@ -28,84 +28,64 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "styles/style_dialogs.h"
#include "fileuploader.h"
class ReplyMarkupClickHandler : public LeftButtonClickHandler {
public:
ReplyMarkupClickHandler(const HistoryItem *item, int row, int col) : _itemId(item->fullId()), _row(row), _col(col) {
}
ReplyMarkupClickHandler::ReplyMarkupClickHandler(const HistoryItem *item, int row, int col)
: _itemId(item->fullId())
, _row(row)
, _col(col) {
}
QString tooltip() const override {
return _fullDisplayed ? QString() : buttonText();
// Copy to clipboard support.
void ReplyMarkupClickHandler::copyToClipboard() const {
if (auto button = getButton()) {
if (button->type == HistoryMessageReplyMarkup::Button::Type::Url) {
auto url = QString::fromUtf8(button->data);
if (!url.isEmpty()) {
QApplication::clipboard()->setText(url);
}
}
}
}
void setFullDisplayed(bool full) {
_fullDisplayed = full;
QString ReplyMarkupClickHandler::copyToClipboardContextItemText() const {
if (auto button = getButton()) {
if (button->type == HistoryMessageReplyMarkup::Button::Type::Url) {
return lang(lng_context_copy_link);
}
}
return QString();
}
// Copy to clipboard support.
void copyToClipboard() const override {
if (auto button = getButton()) {
if (button->type == HistoryMessageReplyMarkup::Button::Type::Url) {
auto url = QString::fromUtf8(button->data);
if (!url.isEmpty()) {
QApplication::clipboard()->setText(url);
// Finds the corresponding button in the items markup struct.
// If the button is not found it returns nullptr.
// Note: it is possible that we will point to the different button
// than the one was used when constructing the handler, but not a big deal.
const HistoryMessageReplyMarkup::Button *ReplyMarkupClickHandler::getButton() const {
if (auto item = App::histItemById(_itemId)) {
if (auto markup = item->Get<HistoryMessageReplyMarkup>()) {
if (_row < markup->rows.size()) {
auto &row = markup->rows.at(_row);
if (_col < row.size()) {
return &row.at(_col);
}
}
}
}
QString copyToClipboardContextItemText() const override {
if (auto button = getButton()) {
if (button->type == HistoryMessageReplyMarkup::Button::Type::Url) {
return lang(lng_context_copy_link);
}
}
return QString();
return nullptr;
}
void ReplyMarkupClickHandler::onClickImpl() const {
if (auto item = App::histItemById(_itemId)) {
App::activateBotCommand(item, _row, _col);
}
}
// Finds the corresponding button in the items markup struct.
// If the button is not found it returns nullptr.
// Note: it is possible that we will point to the different button
// than the one was used when constructing the handler, but not a big deal.
const HistoryMessageReplyMarkup::Button *getButton() const {
if (auto item = App::histItemById(_itemId)) {
if (auto markup = item->Get<HistoryMessageReplyMarkup>()) {
if (_row < markup->rows.size()) {
auto &row = markup->rows.at(_row);
if (_col < row.size()) {
return &row.at(_col);
}
}
}
}
return nullptr;
// Returns the full text of the corresponding button.
QString ReplyMarkupClickHandler::buttonText() const {
if (auto button = getButton()) {
return button->text;
}
// We hold only FullMsgId, not HistoryItem*, because all click handlers
// are activated async and the item may be already destroyed.
void setMessageId(const FullMsgId &msgId) {
_itemId = msgId;
}
protected:
void onClickImpl() const override {
if (auto item = App::histItemById(_itemId)) {
App::activateBotCommand(item, _row, _col);
}
}
private:
FullMsgId _itemId;
int _row, _col;
bool _fullDisplayed = true;
// Returns the full text of the corresponding button.
QString buttonText() const {
if (auto button = getButton()) {
return button->text;
}
return QString();
}
};
return QString();
}
ReplyKeyboard::ReplyKeyboard(const HistoryItem *item, StylePtr &&s)
: _item(item)

View File

@ -230,7 +230,47 @@ private:
};
class ReplyMarkupClickHandler;
class ReplyMarkupClickHandler : public LeftButtonClickHandler {
public:
ReplyMarkupClickHandler(const HistoryItem *item, int row, int col);
QString tooltip() const override {
return _fullDisplayed ? QString() : buttonText();
}
void setFullDisplayed(bool full) {
_fullDisplayed = full;
}
// Copy to clipboard support.
void copyToClipboard() const override;
QString copyToClipboardContextItemText() const override;
// Finds the corresponding button in the items markup struct.
// If the button is not found it returns nullptr.
// Note: it is possible that we will point to the different button
// than the one was used when constructing the handler, but not a big deal.
const HistoryMessageReplyMarkup::Button *getButton() const;
// We hold only FullMsgId, not HistoryItem*, because all click handlers
// are activated async and the item may be already destroyed.
void setMessageId(const FullMsgId &msgId) {
_itemId = msgId;
}
protected:
void onClickImpl() const override;
private:
FullMsgId _itemId;
int _row, _col;
bool _fullDisplayed = true;
// Returns the full text of the corresponding button.
QString buttonText() const;
};
class ReplyKeyboard {
private:
struct Button;

View File

@ -20,38 +20,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
void historyInitMedia();
class RadialAnimation {
public:
RadialAnimation(AnimationCallbacks &&callbacks);
float64 opacity() const {
return _opacity;
}
bool animating() const {
return _animation.animating();
}
void start(float64 prg);
void update(float64 prg, bool finished, uint64 ms);
void stop();
void step(uint64 ms);
void step() {
step(getms());
}
void draw(Painter &p, const QRect &inner, int32 thickness, const style::color &color);
private:
uint64 _firstStart = 0;
uint64 _lastStart = 0;
uint64 _lastTime = 0;
float64 _opacity = 0.;
anim::ivalue a_arcEnd, a_arcStart;
Animation _animation;
enum class MediaInBubbleState {
None,
Top,
Middle,
Bottom,
};
class HistoryMedia : public HistoryElement {
@ -67,7 +40,10 @@ public:
// Returns text with link-start and link-end commands for service-color highlighting.
// Example: "[link1-start]You:[link1-end] [link1-start]Photo,[link1-end] caption text"
virtual QString inDialogsText() const;
virtual QString inDialogsText() const {
auto result = notificationText();
return result.isEmpty() ? QString() : textcmdLink(1, textClean(result));
}
virtual TextWithEntities selectedText(TextSelection selection) const = 0;
bool hasPoint(int x, int y) const {
@ -177,881 +153,22 @@ public:
return _width;
}
void setInBubbleState(MediaInBubbleState state) {
_inBubbleState = state;
}
MediaInBubbleState inBubbleState() const {
return _inBubbleState;
}
bool isBubbleTop() const {
return (_inBubbleState == MediaInBubbleState::Top) || (_inBubbleState == MediaInBubbleState::None);
}
bool isBubbleBottom() const {
return (_inBubbleState == MediaInBubbleState::Bottom) || (_inBubbleState == MediaInBubbleState::None);
}
protected:
HistoryItem *_parent;
int _width = 0;
};
class HistoryFileMedia : public HistoryMedia {
public:
using HistoryMedia::HistoryMedia;
bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override {
return p == _openl || p == _savel || p == _cancell;
}
bool dragItemByHandler(const ClickHandlerPtr &p) const override {
return p == _openl || p == _savel || p == _cancell;
}
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override;
~HistoryFileMedia();
protected:
ClickHandlerPtr _openl, _savel, _cancell;
void setLinks(ClickHandlerPtr &&openl, ClickHandlerPtr &&savel, ClickHandlerPtr &&cancell);
void setDocumentLinks(DocumentData *document, bool inlinegif = false) {
ClickHandlerPtr open, save;
if (inlinegif) {
open.reset(new GifOpenClickHandler(document));
} else {
open.reset(new DocumentOpenClickHandler(document));
}
if (inlinegif) {
save.reset(new GifOpenClickHandler(document));
} else if (document->voice()) {
save.reset(new DocumentOpenClickHandler(document));
} else {
save.reset(new DocumentSaveClickHandler(document));
}
setLinks(std_::move(open), std_::move(save), MakeShared<DocumentCancelClickHandler>(document));
}
// >= 0 will contain download / upload string, _statusSize = loaded bytes
// < 0 will contain played string, _statusSize = -(seconds + 1) played
// 0x7FFFFFF0 will contain status for not yet downloaded file
// 0x7FFFFFF1 will contain status for already downloaded file
// 0x7FFFFFF2 will contain status for failed to download / upload file
mutable int32 _statusSize;
mutable QString _statusText;
// duration = -1 - no duration, duration = -2 - "GIF" duration
void setStatusSize(int32 newSize, int32 fullSize, int32 duration, qint64 realDuration) const;
void step_thumbOver(float64 ms, bool timer);
void step_radial(uint64 ms, bool timer);
void ensureAnimation() const;
void checkAnimationFinished();
bool isRadialAnimation(uint64 ms) const {
if (!_animation || !_animation->radial.animating()) return false;
_animation->radial.step(ms);
return _animation && _animation->radial.animating();
}
bool isThumbAnimation(uint64 ms) const {
if (!_animation || !_animation->_a_thumbOver.animating()) return false;
_animation->_a_thumbOver.step(ms);
return _animation && _animation->_a_thumbOver.animating();
}
virtual float64 dataProgress() const = 0;
virtual bool dataFinished() const = 0;
virtual bool dataLoaded() const = 0;
struct AnimationData {
AnimationData(AnimationCallbacks &&thumbOverCallbacks, AnimationCallbacks &&radialCallbacks) : a_thumbOver(0, 0)
, _a_thumbOver(std_::move(thumbOverCallbacks))
, radial(std_::move(radialCallbacks)) {
}
anim::fvalue a_thumbOver;
Animation _a_thumbOver;
RadialAnimation radial;
};
mutable AnimationData *_animation = nullptr;
};
class HistoryPhoto : public HistoryFileMedia {
public:
HistoryPhoto(HistoryItem *parent, PhotoData *photo, const QString &caption);
HistoryPhoto(HistoryItem *parent, PeerData *chat, const MTPDphoto &photo, int width);
HistoryPhoto(HistoryItem *parent, const HistoryPhoto &other);
void init();
HistoryMediaType type() const override {
return MediaTypePhoto;
}
HistoryPhoto *clone(HistoryItem *newParent) const override {
return new HistoryPhoto(newParent, *this);
}
void initDimensions() override;
int resizeGetHeight(int width) override;
void draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const override;
HistoryTextState getState(int x, int y, HistoryStateRequest request) const override;
TextSelection adjustSelection(TextSelection selection, TextSelectType type) const override {
return _caption.adjustSelection(selection, type);
}
bool hasTextForCopy() const override {
return !_caption.isEmpty();
}
QString notificationText() const override;
QString inDialogsText() const override;
TextWithEntities selectedText(TextSelection selection) const override;
PhotoData *photo() const {
return _data;
}
void updateSentMedia(const MTPMessageMedia &media) override;
bool needReSetInlineResultMedia(const MTPMessageMedia &media) override;
void attachToParent() override;
void detachFromParent() override;
bool hasReplyPreview() const override {
return !_data->thumb->isNull();
}
ImagePtr replyPreview() override;
TextWithEntities getCaption() const override {
return _caption.originalTextWithEntities();
}
bool needsBubble() const override {
if (!_caption.isEmpty()) {
return true;
}
if (_parent->viaBot()) {
return true;
}
return (_parent->Has<HistoryMessageForwarded>() || _parent->Has<HistoryMessageReply>());
}
bool customInfoLayout() const override {
return _caption.isEmpty();
}
bool hideFromName() const override {
return true;
}
protected:
float64 dataProgress() const override {
return _data->progress();
}
bool dataFinished() const override {
return !_data->loading() && !_data->uploading();
}
bool dataLoaded() const override {
return _data->loaded();
}
private:
PhotoData *_data;
int16 _pixw = 1;
int16 _pixh = 1;
Text _caption;
};
class HistoryVideo : public HistoryFileMedia {
public:
HistoryVideo(HistoryItem *parent, DocumentData *document, const QString &caption);
HistoryVideo(HistoryItem *parent, const HistoryVideo &other);
HistoryMediaType type() const override {
return MediaTypeVideo;
}
HistoryVideo *clone(HistoryItem *newParent) const override {
return new HistoryVideo(newParent, *this);
}
void initDimensions() override;
int resizeGetHeight(int width) override;
void draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const override;
HistoryTextState getState(int x, int y, HistoryStateRequest request) const override;
TextSelection adjustSelection(TextSelection selection, TextSelectType type) const override {
return _caption.adjustSelection(selection, type);
}
bool hasTextForCopy() const override {
return !_caption.isEmpty();
}
QString notificationText() const override;
QString inDialogsText() const override;
TextWithEntities selectedText(TextSelection selection) const override;
DocumentData *getDocument() override {
return _data;
}
bool uploading() const override {
return _data->uploading();
}
void attachToParent() override;
void detachFromParent() override;
bool needReSetInlineResultMedia(const MTPMessageMedia &media) override;
bool hasReplyPreview() const override {
return !_data->thumb->isNull();
}
ImagePtr replyPreview() override;
TextWithEntities getCaption() const override {
return _caption.originalTextWithEntities();
}
bool needsBubble() const override {
if (!_caption.isEmpty()) {
return true;
}
if (_parent->viaBot()) {
return true;
}
return (_parent->Has<HistoryMessageForwarded>() || _parent->Has<HistoryMessageReply>());
}
bool customInfoLayout() const override {
return _caption.isEmpty();
}
bool hideFromName() const override {
return true;
}
protected:
float64 dataProgress() const override {
return _data->progress();
}
bool dataFinished() const override {
return !_data->loading() && !_data->uploading();
}
bool dataLoaded() const override {
return _data->loaded();
}
private:
DocumentData *_data;
int32 _thumbw;
Text _caption;
void setStatusSize(int32 newSize) const;
void updateStatusText() const;
};
struct HistoryDocumentThumbed : public BaseComponent<HistoryDocumentThumbed> {
ClickHandlerPtr _linksavel, _linkcancell;
int _thumbw = 0;
mutable int _linkw = 0;
mutable QString _link;
};
struct HistoryDocumentCaptioned : public BaseComponent<HistoryDocumentCaptioned> {
Text _caption = { int(st::msgFileMinWidth) - st::msgPadding.left() - st::msgPadding.right() };
};
struct HistoryDocumentNamed : public BaseComponent<HistoryDocumentNamed> {
QString _name;
int _namew = 0;
};
class HistoryDocument;
struct HistoryDocumentVoicePlayback {
HistoryDocumentVoicePlayback(const HistoryDocument *that);
int32 _position;
anim::fvalue a_progress;
Animation _a_progress;
};
struct HistoryDocumentVoice : public BaseComponent<HistoryDocumentVoice> {
HistoryDocumentVoice &operator=(HistoryDocumentVoice &&other) {
std::swap(_playback, other._playback);
return *this;
}
~HistoryDocumentVoice() {
deleteAndMark(_playback);
}
void ensurePlayback(const HistoryDocument *interfaces) const;
void checkPlaybackFinished() const;
mutable HistoryDocumentVoicePlayback *_playback = nullptr;
};
class HistoryDocument : public HistoryFileMedia, public Composer {
public:
HistoryDocument(HistoryItem *parent, DocumentData *document, const QString &caption);
HistoryDocument(HistoryItem *parent, const HistoryDocument &other);
HistoryMediaType type() const override {
return _data->voice() ? MediaTypeVoiceFile : (_data->song() ? MediaTypeMusicFile : MediaTypeFile);
}
HistoryDocument *clone(HistoryItem *newParent) const override {
return new HistoryDocument(newParent, *this);
}
void initDimensions() override;
int resizeGetHeight(int width) override;
void draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const override;
HistoryTextState getState(int x, int y, HistoryStateRequest request) const override;
TextSelection adjustSelection(TextSelection selection, TextSelectType type) const override {
if (auto captioned = Get<HistoryDocumentCaptioned>()) {
return captioned->_caption.adjustSelection(selection, type);
}
return selection;
}
bool hasTextForCopy() const override {
return Has<HistoryDocumentCaptioned>();
}
QString notificationText() const override;
QString inDialogsText() const override;
TextWithEntities selectedText(TextSelection selection) const override;
bool uploading() const override {
return _data->uploading();
}
DocumentData *getDocument() override {
return _data;
}
void attachToParent() override;
void detachFromParent() override;
void updateSentMedia(const MTPMessageMedia &media) override;
bool needReSetInlineResultMedia(const MTPMessageMedia &media) override;
bool hasReplyPreview() const override {
return !_data->thumb->isNull();
}
ImagePtr replyPreview() override;
TextWithEntities getCaption() const override {
if (const HistoryDocumentCaptioned *captioned = Get<HistoryDocumentCaptioned>()) {
return captioned->_caption.originalTextWithEntities();
}
return TextWithEntities();
}
bool needsBubble() const override {
return true;
}
bool customInfoLayout() const override {
return false;
}
QMargins bubbleMargins() const override {
return Get<HistoryDocumentThumbed>() ? QMargins(st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top(), st::msgFileThumbPadding.left(), st::msgFileThumbPadding.bottom()) : st::msgPadding;
}
bool hideForwardedFrom() const override {
return _data->song();
}
void step_voiceProgress(float64 ms, bool timer);
protected:
float64 dataProgress() const override {
return _data->progress();
}
bool dataFinished() const override {
return !_data->loading() && !_data->uploading();
}
bool dataLoaded() const override {
return _data->loaded();
}
private:
void createComponents(bool caption);
void setStatusSize(int32 newSize, qint64 realDuration = 0) const;
bool updateStatusText() const; // returns showPause
// Callback is a void(const QString &, const QString &, const Text &) functor.
// It will be called as callback(attachType, attachFileName, attachCaption).
template <typename Callback>
void buildStringRepresentation(Callback callback) const;
DocumentData *_data;
};
class HistoryGif : public HistoryFileMedia {
public:
HistoryGif(HistoryItem *parent, DocumentData *document, const QString &caption);
HistoryGif(HistoryItem *parent, const HistoryGif &other);
HistoryMediaType type() const override {
return MediaTypeGif;
}
HistoryGif *clone(HistoryItem *newParent) const override {
return new HistoryGif(newParent, *this);
}
void initDimensions() override;
int resizeGetHeight(int width) override;
void draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const override;
HistoryTextState getState(int x, int y, HistoryStateRequest request) const override;
TextSelection adjustSelection(TextSelection selection, TextSelectType type) const override {
return _caption.adjustSelection(selection, type);
}
bool hasTextForCopy() const override {
return !_caption.isEmpty();
}
QString notificationText() const override;
QString inDialogsText() const override;
TextWithEntities selectedText(TextSelection selection) const override;
bool uploading() const override {
return _data->uploading();
}
DocumentData *getDocument() override {
return _data;
}
Media::Clip::Reader *getClipReader() override {
return gif();
}
bool playInline(bool autoplay) override;
void stopInline() override;
void attachToParent() override;
void detachFromParent() override;
void updateSentMedia(const MTPMessageMedia &media) override;
bool needReSetInlineResultMedia(const MTPMessageMedia &media) override;
bool hasReplyPreview() const override {
return !_data->thumb->isNull();
}
ImagePtr replyPreview() override;
TextWithEntities getCaption() const override {
return _caption.originalTextWithEntities();
}
bool needsBubble() const override {
if (!_caption.isEmpty()) {
return true;
}
if (_parent->viaBot()) {
return true;
}
return (_parent->Has<HistoryMessageForwarded>() || _parent->Has<HistoryMessageReply>());
}
bool customInfoLayout() const override {
return _caption.isEmpty();
}
bool hideFromName() const override {
return true;
}
~HistoryGif();
protected:
float64 dataProgress() const override;
bool dataFinished() const override;
bool dataLoaded() const override;
private:
DocumentData *_data;
int32 _thumbw, _thumbh;
Text _caption;
Media::Clip::Reader *_gif;
Media::Clip::Reader *gif() {
return (_gif == Media::Clip::BadReader) ? nullptr : _gif;
}
const Media::Clip::Reader *gif() const {
return (_gif == Media::Clip::BadReader) ? nullptr : _gif;
}
void setStatusSize(int32 newSize) const;
void updateStatusText() const;
};
class HistorySticker : public HistoryMedia {
public:
HistorySticker(HistoryItem *parent, DocumentData *document);
HistoryMediaType type() const override {
return MediaTypeSticker;
}
HistorySticker *clone(HistoryItem *newParent) const override {
return new HistorySticker(newParent, _data);
}
void initDimensions() override;
int resizeGetHeight(int width) override;
void draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const override;
HistoryTextState getState(int x, int y, HistoryStateRequest request) const override;
bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override {
return true;
}
bool dragItem() const override {
return true;
}
bool dragItemByHandler(const ClickHandlerPtr &p) const override {
return true;
}
QString notificationText() const override;
TextWithEntities selectedText(TextSelection selection) const override;
DocumentData *getDocument() override {
return _data;
}
void attachToParent() override;
void detachFromParent() override;
void updateSentMedia(const MTPMessageMedia &media) override;
bool needReSetInlineResultMedia(const MTPMessageMedia &media) override;
bool needsBubble() const override {
return false;
}
bool customInfoLayout() const override {
return true;
}
private:
int additionalWidth(const HistoryMessageVia *via, const HistoryMessageReply *reply) const;
int additionalWidth() const {
return additionalWidth(_parent->Get<HistoryMessageVia>(), _parent->Get<HistoryMessageReply>());
}
QString toString() const;
int16 _pixw, _pixh;
ClickHandlerPtr _packLink;
DocumentData *_data;
QString _emoji;
};
class SendMessageClickHandler : public PeerClickHandler {
public:
using PeerClickHandler::PeerClickHandler;
protected:
void onClickImpl() const override;
};
class AddContactClickHandler : public MessageClickHandler {
public:
using MessageClickHandler::MessageClickHandler;
protected:
void onClickImpl() const override;
};
class HistoryContact : public HistoryMedia {
public:
HistoryContact(HistoryItem *parent, int32 userId, const QString &first, const QString &last, const QString &phone);
HistoryMediaType type() const override {
return MediaTypeContact;
}
HistoryContact *clone(HistoryItem *newParent) const override {
return new HistoryContact(newParent, _userId, _fname, _lname, _phone);
}
void initDimensions() override;
void draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const override;
HistoryTextState getState(int x, int y, HistoryStateRequest request) const override;
bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override {
return true;
}
bool dragItemByHandler(const ClickHandlerPtr &p) const override {
return true;
}
QString notificationText() const override;
TextWithEntities selectedText(TextSelection selection) const override;
void attachToParent() override;
void detachFromParent() override;
void updateSentMedia(const MTPMessageMedia &media) override;
bool needsBubble() const override {
return true;
}
bool customInfoLayout() const override {
return false;
}
const QString &fname() const {
return _fname;
}
const QString &lname() const {
return _lname;
}
const QString &phone() const {
return _phone;
}
private:
int32 _userId;
UserData *_contact;
int32 _phonew;
QString _fname, _lname, _phone;
Text _name;
ClickHandlerPtr _linkl;
int32 _linkw;
QString _link;
};
class HistoryWebPage : public HistoryMedia {
public:
HistoryWebPage(HistoryItem *parent, WebPageData *data);
HistoryWebPage(HistoryItem *parent, const HistoryWebPage &other);
HistoryMediaType type() const override {
return MediaTypeWebPage;
}
HistoryWebPage *clone(HistoryItem *newParent) const override {
return new HistoryWebPage(newParent, *this);
}
void initDimensions() override;
int resizeGetHeight(int width) override;
void draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const override;
HistoryTextState getState(int x, int y, HistoryStateRequest request) const override;
TextSelection adjustSelection(TextSelection selection, TextSelectType type) const override;
bool hasTextForCopy() const override {
return false; // we do not add _title and _description in FullSelection text copy.
}
bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override {
return _attach && _attach->toggleSelectionByHandlerClick(p);
}
bool dragItemByHandler(const ClickHandlerPtr &p) const override {
return _attach && _attach->dragItemByHandler(p);
}
TextWithEntities selectedText(TextSelection selection) const override;
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override;
bool isDisplayed() const override {
return !_data->pendingTill;
}
DocumentData *getDocument() override {
return _attach ? _attach->getDocument() : 0;
}
Media::Clip::Reader *getClipReader() override {
return _attach ? _attach->getClipReader() : 0;
}
bool playInline(bool autoplay) override {
return _attach ? _attach->playInline(autoplay) : false;
}
void stopInline() override {
if (_attach) _attach->stopInline();
}
void attachToParent() override;
void detachFromParent() override;
bool hasReplyPreview() const override {
return (_data->photo && !_data->photo->thumb->isNull()) || (_data->document && !_data->document->thumb->isNull());
}
ImagePtr replyPreview() override;
WebPageData *webpage() {
return _data;
}
bool needsBubble() const override {
return true;
}
bool customInfoLayout() const override {
return false;
}
HistoryMedia *attach() const {
return _attach.get();
}
private:
TextSelection toDescriptionSelection(TextSelection selection) const {
return internal::unshiftSelection(selection, _title);
}
TextSelection fromDescriptionSelection(TextSelection selection) const {
return internal::shiftSelection(selection, _title);
}
WebPageData *_data;
ClickHandlerPtr _openl;
std_::unique_ptr<HistoryMedia> _attach;
bool _asArticle = false;
int32 _titleLines, _descriptionLines;
Text _title, _description;
int32 _siteNameWidth = 0;
QString _duration;
int32 _durationWidth = 0;
int16 _pixw = 0;
int16 _pixh = 0;
};
class HistoryGame : public HistoryMedia {
public:
HistoryGame(HistoryItem *parent, GameData *data);
HistoryGame(HistoryItem *parent, const HistoryGame &other);
HistoryMediaType type() const override {
return MediaTypeGame;
}
HistoryGame *clone(HistoryItem *newParent) const override {
return new HistoryGame(newParent, *this);
}
void initDimensions() override;
int resizeGetHeight(int width) override;
void draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const override;
HistoryTextState getState(int x, int y, HistoryStateRequest request) const override;
TextSelection adjustSelection(TextSelection selection, TextSelectType type) const override;
bool isAboveMessage() const override {
return true;
}
bool hasTextForCopy() const override {
return false; // we do not add _title and _description in FullSelection text copy.
}
bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override {
return _attach && _attach->toggleSelectionByHandlerClick(p);
}
bool dragItemByHandler(const ClickHandlerPtr &p) const override {
return _attach && _attach->dragItemByHandler(p);
}
TextWithEntities selectedText(TextSelection selection) const override;
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override;
DocumentData *getDocument() override {
return _attach ? _attach->getDocument() : nullptr;
}
Media::Clip::Reader *getClipReader() override {
return _attach ? _attach->getClipReader() : nullptr;
}
bool playInline(bool autoplay) override {
return _attach ? _attach->playInline(autoplay) : false;
}
void stopInline() override {
if (_attach) _attach->stopInline();
}
void attachToParent() override;
void detachFromParent() override;
bool hasReplyPreview() const override {
return (_data->photo && !_data->photo->thumb->isNull()) || (_data->document && !_data->document->thumb->isNull());
}
ImagePtr replyPreview() override;
GameData *game() {
return _data;
}
bool needsBubble() const override {
return true;
}
bool customInfoLayout() const override {
return false;
}
HistoryMedia *attach() const {
return _attach.get();
}
private:
TextSelection toDescriptionSelection(TextSelection selection) const {
return internal::unshiftSelection(selection, _title);
}
TextSelection fromDescriptionSelection(TextSelection selection) const {
return internal::shiftSelection(selection, _title);
}
GameData *_data;
ClickHandlerPtr _openl;
std_::unique_ptr<HistoryMedia> _attach;
int32 _titleLines, _descriptionLines;
Text _title, _description;
};
struct LocationCoords;
struct LocationData;
class HistoryLocation : public HistoryMedia {
public:
HistoryLocation(HistoryItem *parent, const LocationCoords &coords, const QString &title = QString(), const QString &description = QString());
HistoryLocation(HistoryItem *parent, const HistoryLocation &other);
HistoryMediaType type() const override {
return MediaTypeLocation;
}
HistoryLocation *clone(HistoryItem *newParent) const override {
return new HistoryLocation(newParent, *this);
}
void initDimensions() override;
int resizeGetHeight(int32 width) override;
void draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const override;
HistoryTextState getState(int x, int y, HistoryStateRequest request) const override;
TextSelection adjustSelection(TextSelection selection, TextSelectType type) const override;
bool hasTextForCopy() const override {
return !_title.isEmpty() || !_description.isEmpty();
}
bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override {
return p == _link;
}
bool dragItemByHandler(const ClickHandlerPtr &p) const override {
return p == _link;
}
QString notificationText() const override;
QString inDialogsText() const override;
TextWithEntities selectedText(TextSelection selection) const override;
bool needsBubble() const override {
if (!_title.isEmpty() || !_description.isEmpty()) {
return true;
}
if (_parent->viaBot()) {
return true;
}
return (_parent->Has<HistoryMessageForwarded>() || _parent->Has<HistoryMessageReply>());
}
bool customInfoLayout() const override {
return true;
}
private:
TextSelection toDescriptionSelection(TextSelection selection) const {
return internal::unshiftSelection(selection, _title);
}
TextSelection fromDescriptionSelection(TextSelection selection) const {
return internal::shiftSelection(selection, _title);
}
LocationData *_data;
Text _title, _description;
ClickHandlerPtr _link;
int32 fullWidth() const;
int32 fullHeight() const;
MediaInBubbleState _inBubbleState = MediaInBubbleState::None;
};

View File

@ -19,7 +19,7 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "history/history_media.h"
#include "history/history_media_types.h"
#include "lang.h"
#include "mainwidget.h"
@ -86,81 +86,6 @@ void historyInitMedia() {
initTextOptions();
}
RadialAnimation::RadialAnimation(AnimationCallbacks &&callbacks)
: a_arcEnd(0, 0)
, a_arcStart(0, FullArcLength)
, _animation(std_::move(callbacks)) {
}
void RadialAnimation::start(float64 prg) {
_firstStart = _lastStart = _lastTime = getms();
int32 iprg = qRound(qMax(prg, 0.0001) * AlmostFullArcLength), iprgstrict = qRound(prg * AlmostFullArcLength);
a_arcEnd = anim::ivalue(iprgstrict, iprg);
_animation.start();
}
void RadialAnimation::update(float64 prg, bool finished, uint64 ms) {
int32 iprg = qRound(qMax(prg, 0.0001) * AlmostFullArcLength);
if (iprg != a_arcEnd.to()) {
a_arcEnd.start(iprg);
_lastStart = _lastTime;
}
_lastTime = ms;
float64 dt = float64(ms - _lastStart), fulldt = float64(ms - _firstStart);
_opacity = qMin(fulldt / st::radialDuration, 1.);
if (!finished) {
a_arcEnd.update(1. - (st::radialDuration / (st::radialDuration + dt)), anim::linear);
} else if (dt >= st::radialDuration) {
a_arcEnd.update(1, anim::linear);
stop();
} else {
float64 r = dt / st::radialDuration;
a_arcEnd.update(r, anim::linear);
_opacity *= 1 - r;
}
float64 fromstart = fulldt / st::radialPeriod;
a_arcStart.update(fromstart - std::floor(fromstart), anim::linear);
}
void RadialAnimation::stop() {
_firstStart = _lastStart = _lastTime = 0;
a_arcEnd = anim::ivalue(0, 0);
_animation.stop();
}
void RadialAnimation::step(uint64 ms) {
_animation.step(ms);
}
void RadialAnimation::draw(Painter &p, const QRect &inner, int32 thickness, const style::color &color) {
float64 o = p.opacity();
p.setOpacity(o * _opacity);
QPen pen(color->p), was(p.pen());
pen.setWidth(thickness);
p.setPen(pen);
int32 len = MinArcLength + a_arcEnd.current();
int32 from = QuarterArcLength - a_arcStart.current() - len;
if (rtl()) {
from = QuarterArcLength - (from - QuarterArcLength) - len;
if (from < 0) from += FullArcLength;
}
p.setRenderHint(QPainter::HighQualityAntialiasing);
p.drawArc(inner, from, len);
p.setRenderHint(QPainter::HighQualityAntialiasing, false);
p.setPen(was);
p.setOpacity(o);
}
QString HistoryMedia::inDialogsText() const {
auto result = notificationText();
return result.isEmpty() ? QString() : textcmdLink(1, textClean(result));
}
namespace {
int32 documentMaxStatusWidth(DocumentData *document) {
@ -371,7 +296,11 @@ void HistoryPhoto::initDimensions() {
_maxw += st::mediaPadding.left() + st::mediaPadding.right();
_minh += st::mediaPadding.top() + st::mediaPadding.bottom();
if (!_caption.isEmpty()) {
_minh += st::mediaCaptionSkip + _caption.countHeight(maxActualWidth - st::msgPadding.left() - st::msgPadding.right()) + st::msgPadding.bottom();
auto captionw = maxActualWidth - st::msgPadding.left() - st::msgPadding.right();
_minh += st::mediaCaptionSkip + _caption.countHeight(captionw);
if (isBubbleBottom()) {
_minh += st::msgPadding.bottom();
}
}
}
} else {
@ -417,7 +346,10 @@ int HistoryPhoto::resizeGetHeight(int width) {
_height += st::mediaPadding.top() + st::mediaPadding.bottom();
if (!_caption.isEmpty()) {
int captionw = _width - st::msgPadding.left() - st::msgPadding.right();
_height += st::mediaCaptionSkip + _caption.countHeight(captionw) + st::msgPadding.bottom();
_height += st::mediaCaptionSkip + _caption.countHeight(captionw);
if (isBubbleBottom()) {
_height += st::msgPadding.bottom();
}
}
}
return _height;
@ -425,7 +357,6 @@ int HistoryPhoto::resizeGetHeight(int width) {
void HistoryPhoto::draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const {
if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return;
p.fillRect(QRect(0, 0, _width, _height), QColor(128, 255, 128));
_data->automaticLoad(_parent);
bool selected = (selection == FullSelection);
@ -453,7 +384,10 @@ void HistoryPhoto::draw(Painter &p, const QRect &r, TextSelection selection, uin
width -= st::mediaPadding.left() + st::mediaPadding.right();
height -= skipy + st::mediaPadding.bottom();
if (!_caption.isEmpty()) {
height -= st::mediaCaptionSkip + _caption.countHeight(captionw) + st::msgPadding.bottom();
height -= st::mediaCaptionSkip + _caption.countHeight(captionw);
if (isBubbleBottom()) {
height -= st::msgPadding.bottom();
}
}
} else {
App::roundShadow(p, 0, 0, width, height, selected ? st::msgInShadowSelected : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners);
@ -470,7 +404,8 @@ void HistoryPhoto::draw(Painter &p, const QRect &r, TextSelection selection, uin
QRect rthumb(rtlrect(skipx, skipy, width, height, _width));
p.drawPixmap(rthumb.topLeft(), pix);
if (selected) {
App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, SelectedOverlayLargeCorners);
auto overlayCorners = inWebPage ? SelectedOverlaySmallCorners : SelectedOverlayLargeCorners;
App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, overlayCorners);
}
if (notChild && (radial || (!loaded && !_data->loading()))) {
@ -540,7 +475,10 @@ HistoryTextState HistoryPhoto::getState(int x, int y, HistoryStateRequest reques
skipy = st::mediaPadding.top();
if (!_caption.isEmpty()) {
int captionw = width - st::msgPadding.left() - st::msgPadding.right();
height -= _caption.countHeight(captionw) + st::msgPadding.bottom();
height -= _caption.countHeight(captionw);
if (isBubbleBottom()) {
height -= st::msgPadding.bottom();
}
if (x >= st::msgPadding.left() && y >= height && x < st::msgPadding.left() + captionw && y < _height) {
result = _caption.getState(x - st::msgPadding.left(), y - height, captionw, request.forText());
return result;
@ -712,7 +650,11 @@ void HistoryVideo::initDimensions() {
_maxw += st::mediaPadding.left() + st::mediaPadding.right();
_minh += st::mediaPadding.top() + st::mediaPadding.bottom();
if (!_caption.isEmpty()) {
_minh += st::mediaCaptionSkip + _caption.countHeight(_maxw - st::msgPadding.left() - st::msgPadding.right()) + st::msgPadding.bottom();
auto captionw = _maxw - st::msgPadding.left() - st::msgPadding.right();
_minh += st::mediaCaptionSkip + _caption.countHeight(captionw);
if (isBubbleBottom()) {
_minh += st::msgPadding.bottom();
}
}
}
}
@ -750,7 +692,10 @@ int HistoryVideo::resizeGetHeight(int width) {
_height += st::mediaPadding.top() + st::mediaPadding.bottom();
if (!_caption.isEmpty()) {
int captionw = _width - st::msgPadding.left() - st::msgPadding.right();
_height += st::mediaCaptionSkip + _caption.countHeight(captionw) + st::msgPadding.bottom();
_height += st::mediaCaptionSkip + _caption.countHeight(captionw);
if (isBubbleBottom()) {
_height += st::msgPadding.bottom();
}
}
}
return _height;
@ -785,16 +730,22 @@ void HistoryVideo::draw(Painter &p, const QRect &r, TextSelection selection, uin
width -= st::mediaPadding.left() + st::mediaPadding.right();
height -= skipy + st::mediaPadding.bottom();
if (!_caption.isEmpty()) {
height -= st::mediaCaptionSkip + _caption.countHeight(captionw) + st::msgPadding.bottom();
height -= st::mediaCaptionSkip + _caption.countHeight(captionw);
if (isBubbleBottom()) {
height -= st::msgPadding.bottom();
}
}
} else {
App::roundShadow(p, 0, 0, width, height, selected ? st::msgInShadowSelected : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners);
}
auto inWebPage = (_parent->getMedia() != this);
auto roundRadius = inWebPage ? ImageRoundRadius::Small : ImageRoundRadius::Large;
QRect rthumb(rtlrect(skipx, skipy, width, height, _width));
p.drawPixmap(rthumb.topLeft(), _data->thumb->pixBlurredSingle(ImageRoundRadius::Large, _thumbw, 0, width, height));
p.drawPixmap(rthumb.topLeft(), _data->thumb->pixBlurredSingle(roundRadius, _thumbw, 0, width, height));
if (selected) {
App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, SelectedOverlayLargeCorners);
auto overlayCorners = inWebPage ? SelectedOverlaySmallCorners : SelectedOverlayLargeCorners;
App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, overlayCorners);
}
QRect inner(rthumb.x() + (rthumb.width() - st::msgFileSize) / 2, rthumb.y() + (rthumb.height() - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize);
@ -866,8 +817,11 @@ HistoryTextState HistoryVideo::getState(int x, int y, HistoryStateRequest reques
skipx = st::mediaPadding.left();
skipy = st::mediaPadding.top();
if (!_caption.isEmpty()) {
int32 captionw = width - st::msgPadding.left() - st::msgPadding.right();
height -= _caption.countHeight(captionw) + st::msgPadding.bottom();
auto captionw = width - st::msgPadding.left() - st::msgPadding.right();
height -= _caption.countHeight(captionw);
if (isBubbleBottom()) {
height -= st::msgPadding.bottom();
}
if (x >= st::msgPadding.left() && y >= height && x < st::msgPadding.left() + captionw && y < _height) {
result = _caption.getState(x - st::msgPadding.left(), y - height, captionw, request.forText());
}
@ -1077,7 +1031,11 @@ void HistoryDocument::initDimensions() {
}
if (captioned) {
_minh += captioned->_caption.countHeight(_maxw - st::msgPadding.left() - st::msgPadding.right()) + st::msgPadding.bottom();
auto captionw = _maxw - st::msgPadding.left() - st::msgPadding.right();
_minh += captioned->_caption.countHeight(captionw);
if (isBubbleBottom()) {
_minh += st::msgPadding.bottom();
}
} else {
_height = _minh;
}
@ -1095,7 +1053,11 @@ int HistoryDocument::resizeGetHeight(int width) {
} else {
_height = st::msgFilePadding.top() + st::msgFileSize + st::msgFilePadding.bottom();
}
_height += captioned->_caption.countHeight(_width - st::msgPadding.left() - st::msgPadding.right()) + st::msgPadding.bottom();
auto captionw = _width - st::msgPadding.left() - st::msgPadding.right();
_height += captioned->_caption.countHeight(captionw);
if (isBubbleBottom()) {
_height += st::msgPadding.bottom();
}
return _height;
}
@ -1129,11 +1091,14 @@ void HistoryDocument::draw(Painter &p, const QRect &r, TextSelection selection,
linktop = st::msgFileThumbLinkTop;
bottom = st::msgFileThumbPadding.top() + st::msgFileThumbSize + st::msgFileThumbPadding.bottom();
auto inWebPage = (_parent->getMedia() != this);
auto roundRadius = inWebPage ? ImageRoundRadius::Small : ImageRoundRadius::Large;
QRect rthumb(rtlrect(st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top(), st::msgFileThumbSize, st::msgFileThumbSize, _width));
QPixmap thumb = loaded ? _data->thumb->pixSingle(ImageRoundRadius::Large, thumbed->_thumbw, 0, st::msgFileThumbSize, st::msgFileThumbSize) : _data->thumb->pixBlurredSingle(ImageRoundRadius::Small, thumbed->_thumbw, 0, st::msgFileThumbSize, st::msgFileThumbSize);
QPixmap thumb = loaded ? _data->thumb->pixSingle(roundRadius, thumbed->_thumbw, 0, st::msgFileThumbSize, st::msgFileThumbSize) : _data->thumb->pixBlurredSingle(ImageRoundRadius::Small, thumbed->_thumbw, 0, st::msgFileThumbSize, st::msgFileThumbSize);
p.drawPixmap(rthumb.topLeft(), thumb);
if (selected) {
App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, SelectedOverlayLargeCorners);
auto overlayCorners = inWebPage ? SelectedOverlaySmallCorners : SelectedOverlayLargeCorners;
App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, overlayCorners);
}
if (radial || (!loaded && !_data->loading())) {
@ -1366,7 +1331,11 @@ HistoryTextState HistoryDocument::getState(int x, int y, HistoryStateRequest req
result = captioned->_caption.getState(x - st::msgPadding.left(), y - bottom, _width - st::msgPadding.left() - st::msgPadding.right(), request.forText());
return result;
}
height -= captioned->_caption.countHeight(_width - st::msgPadding.left() - st::msgPadding.right()) + st::msgPadding.bottom();
auto captionw = _width - st::msgPadding.left() - st::msgPadding.right();
height -= captioned->_caption.countHeight(captionw);
if (isBubbleBottom()) {
height -= st::msgPadding.bottom();
}
}
if (x >= 0 && y >= 0 && x < _width && y < height && !_data->loading() && !_data->uploading() && _data->isValid()) {
result.link = _openl;
@ -1628,7 +1597,11 @@ void HistoryGif::initDimensions() {
_maxw += st::mediaPadding.left() + st::mediaPadding.right();
_minh += st::mediaPadding.top() + st::mediaPadding.bottom();
if (!_caption.isEmpty()) {
_minh += st::mediaCaptionSkip + _caption.countHeight(_maxw - st::msgPadding.left() - st::msgPadding.right()) + st::msgPadding.bottom();
auto captionw = _maxw - st::msgPadding.left() - st::msgPadding.right();
_minh += st::mediaCaptionSkip + _caption.countHeight(captionw);
if (isBubbleBottom()) {
_minh += st::msgPadding.bottom();
}
}
}
}
@ -1685,7 +1658,11 @@ int HistoryGif::resizeGetHeight(int width) {
_width += st::mediaPadding.left() + st::mediaPadding.right();
_height += st::mediaPadding.top() + st::mediaPadding.bottom();
if (!_caption.isEmpty()) {
_height += st::mediaCaptionSkip + _caption.countHeight(_width - st::msgPadding.left() - st::msgPadding.right()) + st::msgPadding.bottom();
auto captionw = _width - st::msgPadding.left() - st::msgPadding.right();
_height += st::mediaCaptionSkip + _caption.countHeight(captionw);
if (isBubbleBottom()) {
_height += st::msgPadding.bottom();
}
}
}
@ -1729,7 +1706,10 @@ void HistoryGif::draw(Painter &p, const QRect &r, TextSelection selection, uint6
width -= st::mediaPadding.left() + st::mediaPadding.right();
height -= skipy + st::mediaPadding.bottom();
if (!_caption.isEmpty()) {
height -= st::mediaCaptionSkip + _caption.countHeight(captionw) + st::msgPadding.bottom();
height -= st::mediaCaptionSkip + _caption.countHeight(captionw);
if (isBubbleBottom()) {
height -= st::msgPadding.bottom();
}
}
} else {
App::roundShadow(p, 0, 0, width, _height, selected ? st::msgInShadowSelected : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners);
@ -1740,10 +1720,14 @@ void HistoryGif::draw(Painter &p, const QRect &r, TextSelection selection, uint6
if (animating) {
p.drawPixmap(rthumb.topLeft(), _gif->current(_thumbw, _thumbh, width, height, (Ui::isLayerShown() || Ui::isMediaViewShown() || Ui::isInlineItemBeingChosen()) ? 0 : ms));
} else {
p.drawPixmap(rthumb.topLeft(), _data->thumb->pixBlurredSingle(ImageRoundRadius::Large, _thumbw, _thumbh, width, height));
auto inWebPage = (_parent->getMedia() != this);
auto roundRadius = inWebPage ? ImageRoundRadius::Small : ImageRoundRadius::Large;
p.drawPixmap(rthumb.topLeft(), _data->thumb->pixBlurredSingle(roundRadius, _thumbw, _thumbh, width, height));
}
if (selected) {
App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, SelectedOverlayLargeCorners);
auto inWebPage = (_parent->getMedia() != this);
auto overlayCorners = inWebPage ? SelectedOverlaySmallCorners : SelectedOverlayLargeCorners;
App::roundRect(p, rthumb, textstyleCurrent()->selectOverlay, overlayCorners);
}
if (radial || (!_gif && ((!loaded && !_data->loading()) || !cAutoPlayGif())) || (_gif == Media::Clip::BadReader)) {
@ -1817,8 +1801,11 @@ HistoryTextState HistoryGif::getState(int x, int y, HistoryStateRequest request)
skipx = st::mediaPadding.left();
skipy = st::mediaPadding.top();
if (!_caption.isEmpty()) {
int32 captionw = width - st::msgPadding.left() - st::msgPadding.right();
height -= _caption.countHeight(captionw) + st::msgPadding.bottom();
auto captionw = width - st::msgPadding.left() - st::msgPadding.right();
height -= _caption.countHeight(captionw);
if (isBubbleBottom()) {
height -= st::msgPadding.bottom();
}
if (x >= st::msgPadding.left() && y >= height && x < st::msgPadding.left() + captionw && y < _height) {
result = _caption.getState(x - st::msgPadding.left(), y - height, captionw, request.forText());
return result;
@ -2547,25 +2534,31 @@ void HistoryWebPage::initDimensions() {
if (_siteNameWidth) {
if (_title.isEmpty() && _description.isEmpty()) {
_maxw = qMax(_maxw, int32(_siteNameWidth + _parent->skipBlockWidth()));
accumulate_max(_maxw, _siteNameWidth + _parent->skipBlockWidth());
} else {
_maxw = qMax(_maxw, int32(_siteNameWidth + articlePhotoMaxWidth));
accumulate_max(_maxw, _siteNameWidth + articlePhotoMaxWidth);
}
_minh += _lineHeight;
}
if (!_title.isEmpty()) {
_maxw = qMax(_maxw, int32(_title.maxWidth() + articlePhotoMaxWidth));
accumulate_max(_maxw, _title.maxWidth() + articlePhotoMaxWidth);
_minh += titleMinHeight;
}
if (!_description.isEmpty()) {
_maxw = qMax(_maxw, int32(_description.maxWidth() + articlePhotoMaxWidth));
accumulate_max(_maxw, _description.maxWidth() + articlePhotoMaxWidth);
_minh += descriptionMinHeight;
}
if (_attach) {
if (_minh) _minh += st::webPagePhotoSkip;
auto attachAtTop = !_siteNameWidth && _title.isEmpty() && _description.isEmpty();
if (!attachAtTop) _minh += st::mediaInBubbleSkip;
_attach->initDimensions();
QMargins bubble(_attach->bubbleMargins());
_maxw = qMax(_maxw, int32(_attach->maxWidth() - bubble.left() - bubble.top() + (_attach->customInfoLayout() ? skipBlockWidth : 0)));
auto maxMediaWidth = _attach->maxWidth() - bubble.left() - bubble.right();
if (isBubbleBottom() && _attach->customInfoLayout()) {
maxMediaWidth += skipBlockWidth;
}
accumulate_max(_maxw, maxMediaWidth);
_minh += _attach->minHeight() - bubble.top() - bubble.bottom();
}
if (_data->type == WebPageVideo && _data->duration) {
@ -2573,10 +2566,11 @@ void HistoryWebPage::initDimensions() {
_durationWidth = st::msgDateFont->width(_duration);
}
_maxw += st::msgPadding.left() + st::webPageLeft + st::msgPadding.right();
_minh += st::msgPadding.bottom();
auto padding = inBubblePadding();
_minh += padding.top() + padding.bottom();
if (_asArticle) {
_minh = resizeGetHeight(_maxw); // hack
// _minh += st::msgDateFont->height;
_minh = resizeGetHeight(_maxw);
}
}
@ -2625,7 +2619,7 @@ int HistoryWebPage::resizeGetHeight(int width) {
_pixh -= _lineHeight;
} while (_pixh > _lineHeight);
_height += st::msgDateFont->height;
_height += bottomInfoPadding();
} else {
_height = siteNameHeight;
@ -2653,18 +2647,20 @@ int HistoryWebPage::resizeGetHeight(int width) {
}
if (_attach) {
if (_height) _height += st::webPagePhotoSkip;
auto attachAtTop = !_siteNameWidth && !_titleLines && !_descriptionLines;
if (!attachAtTop) _height += st::mediaInBubbleSkip;
QMargins bubble(_attach->bubbleMargins());
_attach->resizeGetHeight(width + bubble.left() + bubble.right());
_height += _attach->height() - bubble.top() - bubble.bottom();
if (_attach->customInfoLayout() && _attach->currentWidth() + _parent->skipBlockWidth() > width + bubble.left() + bubble.right()) {
_height += st::msgDateFont->height;
if (isBubbleBottom() && _attach->customInfoLayout() && _attach->currentWidth() + _parent->skipBlockWidth() > width + bubble.left() + bubble.right()) {
_height += bottomInfoPadding();
}
}
}
_height += st::msgPadding.bottom();
auto padding = inBubblePadding();
_height += padding.top() + padding.bottom();
return _height;
}
@ -2680,14 +2676,16 @@ void HistoryWebPage::draw(Painter &p, const QRect &r, TextSelection selection, u
style::color semibold = (selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg));
style::color regular = (selected ? (outbg ? st::msgOutDateFgSelected : st::msgInDateFgSelected) : (outbg ? st::msgOutDateFg : st::msgInDateFg));
int32 lshift = st::msgPadding.left() + st::webPageLeft, rshift = st::msgPadding.right(), bshift = st::msgPadding.bottom();
width -= lshift + rshift;
QMargins bubble(_attach ? _attach->bubbleMargins() : QMargins());
if (_asArticle || (_attach && _attach->customInfoLayout() && _attach->currentWidth() + _parent->skipBlockWidth() > width + bubble.left() + bubble.right())) {
bshift += st::msgDateFont->height;
auto padding = inBubblePadding();
auto tshift = padding.top();
auto bshift = padding.bottom();
if (_asArticle || (isBubbleBottom() && _attach && _attach->customInfoLayout() && _attach->currentWidth() + _parent->skipBlockWidth() > width + bubble.left() + bubble.right())) {
bshift += bottomInfoPadding();
}
width -= padding.left() + padding.right();
QRect bar(rtlrect(st::msgPadding.left(), 0, st::webPageBar, _height - bshift, _width));
QRect bar(rtlrect(st::msgPadding.left(), tshift, st::webPageBar, _height - tshift - bshift, _width));
p.fillRect(bar, barfg);
if (_asArticle) {
@ -2707,17 +2705,16 @@ void HistoryWebPage::draw(Painter &p, const QRect &r, TextSelection selection, u
} else {
pix = _data->photo->thumb->pixBlurredSingle(ImageRoundRadius::Small, pixw, pixh, pw, ph);
}
p.drawPixmapLeft(lshift + width - pw, 0, _width, pix);
p.drawPixmapLeft(padding.left() + width - pw, 0, _width, pix);
if (selected) {
App::roundRect(p, rtlrect(lshift + width - pw, 0, pw, _pixh, _width), textstyleCurrent()->selectOverlay, SelectedOverlaySmallCorners);
App::roundRect(p, rtlrect(padding.left() + width - pw, 0, pw, _pixh, _width), textstyleCurrent()->selectOverlay, SelectedOverlaySmallCorners);
}
width -= pw + st::webPagePhotoDelta;
}
int32 tshift = 0;
if (_siteNameWidth) {
p.setFont(st::webPageTitleFont);
p.setPen(semibold);
p.drawTextLeft(lshift, tshift, _width, (width >= _siteNameWidth) ? _data->siteName : st::webPageTitleFont->elided(_data->siteName, width));
p.drawTextLeft(padding.left(), tshift, _width, (width >= _siteNameWidth) ? _data->siteName : st::webPageTitleFont->elided(_data->siteName, width));
tshift += _lineHeight;
}
if (_titleLines) {
@ -2726,7 +2723,7 @@ void HistoryWebPage::draw(Painter &p, const QRect &r, TextSelection selection, u
if (_title.hasSkipBlock()) {
endskip = _parent->skipBlockWidth();
}
_title.drawLeftElided(p, lshift, tshift, width, _width, _titleLines, style::al_left, 0, -1, endskip, false, selection);
_title.drawLeftElided(p, padding.left(), tshift, width, _width, _titleLines, style::al_left, 0, -1, endskip, false, selection);
tshift += _titleLines * _lineHeight;
}
if (_descriptionLines) {
@ -2735,16 +2732,17 @@ void HistoryWebPage::draw(Painter &p, const QRect &r, TextSelection selection, u
if (_description.hasSkipBlock()) {
endskip = _parent->skipBlockWidth();
}
_description.drawLeftElided(p, lshift, tshift, width, _width, _descriptionLines, style::al_left, 0, -1, endskip, false, toDescriptionSelection(selection));
_description.drawLeftElided(p, padding.left(), tshift, width, _width, _descriptionLines, style::al_left, 0, -1, endskip, false, toDescriptionSelection(selection));
tshift += _descriptionLines * _lineHeight;
}
if (_attach) {
if (tshift) tshift += st::webPagePhotoSkip;
auto attachAtTop = !_siteNameWidth && !_titleLines && !_descriptionLines;
if (!attachAtTop) tshift += st::mediaInBubbleSkip;
int32 attachLeft = lshift - bubble.left(), attachTop = tshift - bubble.top();
auto attachLeft = padding.left() - bubble.left();
auto attachTop = tshift - bubble.top();
if (rtl()) attachLeft = _width - attachLeft - _attach->currentWidth();
p.save();
p.translate(attachLeft, attachTop);
auto attachSelection = selected ? FullSelection : TextSelection { 0, 0 };
@ -2771,7 +2769,7 @@ void HistoryWebPage::draw(Painter &p, const QRect &r, TextSelection selection, u
}
}
p.restore();
p.translate(-attachLeft, -attachTop);
}
}
@ -2781,22 +2779,24 @@ HistoryTextState HistoryWebPage::getState(int x, int y, HistoryStateRequest requ
if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return result;
int32 skipx = 0, skipy = 0, width = _width, height = _height;
int32 lshift = st::msgPadding.left() + st::webPageLeft, rshift = st::msgPadding.right(), bshift = st::msgPadding.bottom();
width -= lshift + rshift;
QMargins bubble(_attach ? _attach->bubbleMargins() : QMargins());
if (_asArticle || (_attach && _attach->customInfoLayout() && _attach->currentWidth() + _parent->skipBlockWidth() > width + bubble.left() + bubble.right())) {
bshift += st::msgDateFont->height;
auto padding = inBubblePadding();
auto tshift = padding.top();
auto bshift = padding.bottom();
if (_asArticle || (isBubbleBottom() && _attach && _attach->customInfoLayout() && _attach->currentWidth() + _parent->skipBlockWidth() > width + bubble.left() + bubble.right())) {
bshift += bottomInfoPadding();
}
width -= padding.left() + padding.right();
bool inThumb = false;
if (_asArticle) {
int32 pw = qMax(_pixw, int16(_lineHeight));
if (rtlrect(lshift + width - pw, 0, pw, _pixh, _width).contains(x, y)) {
if (rtlrect(padding.left() + width - pw, 0, pw, _pixh, _width).contains(x, y)) {
inThumb = true;
}
width -= pw + st::webPagePhotoDelta;
}
int tshift = 0, symbolAdd = 0;
int symbolAdd = 0;
if (_siteNameWidth) {
tshift += _lineHeight;
}
@ -2804,7 +2804,7 @@ HistoryTextState HistoryWebPage::getState(int x, int y, HistoryStateRequest requ
if (y >= tshift && y < tshift + _titleLines * _lineHeight) {
Text::StateRequestElided titleRequest = request.forText();
titleRequest.lines = _titleLines;
result = _title.getStateElidedLeft(x - lshift, y - tshift, width, _width, titleRequest);
result = _title.getStateElidedLeft(x - padding.left(), y - tshift, width, _width, titleRequest);
} else if (y >= tshift + _titleLines * _lineHeight) {
symbolAdd += _title.length();
}
@ -2814,7 +2814,7 @@ HistoryTextState HistoryWebPage::getState(int x, int y, HistoryStateRequest requ
if (y >= tshift && y < tshift + _descriptionLines * _lineHeight) {
Text::StateRequestElided descriptionRequest = request.forText();
descriptionRequest.lines = _descriptionLines;
result = _description.getStateElidedLeft(x - lshift, y - tshift, width, _width, descriptionRequest);
result = _description.getStateElidedLeft(x - padding.left(), y - tshift, width, _width, descriptionRequest);
} else if (y >= tshift + _descriptionLines * _lineHeight) {
symbolAdd += _description.length();
}
@ -2823,10 +2823,12 @@ HistoryTextState HistoryWebPage::getState(int x, int y, HistoryStateRequest requ
if (inThumb) {
result.link = _openl;
} else if (_attach) {
if (tshift) tshift += st::webPagePhotoSkip;
auto attachAtTop = !_siteNameWidth && !_titleLines && !_descriptionLines;
if (!attachAtTop) tshift += st::mediaInBubbleSkip;
if (x >= lshift && x < lshift + width && y >= tshift && y < _height - st::msgPadding.bottom()) {
int32 attachLeft = lshift - bubble.left(), attachTop = tshift - bubble.top();
if (x >= padding.left() && x < padding.left() + width && y >= tshift && y < _height - bshift) {
auto attachLeft = padding.left() - bubble.left();
auto attachTop = tshift - bubble.top();
if (rtl()) attachLeft = _width - attachLeft - _attach->currentWidth();
result = _attach->getState(x - attachLeft, y - attachTop, request);
@ -2901,6 +2903,28 @@ ImagePtr HistoryWebPage::replyPreview() {
return _attach ? _attach->replyPreview() : (_data->photo ? _data->photo->makeReplyPreview() : ImagePtr());
}
QMargins HistoryWebPage::inBubblePadding() const {
auto lshift = st::msgPadding.left() + st::webPageLeft;
auto rshift = st::msgPadding.right();
auto bshift = isBubbleBottom() ? st::msgPadding.left() : st::mediaInBubbleSkip;
auto tshift = isBubbleTop() ? st::msgPadding.left() : st::mediaInBubbleSkip;
return QMargins(lshift, tshift, rshift, bshift);
}
int HistoryWebPage::bottomInfoPadding() const {
if (!isBubbleBottom()) return 0;
auto result = st::msgDateFont->height;
// we use padding greater than st::msgPadding.bottom() in the
// bottom of the bubble so that the left line looks pretty.
// but if we have bottom skip because of the info display
// we don't need that additional padding so we replace it
// back with st::msgPadding.bottom() instead of left().
result += st::msgPadding.bottom() - st::msgPadding.left();
return result;
}
HistoryGame::HistoryGame(HistoryItem *parent, GameData *data) : HistoryMedia(parent)
, _data(data)
, _title(st::msgMinWidth - st::webPageLeft)
@ -2917,7 +2941,7 @@ HistoryGame::HistoryGame(HistoryItem *parent, const HistoryGame &other) : Histor
void HistoryGame::initDimensions() {
if (!_lineHeight) _lineHeight = qMax(st::webPageTitleFont->height, st::webPageDescriptionFont->height);
if (!_openl && !_data->url.isEmpty()) _openl.reset(new UrlClickHandler(_data->url, true));
if (!_openl) _openl.reset(new ReplyMarkupClickHandler(_parent, 0, 0));
auto title = _data->title;
@ -2960,36 +2984,44 @@ void HistoryGame::initDimensions() {
int32 l = st::msgPadding.left() + st::webPageLeft, r = st::msgPadding.right();
int32 skipBlockWidth = _parent->skipBlockWidth();
_maxw = skipBlockWidth;
_minh = st::msgPadding.top();
_minh = 0;
int32 titleMinHeight = _title.isEmpty() ? 0 : _lineHeight;
int32 descMaxLines = (4 + (titleMinHeight ? 0 : 1));
int32 descriptionMinHeight = _description.isEmpty() ? 0 : qMin(_description.minHeight(), descMaxLines * _lineHeight);
if (!_title.isEmpty()) {
_maxw = qMax(_maxw, int32(_title.maxWidth()));
accumulate_max(_maxw, _title.maxWidth());
_minh += titleMinHeight;
}
if (!_description.isEmpty()) {
_maxw = qMax(_maxw, int32(_description.maxWidth()));
accumulate_max(_maxw, _description.maxWidth());
_minh += descriptionMinHeight;
}
if (_attach) {
if (_minh) _minh += st::webPagePhotoSkip;
auto attachAtTop = !_titleLines && !_descriptionLines;
if (!attachAtTop) _minh += st::mediaInBubbleSkip;
_attach->initDimensions();
QMargins bubble(_attach->bubbleMargins());
_maxw = qMax(_maxw, int32(_attach->maxWidth() - bubble.left() - bubble.top()));
auto maxMediaWidth = _attach->maxWidth() - bubble.left() - bubble.right();
if (isBubbleBottom() && _attach->customInfoLayout()) {
maxMediaWidth += skipBlockWidth;
}
accumulate_max(_maxw, maxMediaWidth);
_minh += _attach->minHeight() - bubble.top() - bubble.bottom();
}
_maxw += st::msgPadding.left() + st::webPageLeft + st::msgPadding.right();
auto padding = inBubblePadding();
_minh += padding.top() + padding.bottom();
}
int HistoryGame::resizeGetHeight(int width) {
_width = qMin(width, _maxw);
width -= st::msgPadding.left() + st::webPageLeft + st::msgPadding.right();
int32 linesMax = 5;
_height = st::msgPadding.top();
int linesMax = 5;
_height = 0;
if (_title.isEmpty()) {
_titleLines = 0;
} else {
@ -3014,13 +3046,19 @@ int HistoryGame::resizeGetHeight(int width) {
}
if (_attach) {
if (_height) _height += st::webPagePhotoSkip;
auto attachAtTop = !_titleLines && !_descriptionLines;
if (!attachAtTop) _height += st::mediaInBubbleSkip;
QMargins bubble(_attach->bubbleMargins());
_attach->resizeGetHeight(width + bubble.left() + bubble.right());
_height += _attach->height() - bubble.top() - bubble.bottom();
if (isBubbleBottom() && _attach->customInfoLayout() && _attach->currentWidth() + _parent->skipBlockWidth() > width + bubble.left() + bubble.right()) {
_height += bottomInfoPadding();
}
}
auto padding = inBubblePadding();
_height += padding.top() + padding.bottom();
return _height;
}
@ -3036,21 +3074,25 @@ void HistoryGame::draw(Painter &p, const QRect &r, TextSelection selection, uint
style::color semibold = (selected ? (outbg ? st::msgOutServiceFgSelected : st::msgInServiceFgSelected) : (outbg ? st::msgOutServiceFg : st::msgInServiceFg));
style::color regular = (selected ? (outbg ? st::msgOutDateFgSelected : st::msgInDateFgSelected) : (outbg ? st::msgOutDateFg : st::msgInDateFg));
int32 lshift = st::msgPadding.left() + st::webPageLeft, rshift = st::msgPadding.right(), bshift = 0;
width -= lshift + rshift;
QMargins bubble(_attach ? _attach->bubbleMargins() : QMargins());
auto padding = inBubblePadding();
auto tshift = padding.top();
auto bshift = padding.bottom();
if (isBubbleBottom() && _attach && _attach->customInfoLayout() && _attach->currentWidth() + _parent->skipBlockWidth() > width + bubble.left() + bubble.right()) {
bshift += bottomInfoPadding();
}
width -= padding.left() + padding.right();
QRect bar(rtlrect(st::msgPadding.left(), st::msgPadding.top(), st::webPageBar, _height - bshift, _width));
QRect bar(rtlrect(st::msgPadding.left(), tshift, st::webPageBar, _height - tshift - bshift, _width));
p.fillRect(bar, barfg);
int32 tshift = st::msgPadding.top();
if (_titleLines) {
p.setPen(st::black);
int32 endskip = 0;
if (_title.hasSkipBlock()) {
endskip = _parent->skipBlockWidth();
}
_title.drawLeftElided(p, lshift, tshift, width, _width, _titleLines, style::al_left, 0, -1, endskip, false, selection);
_title.drawLeftElided(p, padding.left(), tshift, width, _width, _titleLines, style::al_left, 0, -1, endskip, false, selection);
tshift += _titleLines * _lineHeight;
}
if (_descriptionLines) {
@ -3059,13 +3101,15 @@ void HistoryGame::draw(Painter &p, const QRect &r, TextSelection selection, uint
if (_description.hasSkipBlock()) {
endskip = _parent->skipBlockWidth();
}
_description.drawLeftElided(p, lshift, tshift, width, _width, _descriptionLines, style::al_left, 0, -1, endskip, false, toDescriptionSelection(selection));
_description.drawLeftElided(p, padding.left(), tshift, width, _width, _descriptionLines, style::al_left, 0, -1, endskip, false, toDescriptionSelection(selection));
tshift += _descriptionLines * _lineHeight;
}
if (_attach) {
if (tshift) tshift += st::webPagePhotoSkip;
auto attachAtTop = !_titleLines && !_descriptionLines;
if (!attachAtTop) tshift += st::mediaInBubbleSkip;
int32 attachLeft = lshift - bubble.left(), attachTop = tshift - bubble.top();
auto attachLeft = padding.left() - bubble.left();
auto attachTop = tshift - bubble.top();
if (rtl()) attachLeft = _width - attachLeft - _attach->currentWidth();
auto attachSelection = selected ? FullSelection : TextSelection { 0, 0 };
@ -3082,17 +3126,22 @@ HistoryTextState HistoryGame::getState(int x, int y, HistoryStateRequest request
if (_width < st::msgPadding.left() + st::msgPadding.right() + 1) return result;
int32 width = _width, height = _height;
int32 lshift = st::msgPadding.left() + st::webPageLeft, rshift = st::msgPadding.right(), bshift = 0;
width -= lshift + rshift;
QMargins bubble(_attach ? _attach->bubbleMargins() : QMargins());
auto padding = inBubblePadding();
auto tshift = padding.top();
auto bshift = padding.bottom();
if (isBubbleBottom() && _attach && _attach->customInfoLayout() && _attach->currentWidth() + _parent->skipBlockWidth() > width + bubble.left() + bubble.right()) {
bshift += bottomInfoPadding();
}
width -= padding.left() + padding.right();
bool inThumb = false;
int tshift = st::msgPadding.top(), symbolAdd = 0;
int symbolAdd = 0;
if (_titleLines) {
if (y >= tshift && y < tshift + _titleLines * _lineHeight) {
Text::StateRequestElided titleRequest = request.forText();
titleRequest.lines = _titleLines;
result = _title.getStateElidedLeft(x - lshift, y - tshift, width, _width, titleRequest);
result = _title.getStateElidedLeft(x - padding.left(), y - tshift, width, _width, titleRequest);
} else if (y >= tshift + _titleLines * _lineHeight) {
symbolAdd += _title.length();
}
@ -3102,7 +3151,7 @@ HistoryTextState HistoryGame::getState(int x, int y, HistoryStateRequest request
if (y >= tshift && y < tshift + _descriptionLines * _lineHeight) {
Text::StateRequestElided descriptionRequest = request.forText();
descriptionRequest.lines = _descriptionLines;
result = _description.getStateElidedLeft(x - lshift, y - tshift, width, _width, descriptionRequest);
result = _description.getStateElidedLeft(x - padding.left(), y - tshift, width, _width, descriptionRequest);
} else if (y >= tshift + _descriptionLines * _lineHeight) {
symbolAdd += _description.length();
}
@ -3111,9 +3160,14 @@ HistoryTextState HistoryGame::getState(int x, int y, HistoryStateRequest request
if (inThumb) {
result.link = _openl;
} else if (_attach) {
if (tshift) tshift += st::webPagePhotoSkip;
auto attachAtTop = !_titleLines && !_descriptionLines;
if (!attachAtTop) tshift += st::mediaInBubbleSkip;
if (x >= lshift && x < lshift + width && y >= tshift && y < _height) {
auto attachLeft = padding.left() - bubble.left();
auto attachTop = tshift - bubble.top();
if (rtl()) attachLeft = _width - attachLeft - _attach->currentWidth();
if (x >= attachLeft && x < attachLeft + _attach->currentWidth() && y >= tshift && y < _height - bshift) {
result.link = _openl;
}
}
@ -3177,6 +3231,28 @@ ImagePtr HistoryGame::replyPreview() {
return _attach ? _attach->replyPreview() : (_data->photo ? _data->photo->makeReplyPreview() : ImagePtr());
}
QMargins HistoryGame::inBubblePadding() const {
auto lshift = st::msgPadding.left() + st::webPageLeft;
auto rshift = st::msgPadding.right();
auto bshift = isBubbleBottom() ? st::msgPadding.left() : st::mediaInBubbleSkip;
auto tshift = isBubbleTop() ? st::msgPadding.left() : st::mediaInBubbleSkip;
return QMargins(lshift, tshift, rshift, bshift);
}
int HistoryGame::bottomInfoPadding() const {
if (!isBubbleBottom()) return 0;
auto result = st::msgDateFont->height;
// we use padding greater than st::msgPadding.bottom() in the
// bottom of the bubble so that the left line looks pretty.
// but if we have bottom skip because of the info display
// we don't need that additional padding so we replace it
// back with st::msgPadding.bottom() instead of left().
result += st::msgPadding.bottom() - st::msgPadding.left();
return result;
}
HistoryLocation::HistoryLocation(HistoryItem *parent, const LocationCoords &coords, const QString &title, const QString &description) : HistoryMedia(parent)
, _data(App::location(coords))
, _title(st::msgMinWidth)
@ -3220,8 +3296,8 @@ void HistoryLocation::initDimensions() {
}
_minh += st::mediaPadding.top() + st::mediaPadding.bottom();
if (!_title.isEmpty() || !_description.isEmpty()) {
_minh += st::webPagePhotoSkip;
if (!_parent->Has<HistoryMessageForwarded>() && !_parent->Has<HistoryMessageReply>()) {
_minh += st::mediaInBubbleSkip;
if (isBubbleTop()) {
_minh += st::msgPadding.top();
}
}
@ -3260,8 +3336,8 @@ int HistoryLocation::resizeGetHeight(int width) {
_height += qMin(_description.countHeight(_width - st::msgPadding.left() - st::msgPadding.right()), st::webPageDescriptionFont->height * 3);
}
if (!_title.isEmpty() || !_description.isEmpty()) {
_height += st::webPagePhotoSkip;
if (!_parent->Has<HistoryMessageForwarded>() && !_parent->Has<HistoryMessageReply>()) {
_height += st::mediaInBubbleSkip;
if (isBubbleTop()) {
_height += st::msgPadding.top();
}
}
@ -3281,7 +3357,7 @@ void HistoryLocation::draw(Painter &p, const QRect &r, TextSelection selection,
skipy = st::mediaPadding.top();
if (!_title.isEmpty() || !_description.isEmpty()) {
if (!_parent->Has<HistoryMessageForwarded>() && !_parent->Has<HistoryMessageReply>()) {
if (isBubbleTop()) {
skipy += st::msgPadding.top();
}
}
@ -3299,7 +3375,7 @@ void HistoryLocation::draw(Painter &p, const QRect &r, TextSelection selection,
skipy += qMin(_description.countHeight(textw), 3 * st::webPageDescriptionFont->height);
}
if (!_title.isEmpty() || !_description.isEmpty()) {
skipy += st::webPagePhotoSkip;
skipy += st::mediaInBubbleSkip;
}
height -= skipy + st::mediaPadding.bottom();
} else {
@ -3347,7 +3423,7 @@ HistoryTextState HistoryLocation::getState(int x, int y, HistoryStateRequest req
skipy = st::mediaPadding.top();
if (!_title.isEmpty() || !_description.isEmpty()) {
if (!_parent->Has<HistoryMessageForwarded>() && !_parent->Has<HistoryMessageReply>()) {
if (isBubbleTop()) {
skipy += st::msgPadding.top();
}
}
@ -3375,7 +3451,7 @@ HistoryTextState HistoryLocation::getState(int x, int y, HistoryStateRequest req
skipy += descriptionh;
}
if (!_title.isEmpty() || !_description.isEmpty()) {
skipy += st::webPagePhotoSkip;
skipy += st::mediaInBubbleSkip;
}
height -= skipy + st::mediaPadding.bottom();
}

View File

@ -0,0 +1,906 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "ui/effects/radial_animation.h"
void historyInitMedia();
class HistoryFileMedia : public HistoryMedia {
public:
using HistoryMedia::HistoryMedia;
bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override {
return p == _openl || p == _savel || p == _cancell;
}
bool dragItemByHandler(const ClickHandlerPtr &p) const override {
return p == _openl || p == _savel || p == _cancell;
}
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override;
~HistoryFileMedia();
protected:
ClickHandlerPtr _openl, _savel, _cancell;
void setLinks(ClickHandlerPtr &&openl, ClickHandlerPtr &&savel, ClickHandlerPtr &&cancell);
void setDocumentLinks(DocumentData *document, bool inlinegif = false) {
ClickHandlerPtr open, save;
if (inlinegif) {
open.reset(new GifOpenClickHandler(document));
} else {
open.reset(new DocumentOpenClickHandler(document));
}
if (inlinegif) {
save.reset(new GifOpenClickHandler(document));
} else if (document->voice()) {
save.reset(new DocumentOpenClickHandler(document));
} else {
save.reset(new DocumentSaveClickHandler(document));
}
setLinks(std_::move(open), std_::move(save), MakeShared<DocumentCancelClickHandler>(document));
}
// >= 0 will contain download / upload string, _statusSize = loaded bytes
// < 0 will contain played string, _statusSize = -(seconds + 1) played
// 0x7FFFFFF0 will contain status for not yet downloaded file
// 0x7FFFFFF1 will contain status for already downloaded file
// 0x7FFFFFF2 will contain status for failed to download / upload file
mutable int32 _statusSize;
mutable QString _statusText;
// duration = -1 - no duration, duration = -2 - "GIF" duration
void setStatusSize(int32 newSize, int32 fullSize, int32 duration, qint64 realDuration) const;
void step_thumbOver(float64 ms, bool timer);
void step_radial(uint64 ms, bool timer);
void ensureAnimation() const;
void checkAnimationFinished();
bool isRadialAnimation(uint64 ms) const {
if (!_animation || !_animation->radial.animating()) return false;
_animation->radial.step(ms);
return _animation && _animation->radial.animating();
}
bool isThumbAnimation(uint64 ms) const {
if (!_animation || !_animation->_a_thumbOver.animating()) return false;
_animation->_a_thumbOver.step(ms);
return _animation && _animation->_a_thumbOver.animating();
}
virtual float64 dataProgress() const = 0;
virtual bool dataFinished() const = 0;
virtual bool dataLoaded() const = 0;
struct AnimationData {
AnimationData(AnimationCallbacks &&thumbOverCallbacks, AnimationCallbacks &&radialCallbacks) : a_thumbOver(0, 0)
, _a_thumbOver(std_::move(thumbOverCallbacks))
, radial(std_::move(radialCallbacks)) {
}
anim::fvalue a_thumbOver;
Animation _a_thumbOver;
Ui::RadialAnimation radial;
};
mutable AnimationData *_animation = nullptr;
};
class HistoryPhoto : public HistoryFileMedia {
public:
HistoryPhoto(HistoryItem *parent, PhotoData *photo, const QString &caption);
HistoryPhoto(HistoryItem *parent, PeerData *chat, const MTPDphoto &photo, int width);
HistoryPhoto(HistoryItem *parent, const HistoryPhoto &other);
void init();
HistoryMediaType type() const override {
return MediaTypePhoto;
}
HistoryPhoto *clone(HistoryItem *newParent) const override {
return new HistoryPhoto(newParent, *this);
}
void initDimensions() override;
int resizeGetHeight(int width) override;
void draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const override;
HistoryTextState getState(int x, int y, HistoryStateRequest request) const override;
TextSelection adjustSelection(TextSelection selection, TextSelectType type) const override {
return _caption.adjustSelection(selection, type);
}
bool hasTextForCopy() const override {
return !_caption.isEmpty();
}
QString notificationText() const override;
QString inDialogsText() const override;
TextWithEntities selectedText(TextSelection selection) const override;
PhotoData *photo() const {
return _data;
}
void updateSentMedia(const MTPMessageMedia &media) override;
bool needReSetInlineResultMedia(const MTPMessageMedia &media) override;
void attachToParent() override;
void detachFromParent() override;
bool hasReplyPreview() const override {
return !_data->thumb->isNull();
}
ImagePtr replyPreview() override;
TextWithEntities getCaption() const override {
return _caption.originalTextWithEntities();
}
bool needsBubble() const override {
if (!_caption.isEmpty()) {
return true;
}
if (_parent->viaBot()) {
return true;
}
return (_parent->Has<HistoryMessageForwarded>() || _parent->Has<HistoryMessageReply>());
}
bool customInfoLayout() const override {
return _caption.isEmpty();
}
bool hideFromName() const override {
return true;
}
protected:
float64 dataProgress() const override {
return _data->progress();
}
bool dataFinished() const override {
return !_data->loading() && !_data->uploading();
}
bool dataLoaded() const override {
return _data->loaded();
}
private:
PhotoData *_data;
int16 _pixw = 1;
int16 _pixh = 1;
Text _caption;
};
class HistoryVideo : public HistoryFileMedia {
public:
HistoryVideo(HistoryItem *parent, DocumentData *document, const QString &caption);
HistoryVideo(HistoryItem *parent, const HistoryVideo &other);
HistoryMediaType type() const override {
return MediaTypeVideo;
}
HistoryVideo *clone(HistoryItem *newParent) const override {
return new HistoryVideo(newParent, *this);
}
void initDimensions() override;
int resizeGetHeight(int width) override;
void draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const override;
HistoryTextState getState(int x, int y, HistoryStateRequest request) const override;
TextSelection adjustSelection(TextSelection selection, TextSelectType type) const override {
return _caption.adjustSelection(selection, type);
}
bool hasTextForCopy() const override {
return !_caption.isEmpty();
}
QString notificationText() const override;
QString inDialogsText() const override;
TextWithEntities selectedText(TextSelection selection) const override;
DocumentData *getDocument() override {
return _data;
}
bool uploading() const override {
return _data->uploading();
}
void attachToParent() override;
void detachFromParent() override;
bool needReSetInlineResultMedia(const MTPMessageMedia &media) override;
bool hasReplyPreview() const override {
return !_data->thumb->isNull();
}
ImagePtr replyPreview() override;
TextWithEntities getCaption() const override {
return _caption.originalTextWithEntities();
}
bool needsBubble() const override {
if (!_caption.isEmpty()) {
return true;
}
if (_parent->viaBot()) {
return true;
}
return (_parent->Has<HistoryMessageForwarded>() || _parent->Has<HistoryMessageReply>());
}
bool customInfoLayout() const override {
return _caption.isEmpty();
}
bool hideFromName() const override {
return true;
}
protected:
float64 dataProgress() const override {
return _data->progress();
}
bool dataFinished() const override {
return !_data->loading() && !_data->uploading();
}
bool dataLoaded() const override {
return _data->loaded();
}
private:
DocumentData *_data;
int32 _thumbw;
Text _caption;
void setStatusSize(int32 newSize) const;
void updateStatusText() const;
};
struct HistoryDocumentThumbed : public BaseComponent<HistoryDocumentThumbed> {
ClickHandlerPtr _linksavel, _linkcancell;
int _thumbw = 0;
mutable int _linkw = 0;
mutable QString _link;
};
struct HistoryDocumentCaptioned : public BaseComponent<HistoryDocumentCaptioned> {
Text _caption = { int(st::msgFileMinWidth) - st::msgPadding.left() - st::msgPadding.right() };
};
struct HistoryDocumentNamed : public BaseComponent<HistoryDocumentNamed> {
QString _name;
int _namew = 0;
};
class HistoryDocument;
struct HistoryDocumentVoicePlayback {
HistoryDocumentVoicePlayback(const HistoryDocument *that);
int32 _position;
anim::fvalue a_progress;
Animation _a_progress;
};
struct HistoryDocumentVoice : public BaseComponent<HistoryDocumentVoice> {
HistoryDocumentVoice &operator=(HistoryDocumentVoice &&other) {
std::swap(_playback, other._playback);
return *this;
}
~HistoryDocumentVoice() {
deleteAndMark(_playback);
}
void ensurePlayback(const HistoryDocument *interfaces) const;
void checkPlaybackFinished() const;
mutable HistoryDocumentVoicePlayback *_playback = nullptr;
};
class HistoryDocument : public HistoryFileMedia, public Composer {
public:
HistoryDocument(HistoryItem *parent, DocumentData *document, const QString &caption);
HistoryDocument(HistoryItem *parent, const HistoryDocument &other);
HistoryMediaType type() const override {
return _data->voice() ? MediaTypeVoiceFile : (_data->song() ? MediaTypeMusicFile : MediaTypeFile);
}
HistoryDocument *clone(HistoryItem *newParent) const override {
return new HistoryDocument(newParent, *this);
}
void initDimensions() override;
int resizeGetHeight(int width) override;
void draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const override;
HistoryTextState getState(int x, int y, HistoryStateRequest request) const override;
TextSelection adjustSelection(TextSelection selection, TextSelectType type) const override {
if (auto captioned = Get<HistoryDocumentCaptioned>()) {
return captioned->_caption.adjustSelection(selection, type);
}
return selection;
}
bool hasTextForCopy() const override {
return Has<HistoryDocumentCaptioned>();
}
QString notificationText() const override;
QString inDialogsText() const override;
TextWithEntities selectedText(TextSelection selection) const override;
bool uploading() const override {
return _data->uploading();
}
DocumentData *getDocument() override {
return _data;
}
void attachToParent() override;
void detachFromParent() override;
void updateSentMedia(const MTPMessageMedia &media) override;
bool needReSetInlineResultMedia(const MTPMessageMedia &media) override;
bool hasReplyPreview() const override {
return !_data->thumb->isNull();
}
ImagePtr replyPreview() override;
TextWithEntities getCaption() const override {
if (const HistoryDocumentCaptioned *captioned = Get<HistoryDocumentCaptioned>()) {
return captioned->_caption.originalTextWithEntities();
}
return TextWithEntities();
}
bool needsBubble() const override {
return true;
}
bool customInfoLayout() const override {
return false;
}
QMargins bubbleMargins() const override {
return Get<HistoryDocumentThumbed>() ? QMargins(st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top(), st::msgFileThumbPadding.left(), st::msgFileThumbPadding.bottom()) : st::msgPadding;
}
bool hideForwardedFrom() const override {
return _data->song();
}
void step_voiceProgress(float64 ms, bool timer);
protected:
float64 dataProgress() const override {
return _data->progress();
}
bool dataFinished() const override {
return !_data->loading() && !_data->uploading();
}
bool dataLoaded() const override {
return _data->loaded();
}
private:
void createComponents(bool caption);
void setStatusSize(int32 newSize, qint64 realDuration = 0) const;
bool updateStatusText() const; // returns showPause
// Callback is a void(const QString &, const QString &, const Text &) functor.
// It will be called as callback(attachType, attachFileName, attachCaption).
template <typename Callback>
void buildStringRepresentation(Callback callback) const;
DocumentData *_data;
};
class HistoryGif : public HistoryFileMedia {
public:
HistoryGif(HistoryItem *parent, DocumentData *document, const QString &caption);
HistoryGif(HistoryItem *parent, const HistoryGif &other);
HistoryMediaType type() const override {
return MediaTypeGif;
}
HistoryGif *clone(HistoryItem *newParent) const override {
return new HistoryGif(newParent, *this);
}
void initDimensions() override;
int resizeGetHeight(int width) override;
void draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const override;
HistoryTextState getState(int x, int y, HistoryStateRequest request) const override;
TextSelection adjustSelection(TextSelection selection, TextSelectType type) const override {
return _caption.adjustSelection(selection, type);
}
bool hasTextForCopy() const override {
return !_caption.isEmpty();
}
QString notificationText() const override;
QString inDialogsText() const override;
TextWithEntities selectedText(TextSelection selection) const override;
bool uploading() const override {
return _data->uploading();
}
DocumentData *getDocument() override {
return _data;
}
Media::Clip::Reader *getClipReader() override {
return gif();
}
bool playInline(bool autoplay) override;
void stopInline() override;
void attachToParent() override;
void detachFromParent() override;
void updateSentMedia(const MTPMessageMedia &media) override;
bool needReSetInlineResultMedia(const MTPMessageMedia &media) override;
bool hasReplyPreview() const override {
return !_data->thumb->isNull();
}
ImagePtr replyPreview() override;
TextWithEntities getCaption() const override {
return _caption.originalTextWithEntities();
}
bool needsBubble() const override {
if (!_caption.isEmpty()) {
return true;
}
if (_parent->viaBot()) {
return true;
}
return (_parent->Has<HistoryMessageForwarded>() || _parent->Has<HistoryMessageReply>());
}
bool customInfoLayout() const override {
return _caption.isEmpty();
}
bool hideFromName() const override {
return true;
}
~HistoryGif();
protected:
float64 dataProgress() const override;
bool dataFinished() const override;
bool dataLoaded() const override;
private:
DocumentData *_data;
int32 _thumbw, _thumbh;
Text _caption;
Media::Clip::Reader *_gif;
Media::Clip::Reader *gif() {
return (_gif == Media::Clip::BadReader) ? nullptr : _gif;
}
const Media::Clip::Reader *gif() const {
return (_gif == Media::Clip::BadReader) ? nullptr : _gif;
}
void setStatusSize(int32 newSize) const;
void updateStatusText() const;
};
class HistorySticker : public HistoryMedia {
public:
HistorySticker(HistoryItem *parent, DocumentData *document);
HistoryMediaType type() const override {
return MediaTypeSticker;
}
HistorySticker *clone(HistoryItem *newParent) const override {
return new HistorySticker(newParent, _data);
}
void initDimensions() override;
int resizeGetHeight(int width) override;
void draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const override;
HistoryTextState getState(int x, int y, HistoryStateRequest request) const override;
bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override {
return true;
}
bool dragItem() const override {
return true;
}
bool dragItemByHandler(const ClickHandlerPtr &p) const override {
return true;
}
QString notificationText() const override;
TextWithEntities selectedText(TextSelection selection) const override;
DocumentData *getDocument() override {
return _data;
}
void attachToParent() override;
void detachFromParent() override;
void updateSentMedia(const MTPMessageMedia &media) override;
bool needReSetInlineResultMedia(const MTPMessageMedia &media) override;
bool needsBubble() const override {
return false;
}
bool customInfoLayout() const override {
return true;
}
private:
int additionalWidth(const HistoryMessageVia *via, const HistoryMessageReply *reply) const;
int additionalWidth() const {
return additionalWidth(_parent->Get<HistoryMessageVia>(), _parent->Get<HistoryMessageReply>());
}
QString toString() const;
int16 _pixw, _pixh;
ClickHandlerPtr _packLink;
DocumentData *_data;
QString _emoji;
};
class HistoryContact : public HistoryMedia {
public:
HistoryContact(HistoryItem *parent, int32 userId, const QString &first, const QString &last, const QString &phone);
HistoryMediaType type() const override {
return MediaTypeContact;
}
HistoryContact *clone(HistoryItem *newParent) const override {
return new HistoryContact(newParent, _userId, _fname, _lname, _phone);
}
void initDimensions() override;
void draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const override;
HistoryTextState getState(int x, int y, HistoryStateRequest request) const override;
bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override {
return true;
}
bool dragItemByHandler(const ClickHandlerPtr &p) const override {
return true;
}
QString notificationText() const override;
TextWithEntities selectedText(TextSelection selection) const override;
void attachToParent() override;
void detachFromParent() override;
void updateSentMedia(const MTPMessageMedia &media) override;
bool needsBubble() const override {
return true;
}
bool customInfoLayout() const override {
return false;
}
const QString &fname() const {
return _fname;
}
const QString &lname() const {
return _lname;
}
const QString &phone() const {
return _phone;
}
private:
int32 _userId;
UserData *_contact;
int32 _phonew;
QString _fname, _lname, _phone;
Text _name;
ClickHandlerPtr _linkl;
int32 _linkw;
QString _link;
};
class HistoryWebPage : public HistoryMedia {
public:
HistoryWebPage(HistoryItem *parent, WebPageData *data);
HistoryWebPage(HistoryItem *parent, const HistoryWebPage &other);
HistoryMediaType type() const override {
return MediaTypeWebPage;
}
HistoryWebPage *clone(HistoryItem *newParent) const override {
return new HistoryWebPage(newParent, *this);
}
void initDimensions() override;
int resizeGetHeight(int width) override;
void draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const override;
HistoryTextState getState(int x, int y, HistoryStateRequest request) const override;
TextSelection adjustSelection(TextSelection selection, TextSelectType type) const override;
bool hasTextForCopy() const override {
return false; // we do not add _title and _description in FullSelection text copy.
}
bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override {
return _attach && _attach->toggleSelectionByHandlerClick(p);
}
bool dragItemByHandler(const ClickHandlerPtr &p) const override {
return _attach && _attach->dragItemByHandler(p);
}
TextWithEntities selectedText(TextSelection selection) const override;
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override;
bool isDisplayed() const override {
return !_data->pendingTill;
}
DocumentData *getDocument() override {
return _attach ? _attach->getDocument() : 0;
}
Media::Clip::Reader *getClipReader() override {
return _attach ? _attach->getClipReader() : 0;
}
bool playInline(bool autoplay) override {
return _attach ? _attach->playInline(autoplay) : false;
}
void stopInline() override {
if (_attach) _attach->stopInline();
}
void attachToParent() override;
void detachFromParent() override;
bool hasReplyPreview() const override {
return (_data->photo && !_data->photo->thumb->isNull()) || (_data->document && !_data->document->thumb->isNull());
}
ImagePtr replyPreview() override;
WebPageData *webpage() {
return _data;
}
bool needsBubble() const override {
return true;
}
bool customInfoLayout() const override {
return false;
}
HistoryMedia *attach() const {
return _attach.get();
}
private:
TextSelection toDescriptionSelection(TextSelection selection) const {
return internal::unshiftSelection(selection, _title);
}
TextSelection fromDescriptionSelection(TextSelection selection) const {
return internal::shiftSelection(selection, _title);
}
QMargins inBubblePadding() const;
int bottomInfoPadding() const;
WebPageData *_data;
ClickHandlerPtr _openl;
std_::unique_ptr<HistoryMedia> _attach;
bool _asArticle = false;
int32 _titleLines, _descriptionLines;
Text _title, _description;
int32 _siteNameWidth = 0;
QString _duration;
int32 _durationWidth = 0;
int16 _pixw = 0;
int16 _pixh = 0;
};
class HistoryGame : public HistoryMedia {
public:
HistoryGame(HistoryItem *parent, GameData *data);
HistoryGame(HistoryItem *parent, const HistoryGame &other);
HistoryMediaType type() const override {
return MediaTypeGame;
}
HistoryGame *clone(HistoryItem *newParent) const override {
return new HistoryGame(newParent, *this);
}
void initDimensions() override;
int resizeGetHeight(int width) override;
void draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const override;
HistoryTextState getState(int x, int y, HistoryStateRequest request) const override;
TextSelection adjustSelection(TextSelection selection, TextSelectType type) const override;
bool isAboveMessage() const override {
return true;
}
bool hasTextForCopy() const override {
return false; // we do not add _title and _description in FullSelection text copy.
}
bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override {
return _attach && _attach->toggleSelectionByHandlerClick(p);
}
bool dragItemByHandler(const ClickHandlerPtr &p) const override {
return _attach && _attach->dragItemByHandler(p);
}
TextWithEntities selectedText(TextSelection selection) const override;
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override;
DocumentData *getDocument() override {
return _attach ? _attach->getDocument() : nullptr;
}
Media::Clip::Reader *getClipReader() override {
return _attach ? _attach->getClipReader() : nullptr;
}
bool playInline(bool autoplay) override {
return _attach ? _attach->playInline(autoplay) : false;
}
void stopInline() override {
if (_attach) _attach->stopInline();
}
void attachToParent() override;
void detachFromParent() override;
bool hasReplyPreview() const override {
return (_data->photo && !_data->photo->thumb->isNull()) || (_data->document && !_data->document->thumb->isNull());
}
ImagePtr replyPreview() override;
GameData *game() {
return _data;
}
bool needsBubble() const override {
return true;
}
bool customInfoLayout() const override {
return false;
}
HistoryMedia *attach() const {
return _attach.get();
}
private:
TextSelection toDescriptionSelection(TextSelection selection) const {
return internal::unshiftSelection(selection, _title);
}
TextSelection fromDescriptionSelection(TextSelection selection) const {
return internal::shiftSelection(selection, _title);
}
QMargins inBubblePadding() const;
int bottomInfoPadding() const;
GameData *_data;
ClickHandlerPtr _openl;
std_::unique_ptr<HistoryMedia> _attach;
int32 _titleLines, _descriptionLines;
Text _title, _description;
};
struct LocationCoords;
struct LocationData;
class HistoryLocation : public HistoryMedia {
public:
HistoryLocation(HistoryItem *parent, const LocationCoords &coords, const QString &title = QString(), const QString &description = QString());
HistoryLocation(HistoryItem *parent, const HistoryLocation &other);
HistoryMediaType type() const override {
return MediaTypeLocation;
}
HistoryLocation *clone(HistoryItem *newParent) const override {
return new HistoryLocation(newParent, *this);
}
void initDimensions() override;
int resizeGetHeight(int32 width) override;
void draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const override;
HistoryTextState getState(int x, int y, HistoryStateRequest request) const override;
TextSelection adjustSelection(TextSelection selection, TextSelectType type) const override;
bool hasTextForCopy() const override {
return !_title.isEmpty() || !_description.isEmpty();
}
bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override {
return p == _link;
}
bool dragItemByHandler(const ClickHandlerPtr &p) const override {
return p == _link;
}
QString notificationText() const override;
QString inDialogsText() const override;
TextWithEntities selectedText(TextSelection selection) const override;
bool needsBubble() const override {
if (!_title.isEmpty() || !_description.isEmpty()) {
return true;
}
if (_parent->viaBot()) {
return true;
}
return (_parent->Has<HistoryMessageForwarded>() || _parent->Has<HistoryMessageReply>());
}
bool customInfoLayout() const override {
return true;
}
private:
TextSelection toDescriptionSelection(TextSelection selection) const {
return internal::unshiftSelection(selection, _title);
}
TextSelection fromDescriptionSelection(TextSelection selection) const {
return internal::shiftSelection(selection, _title);
}
LocationData *_data;
Text _title, _description;
ClickHandlerPtr _link;
int32 fullWidth() const;
int32 fullHeight() const;
};
class SendMessageClickHandler : public PeerClickHandler {
public:
using PeerClickHandler::PeerClickHandler;
protected:
void onClickImpl() const override;
};
class AddContactClickHandler : public MessageClickHandler {
public:
using MessageClickHandler::MessageClickHandler;
protected:
void onClickImpl() const override;
};

View File

@ -27,6 +27,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "apiwrap.h"
#include "history/history_location_manager.h"
#include "history/history_service_layout.h"
#include "history/history_media_types.h"
#include "styles/style_dialogs.h"
namespace {
@ -476,6 +477,39 @@ void HistoryMessage::createComponentsHelper(MTPDmessage::Flags flags, MsgId repl
createComponents(config);
}
void HistoryMessage::updateMediaInBubbleState() {
if (!_media) {
return;
}
if (!drawBubble()) {
_media->setInBubbleState(MediaInBubbleState::None);
return;
}
bool hasSomethingAbove = displayFromName() || displayForwardedFrom() || Has<HistoryMessageVia>();
bool hasSomethingBelow = false;
if (!emptyText()) {
if (_media->isAboveMessage()) {
hasSomethingBelow = true;
} else {
hasSomethingAbove = true;
}
}
auto computeState = [hasSomethingAbove, hasSomethingBelow] {
if (hasSomethingAbove) {
if (hasSomethingBelow) {
return MediaInBubbleState::Middle;
}
return MediaInBubbleState::Bottom;
} else if (hasSomethingBelow) {
return MediaInBubbleState::Top;
}
return MediaInBubbleState::None;
};
_media->setInBubbleState(computeState());
}
bool HistoryMessage::displayEditedBadge(bool hasViaBot) const {
if (!(_flags & MTPDmessage::Flag::f_edit_date)) {
return false;
@ -658,6 +692,8 @@ void HistoryMessage::initDimensions() {
if (reply) {
reply->updateName();
}
updateMediaInBubbleState();
if (drawBubble()) {
auto fwd = Get<HistoryMessageForwarded>();
auto via = Get<HistoryMessageVia>();
@ -665,9 +701,11 @@ void HistoryMessage::initDimensions() {
fwd->create(via);
}
auto mediaDisplayed = false;
if (_media) {
mediaDisplayed = _media->isDisplayed();
_media->initDimensions();
if (_media->isDisplayed() && !_media->isAboveMessage()) {
if (mediaDisplayed && _media->isBubbleBottom()) {
if (_text.hasSkipBlock()) {
_text.removeSkipBlock();
_textWidth = -1;
@ -681,19 +719,21 @@ void HistoryMessage::initDimensions() {
}
_maxw = plainMaxWidth();
if (emptyText()) {
_minh = 0;
} else {
_minh = st::msgPadding.top() + _text.minHeight() + st::msgPadding.bottom();
}
if (_media && _media->isDisplayed()) {
int32 maxw = _media->maxWidth();
_minh = emptyText() ? 0 : _text.minHeight();
if (mediaDisplayed) {
if (!_media->isBubbleTop()) {
_minh += st::msgPadding.top() + st::mediaInBubbleSkip;
}
if (!_media->isBubbleBottom()) {
_minh += st::msgPadding.bottom() + st::mediaInBubbleSkip;
}
auto maxw = _media->maxWidth();
if (maxw > _maxw) _maxw = maxw;
_minh += _media->minHeight();
}
if (!_media) {
} else {
_minh += st::msgPadding.top() + st::msgPadding.bottom();
if (displayFromName()) {
int32 namew = st::msgPadding.left() + author()->nameText.maxWidth() + st::msgPadding.right();
auto namew = st::msgPadding.left() + author()->nameText.maxWidth() + st::msgPadding.right();
if (via && !fwd) {
namew += st::msgServiceFont->spacew + via->_maxWidth;
}
@ -704,7 +744,7 @@ void HistoryMessage::initDimensions() {
}
}
if (fwd) {
int32 _namew = st::msgPadding.left() + fwd->_text.maxWidth() + st::msgPadding.right();
auto _namew = st::msgPadding.left() + fwd->_text.maxWidth() + st::msgPadding.right();
if (via) {
_namew += st::msgServiceFont->spacew + via->_maxWidth;
}
@ -1029,19 +1069,19 @@ void HistoryMessage::drawInfo(Painter &p, int32 right, int32 bottom, int32 width
int32 infoRight = right, infoBottom = bottom;
switch (type) {
case InfoDisplayDefault:
infoRight -= st::msgPadding.right() - st::msgDateDelta.x();
infoBottom -= st::msgPadding.bottom() - st::msgDateDelta.y();
p.setPen(selected ? (outbg ? st::msgOutDateFgSelected : st::msgInDateFgSelected) : (outbg ? st::msgOutDateFg : st::msgInDateFg));
infoRight -= st::msgPadding.right() - st::msgDateDelta.x();
infoBottom -= st::msgPadding.bottom() - st::msgDateDelta.y();
p.setPen(selected ? (outbg ? st::msgOutDateFgSelected : st::msgInDateFgSelected) : (outbg ? st::msgOutDateFg : st::msgInDateFg));
break;
case InfoDisplayOverImage:
infoRight -= st::msgDateImgDelta + st::msgDateImgPadding.x();
infoBottom -= st::msgDateImgDelta + st::msgDateImgPadding.y();
p.setPen(st::msgDateImgColor);
infoRight -= st::msgDateImgDelta + st::msgDateImgPadding.x();
infoBottom -= st::msgDateImgDelta + st::msgDateImgPadding.y();
p.setPen(st::msgDateImgColor);
break;
case InfoDisplayOverBackground:
infoRight -= st::msgDateImgDelta + st::msgDateImgPadding.x();
infoBottom -= st::msgDateImgDelta + st::msgDateImgPadding.y();
p.setPen(st::msgServiceColor);
infoRight -= st::msgDateImgDelta + st::msgDateImgPadding.x();
infoBottom -= st::msgDateImgDelta + st::msgDateImgPadding.y();
p.setPen(st::msgServiceColor);
break;
}
@ -1152,9 +1192,6 @@ void HistoryMessage::draw(Painter &p, const QRect &r, TextSelection selection, u
int dateh = 0, unreadbarh = 0;
if (auto date = Get<HistoryMessageDate>()) {
dateh = date->height();
//if (r.intersects(QRect(0, 0, _history->width, dateh))) {
// date->paint(p, 0, _history->width);
//}
}
if (auto unreadbar = Get<HistoryMessageUnreadBar>()) {
unreadbarh = unreadbar->height();
@ -1197,7 +1234,8 @@ void HistoryMessage::draw(Painter &p, const QRect &r, TextSelection selection, u
fromNameUpdated(width);
}
int32 top = marginTop();
auto mediaDisplayed = _media && _media->isDisplayed();
auto top = marginTop();
QRect r(left, top, width, height - top - marginBottom());
style::color bg(selected ? (outbg ? st::msgOutBgSelected : st::msgInBgSelected) : (outbg ? st::msgOutBg : st::msgInBg));
@ -1206,17 +1244,23 @@ void HistoryMessage::draw(Painter &p, const QRect &r, TextSelection selection, u
App::roundRect(p, r, bg, cors, &sh);
QRect trect(r.marginsAdded(-st::msgPadding));
paintFromName(p, trect, selected);
paintForwardedInfo(p, trect, selected);
paintReplyInfo(p, trect, selected);
paintViaBotIdInfo(p, trect, selected);
if (mediaDisplayed && _media->isBubbleTop()) {
trect.setY(trect.y() - st::msgPadding.top());
} else {
paintFromName(p, trect, selected);
paintForwardedInfo(p, trect, selected);
paintReplyInfo(p, trect, selected);
paintViaBotIdInfo(p, trect, selected);
}
if (mediaDisplayed && _media->isBubbleBottom()) {
trect.setHeight(trect.height() + st::msgPadding.bottom());
}
auto needDrawInfo = true;
if (_media && _media->isDisplayed()) {
if (mediaDisplayed) {
auto mediaAboveText = _media->isAboveMessage();
auto mediaHeight = _media->height();
auto mediaLeft = trect.x() - st::msgPadding.left();
auto mediaTop = mediaAboveText ? (trect.y() - st::msgPadding.top()) : (r.y() + r.height() - mediaHeight);
auto mediaTop = mediaAboveText ? trect.y() : (trect.y() + trect.height() - mediaHeight);
if (!mediaAboveText) {
paintText(p, trect, selection);
}
@ -1361,46 +1405,50 @@ int HistoryMessage::performResizeGetHeight(int width) {
auto reply = Get<HistoryMessageReply>();
auto via = Get<HistoryMessageVia>();
bool media = (_media && _media->isDisplayed());
auto mediaDisplayed = false;
auto mediaInBubbleState = MediaInBubbleState::None;
if (_media) {
mediaDisplayed = _media->isDisplayed();
mediaInBubbleState = _media->inBubbleState();
}
if (width >= _maxw) {
_height = _minh;
if (media) _media->resizeGetHeight(_maxw);
if (mediaDisplayed) _media->resizeGetHeight(_maxw);
} else {
if (emptyText()) {
_height = 0;
} else {
int32 textWidth = qMax(width - st::msgPadding.left() - st::msgPadding.right(), 1);
auto textWidth = qMax(width - st::msgPadding.left() - st::msgPadding.right(), 1);
if (textWidth != _textWidth) {
textstyleSet(&((out() && !isPost()) ? st::outTextStyle : st::inTextStyle));
_textWidth = textWidth;
_textHeight = _text.countHeight(textWidth);
textstyleRestore();
}
_height = st::msgPadding.top() + _textHeight + st::msgPadding.bottom();
_height = _textHeight;
}
if (mediaDisplayed) {
if (!_media->isBubbleTop()) {
_height += st::msgPadding.top() + st::mediaInBubbleSkip;
}
if (!_media->isBubbleBottom()) {
_height += st::msgPadding.bottom() + st::mediaInBubbleSkip;
}
_height += _media->resizeGetHeight(width);
} else {
_height += st::msgPadding.top() + st::msgPadding.bottom();
}
if (media) _height += _media->resizeGetHeight(width);
}
auto mediaTopPaddingAdded = !emptyText();
if (displayFromName()) {
int32 l = 0, w = 0;
countPositionAndSize(l, w);
fromNameUpdated(w);
if (!mediaTopPaddingAdded) {
_height += st::msgPadding.top() + st::mediaHeaderSkip;
mediaTopPaddingAdded = true;
}
_height += st::msgNameFont->height;
} else if (via && !fwd) {
int32 l = 0, w = 0;
countPositionAndSize(l, w);
via->resize(w - st::msgPadding.left() - st::msgPadding.right());
if (!mediaTopPaddingAdded) {
_height += st::msgPadding.top() + st::mediaHeaderSkip;
mediaTopPaddingAdded = true;
}
_height += st::msgNameFont->height;
}
@ -1408,11 +1456,6 @@ int HistoryMessage::performResizeGetHeight(int width) {
int32 l = 0, w = 0;
countPositionAndSize(l, w);
int32 fwdheight = ((fwd->_text.maxWidth() > (w - st::msgPadding.left() - st::msgPadding.right())) ? 2 : 1) * st::semiboldFont->height;
if (!mediaTopPaddingAdded) {
_height += st::msgPadding.top() + st::mediaHeaderSkip;
mediaTopPaddingAdded = true;
}
_height += fwdheight;
}
@ -1420,11 +1463,6 @@ int HistoryMessage::performResizeGetHeight(int width) {
int32 l = 0, w = 0;
countPositionAndSize(l, w);
reply->resize(w - st::msgPadding.left() - st::msgPadding.right());
if (!mediaTopPaddingAdded) {
_height += st::msgPadding.top() + st::mediaHeaderSkip;
mediaTopPaddingAdded = true;
}
_height += st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
}
} else if (_media) {
@ -1465,12 +1503,12 @@ bool HistoryMessage::pointInTime(int32 right, int32 bottom, int x, int y, InfoDi
int32 infoRight = right, infoBottom = bottom;
switch (type) {
case InfoDisplayDefault:
infoRight -= st::msgPadding.right() - st::msgDateDelta.x();
infoBottom -= st::msgPadding.bottom() - st::msgDateDelta.y();
infoRight -= st::msgPadding.right() - st::msgDateDelta.x();
infoBottom -= st::msgPadding.bottom() - st::msgDateDelta.y();
break;
case InfoDisplayOverImage:
infoRight -= st::msgDateImgDelta + st::msgDateImgPadding.x();
infoBottom -= st::msgDateImgDelta + st::msgDateImgPadding.y();
infoRight -= st::msgDateImgDelta + st::msgDateImgPadding.x();
infoBottom -= st::msgDateImgDelta + st::msgDateImgPadding.y();
break;
}
int32 dateX = infoRight - HistoryMessage::infoWidth() + HistoryMessage::timeLeft();
@ -1493,85 +1531,47 @@ HistoryTextState HistoryMessage::getState(int x, int y, HistoryStateRequest requ
}
if (drawBubble()) {
auto fwd = Get<HistoryMessageForwarded>();
auto via = Get<HistoryMessageVia>();
auto reply = Get<HistoryMessageReply>();
int top = marginTop();
auto mediaDisplayed = _media && _media->isDisplayed();
auto top = marginTop();
QRect r(left, top, width, height - top - marginBottom());
QRect trect(r.marginsAdded(-st::msgPadding));
if (displayFromName()) {
if (y >= trect.top() && y < trect.top() + st::msgNameFont->height) {
if (x >= trect.left() && x < trect.left() + trect.width() && x < trect.left() + author()->nameText.maxWidth()) {
result.link = author()->openLink();
return result;
}
if (via && !fwd && x >= trect.left() + author()->nameText.maxWidth() + st::msgServiceFont->spacew && x < trect.left() + author()->nameText.maxWidth() + st::msgServiceFont->spacew + via->_width) {
result.link = via->_lnk;
return result;
}
}
trect.setTop(trect.top() + st::msgNameFont->height);
if (mediaDisplayed && _media->isBubbleTop()) {
trect.setY(trect.y() - st::msgPadding.top());
} else {
if (getStateFromName(x, y, trect, &result)) return result;
if (getStateForwardedInfo(x, y, trect, &result, request)) return result;
if (getStateReplyInfo(x, y, trect, &result)) return result;
if (getStateViaBotIdInfo(x, y, trect, &result)) return result;
}
if (displayForwardedFrom()) {
int32 fwdheight = ((fwd->_text.maxWidth() > trect.width()) ? 2 : 1) * st::semiboldFont->height;
if (y >= trect.top() && y < trect.top() + fwdheight) {
bool breakEverywhere = (fwd->_text.countHeight(trect.width()) > 2 * st::semiboldFont->height);
auto textRequest = request.forText();
if (breakEverywhere) {
textRequest.flags |= Text::StateRequest::Flag::BreakEverywhere;
}
textstyleSet(&st::inFwdTextStyle);
result = fwd->_text.getState(x - trect.left(), y - trect.top(), trect.width(), textRequest);
textstyleRestore();
result.symbol = 0;
result.afterSymbol = false;
if (breakEverywhere) {
result.cursor = HistoryInForwardedCursorState;
} else {
result.cursor = HistoryDefaultCursorState;
}
return result;
}
trect.setTop(trect.top() + fwdheight);
}
if (reply) {
int32 h = st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
if (y >= trect.top() && y < trect.top() + h) {
if (reply->replyToMsg && y >= trect.top() + st::msgReplyPadding.top() && y < trect.top() + st::msgReplyPadding.top() + st::msgReplyBarSize.height() && x >= trect.left() && x < trect.left() + trect.width()) {
result.link = reply->replyToLink();
}
return result;
}
trect.setTop(trect.top() + h);
}
if (via && !displayFromName() && !displayForwardedFrom()) {
if (x >= trect.left() && y >= trect.top() && y < trect.top() + st::msgNameFont->height && x < trect.left() + via->_width) {
result.link = via->_lnk;
return result;
}
trect.setTop(trect.top() + st::msgNameFont->height);
}
bool inDate = false, mediaDisplayed = _media && _media->isDisplayed();
if (!mediaDisplayed || !_media->customInfoLayout()) {
inDate = HistoryMessage::pointInTime(r.x() + r.width(), r.y() + r.height(), x, y, InfoDisplayDefault);
if (mediaDisplayed && _media->isBubbleBottom()) {
trect.setHeight(trect.height() + st::msgPadding.bottom());
}
auto needDateCheck = true;
if (mediaDisplayed) {
trect.setBottom(trect.bottom() - _media->height());
if (y >= r.bottom() - _media->height()) {
result = _media->getState(x - r.left(), y - (r.bottom() - _media->height()), request);
auto mediaAboveText = _media->isAboveMessage();
auto mediaHeight = _media->height();
auto mediaLeft = trect.x() - st::msgPadding.left();
auto mediaTop = mediaAboveText ? trect.y() : (trect.y() + trect.height() - mediaHeight);
if (y >= mediaTop && y < mediaTop + mediaHeight) {
result = _media->getState(x - mediaLeft, y - mediaTop, request);
result.symbol += _text.length();
} else {
if (mediaAboveText) {
trect.setY(trect.y() + mediaHeight);
}
getStateText(x, y, trect, &result, request);
}
needDateCheck = !_media->customInfoLayout();
} else {
getStateText(x, y, trect, &result, request);
}
if (!mediaDisplayed || (y < r.bottom() - _media->height())) {
textstyleSet(&((out() && !isPost()) ? st::outTextStyle : st::inTextStyle));
result = _text.getState(x - trect.x(), y - trect.y(), trect.width(), request.forText());
textstyleRestore();
}
if (inDate) {
result.cursor = HistoryInDateCursorState;
if (needDateCheck) {
if (HistoryMessage::pointInTime(r.x() + r.width(), r.y() + r.height(), x, y, InfoDisplayDefault)) {
result.cursor = HistoryInDateCursorState;
}
}
} else if (_media) {
result = _media->getState(x - left, y - marginTop(), request);
@ -1589,6 +1589,89 @@ HistoryTextState HistoryMessage::getState(int x, int y, HistoryStateRequest requ
return result;
}
bool HistoryMessage::getStateFromName(int x, int y, QRect &trect, HistoryTextState *outResult) const {
if (displayFromName()) {
if (y >= trect.top() && y < trect.top() + st::msgNameFont->height) {
if (x >= trect.left() && x < trect.left() + trect.width() && x < trect.left() + author()->nameText.maxWidth()) {
outResult->link = author()->openLink();
return true;
}
auto fwd = Get<HistoryMessageForwarded>();
auto via = Get<HistoryMessageVia>();
if (via && !fwd && x >= trect.left() + author()->nameText.maxWidth() + st::msgServiceFont->spacew && x < trect.left() + author()->nameText.maxWidth() + st::msgServiceFont->spacew + via->_width) {
outResult->link = via->_lnk;
return true;
}
}
trect.setTop(trect.top() + st::msgNameFont->height);
}
return false;
}
bool HistoryMessage::getStateForwardedInfo(int x, int y, QRect &trect, HistoryTextState *outResult, const HistoryStateRequest &request) const {
if (displayForwardedFrom()) {
auto fwd = Get<HistoryMessageForwarded>();
int32 fwdheight = ((fwd->_text.maxWidth() > trect.width()) ? 2 : 1) * st::semiboldFont->height;
if (y >= trect.top() && y < trect.top() + fwdheight) {
bool breakEverywhere = (fwd->_text.countHeight(trect.width()) > 2 * st::semiboldFont->height);
auto textRequest = request.forText();
if (breakEverywhere) {
textRequest.flags |= Text::StateRequest::Flag::BreakEverywhere;
}
textstyleSet(&st::inFwdTextStyle);
*outResult = fwd->_text.getState(x - trect.left(), y - trect.top(), trect.width(), textRequest);
textstyleRestore();
outResult->symbol = 0;
outResult->afterSymbol = false;
if (breakEverywhere) {
outResult->cursor = HistoryInForwardedCursorState;
} else {
outResult->cursor = HistoryDefaultCursorState;
}
return true;
}
trect.setTop(trect.top() + fwdheight);
}
return false;
}
bool HistoryMessage::getStateReplyInfo(int x, int y, QRect &trect, HistoryTextState *outResult) const {
if (auto reply = Get<HistoryMessageReply>()) {
int32 h = st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom();
if (y >= trect.top() && y < trect.top() + h) {
if (reply->replyToMsg && y >= trect.top() + st::msgReplyPadding.top() && y < trect.top() + st::msgReplyPadding.top() + st::msgReplyBarSize.height() && x >= trect.left() && x < trect.left() + trect.width()) {
outResult->link = reply->replyToLink();
}
return true;
}
trect.setTop(trect.top() + h);
}
return false;
}
bool HistoryMessage::getStateViaBotIdInfo(int x, int y, QRect &trect, HistoryTextState *outResult) const {
if (!displayFromName() && !Has<HistoryMessageForwarded>()) {
if (auto via = Get<HistoryMessageVia>()) {
if (x >= trect.left() && y >= trect.top() && y < trect.top() + st::msgNameFont->height && x < trect.left() + via->_width) {
outResult->link = via->_lnk;
return true;
}
trect.setTop(trect.top() + st::msgNameFont->height);
}
}
return false;
}
bool HistoryMessage::getStateText(int x, int y, QRect &trect, HistoryTextState *outResult, const HistoryStateRequest &request) const {
if (trect.contains(x, y)) {
textstyleSet(&((out() && !isPost()) ? st::outTextStyle : st::inTextStyle));
*outResult = _text.getState(x - trect.x(), y - trect.y(), trect.width(), request.forText());
textstyleRestore();
return true;
}
return false;
}
TextSelection HistoryMessage::adjustSelection(TextSelection selection, TextSelectType type) const {
if (!_media || selection.to <= _text.length()) {
return _text.adjustSelection(selection, type);

View File

@ -174,7 +174,7 @@ private:
void applyEditionToEmpty();
bool displayForwardedFrom() const {
if (const HistoryMessageForwarded *fwd = Get<HistoryMessageForwarded>()) {
if (auto fwd = Get<HistoryMessageForwarded>()) {
return Has<HistoryMessageVia>() || !_media || !_media->isDisplayed() || fwd->_authorOriginal->isChannel() || !_media->hideForwardedFrom();
}
return false;
@ -182,12 +182,16 @@ private:
void paintFromName(Painter &p, QRect &trect, bool selected) const;
void paintForwardedInfo(Painter &p, QRect &trect, bool selected) const;
void paintReplyInfo(Painter &p, QRect &trect, bool selected) const;
// this method draws "via @bot" if it is not painted in forwarded info or in from name
void paintViaBotIdInfo(Painter &p, QRect &trect, bool selected) const;
void paintText(Painter &p, QRect &trect, TextSelection selection) const;
bool getStateFromName(int x, int y, QRect &trect, HistoryTextState *outResult) const;
bool getStateForwardedInfo(int x, int y, QRect &trect, HistoryTextState *outResult, const HistoryStateRequest &request) const;
bool getStateReplyInfo(int x, int y, QRect &trect, HistoryTextState *outResult) const;
bool getStateViaBotIdInfo(int x, int y, QRect &trect, HistoryTextState *outResult) const;
bool getStateText(int x, int y, QRect &trect, HistoryTextState *outResult, const HistoryStateRequest &request) const;
void setMedia(const MTPMessageMedia *media);
void setReplyMarkup(const MTPReplyMarkup *markup);
@ -223,6 +227,8 @@ private:
};
void updateMediaInBubbleState();
};
inline MTPDmessage::Flags newMessageFlags(PeerData *p) {

View File

@ -33,6 +33,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "inline_bots/inline_bot_result.h"
#include "data/data_drafts.h"
#include "history/history_service_layout.h"
#include "history/history_media_types.h"
#include "profile/profile_members_widget.h"
#include "core/click_handler_types.h"
#include "stickers/emoji_pan.h"

View File

@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#pragma once
#include "inline_bots/inline_bot_layout_item.h"
#include "ui/effects/radial_animation.h"
#include "ui/text/text.h"
namespace InlineBots {
@ -113,7 +114,7 @@ private:
}
bool over;
FloatAnimation _a_over;
RadialAnimation radial;
Ui::RadialAnimation radial;
};
mutable AnimationData *_animation = nullptr;
mutable FloatAnimation _a_deleteOver;
@ -275,7 +276,7 @@ private:
anim::fvalue a_thumbOver;
Animation _a_thumbOver;
RadialAnimation radial;
Ui::RadialAnimation radial;
};
mutable std_::unique_ptr<AnimationData> _animation;

View File

@ -30,6 +30,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "media/view/media_clip_controller.h"
#include "styles/style_mediaview.h"
#include "media/media_audio.h"
#include "history/history_media_types.h"
namespace {

View File

@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#pragma once
#include "dropdown.h"
#include "ui/effects/radial_animation.h"
namespace Media {
namespace Clip {
@ -237,7 +238,7 @@ private:
LinkButton _docDownload, _docSaveAs, _docCancel;
QRect _photoRadialRect;
RadialAnimation _radial;
Ui::RadialAnimation _radial;
History *_migrated = nullptr;
History *_history = nullptr; // if conversation photos or files overview

View File

@ -654,7 +654,7 @@ inputBotInlineMessageGame#3c00f8aa reply_markup:ReplyMarkup = InputBotInlineMess
inputBotInlineResult#2cbbe15a flags:# id:string type:string title:flags.1?string description:flags.2?string url:flags.3?string thumb_url:flags.4?string content_url:flags.5?string content_type:flags.5?string w:flags.6?int h:flags.6?int duration:flags.7?int send_message:InputBotInlineMessage = InputBotInlineResult;
inputBotInlineResultPhoto#a8d864a7 id:string type:string photo:InputPhoto send_message:InputBotInlineMessage = InputBotInlineResult;
inputBotInlineResultDocument#fff8fdc4 flags:# id:string type:string title:flags.1?string description:flags.2?string document:InputDocument send_message:InputBotInlineMessage = InputBotInlineResult;
inputBotInlineResultGame#efff34f9 flags:# id:string short_name:string send_message:InputBotInlineMessage = InputBotInlineResult;
inputBotInlineResultGame#4fa417f2 id:string short_name:string send_message:InputBotInlineMessage = InputBotInlineResult;
botInlineMessageMediaAuto#a74b15b flags:# caption:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
botInlineMessageText#8c7f65e2 flags:# no_webpage:flags.0?true message:string entities:flags.1?Vector<MessageEntity> reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
@ -725,7 +725,7 @@ maskCoords#aed6dbb2 n:int x:double y:double zoom:double = MaskCoords;
inputStickeredMediaPhoto#4a992157 id:InputPhoto = InputStickeredMedia;
inputStickeredMediaDocument#438865b id:InputDocument = InputStickeredMedia;
game#b351c590 flags:# id:long access_hash:long short_name:string title:string description:string url:string photo:Photo document:flags.0?Document = Game;
game#bdf9653b flags:# id:long access_hash:long short_name:string title:string description:string photo:Photo document:flags.0?Document = Game;
inputGameID#32c3e77 id:long access_hash:long = InputGame;
inputGameShortName#c331e80a bot_id:InputUser short_name:string = InputGame;

View File

@ -5471,10 +5471,9 @@ void _serialize_inputBotInlineResultGame(MTPStringLogger &to, int32 stage, int32
to.add("\n").addSpaces(lev);
}
switch (stage) {
case 0: to.add(" flags: "); ++stages.back(); if (start >= end) throw Exception("start >= end in flags"); else flags.back() = *start; types.push_back(mtpc_flags); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" id: "); ++stages.back(); types.push_back(mtpc_string+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 2: to.add(" short_name: "); ++stages.back(); types.push_back(mtpc_string+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 3: to.add(" send_message: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 0: to.add(" id: "); ++stages.back(); types.push_back(mtpc_string+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 1: to.add(" short_name: "); ++stages.back(); types.push_back(mtpc_string+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 2: to.add(" send_message: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
}
@ -6064,9 +6063,8 @@ void _serialize_game(MTPStringLogger &to, int32 stage, int32 lev, Types &types,
case 3: to.add(" short_name: "); ++stages.back(); types.push_back(mtpc_string+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 4: to.add(" title: "); ++stages.back(); types.push_back(mtpc_string+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 5: to.add(" description: "); ++stages.back(); types.push_back(mtpc_string+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 6: to.add(" url: "); ++stages.back(); types.push_back(mtpc_string+0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 7: to.add(" photo: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 8: to.add(" document: "); ++stages.back(); if (flag & MTPDgame::Flag::f_document) { types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 0 IN FIELD flags ]"); } break;
case 6: to.add(" photo: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
case 7: to.add(" document: "); ++stages.back(); if (flag & MTPDgame::Flag::f_document) { types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 0 IN FIELD flags ]"); } break;
default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
}
}

View File

@ -478,7 +478,7 @@ enum {
mtpc_inputBotInlineResult = 0x2cbbe15a,
mtpc_inputBotInlineResultPhoto = 0xa8d864a7,
mtpc_inputBotInlineResultDocument = 0xfff8fdc4,
mtpc_inputBotInlineResultGame = 0xefff34f9,
mtpc_inputBotInlineResultGame = 0x4fa417f2,
mtpc_botInlineMessageMediaAuto = 0xa74b15b,
mtpc_botInlineMessageText = 0x8c7f65e2,
mtpc_botInlineMessageMediaGeo = 0x3a8fd8b8,
@ -524,7 +524,7 @@ enum {
mtpc_maskCoords = 0xaed6dbb2,
mtpc_inputStickeredMediaPhoto = 0x4a992157,
mtpc_inputStickeredMediaDocument = 0x438865b,
mtpc_game = 0xb351c590,
mtpc_game = 0xbdf9653b,
mtpc_inputGameID = 0x32c3e77,
mtpc_inputGameShortName = 0xc331e80a,
mtpc_highScore = 0x58fffcd0,
@ -14738,18 +14738,11 @@ public:
class MTPDinputBotInlineResultGame : public mtpDataImpl<MTPDinputBotInlineResultGame> {
public:
enum class Flag : int32 {
MAX_FIELD = (1 << 0),
};
Q_DECLARE_FLAGS(Flags, Flag);
friend inline Flags operator~(Flag v) { return QFlag(~static_cast<int32>(v)); }
MTPDinputBotInlineResultGame() {
}
MTPDinputBotInlineResultGame(const MTPflags<MTPDinputBotInlineResultGame::Flags> &_flags, const MTPstring &_id, const MTPstring &_short_name, const MTPInputBotInlineMessage &_send_message) : vflags(_flags), vid(_id), vshort_name(_short_name), vsend_message(_send_message) {
MTPDinputBotInlineResultGame(const MTPstring &_id, const MTPstring &_short_name, const MTPInputBotInlineMessage &_send_message) : vid(_id), vshort_name(_short_name), vsend_message(_send_message) {
}
MTPflags<MTPDinputBotInlineResultGame::Flags> vflags;
MTPstring vid;
MTPstring vshort_name;
MTPInputBotInlineMessage vsend_message;
@ -15322,7 +15315,7 @@ public:
MTPDgame() {
}
MTPDgame(const MTPflags<MTPDgame::Flags> &_flags, const MTPlong &_id, const MTPlong &_access_hash, const MTPstring &_short_name, const MTPstring &_title, const MTPstring &_description, const MTPstring &_url, const MTPPhoto &_photo, const MTPDocument &_document) : vflags(_flags), vid(_id), vaccess_hash(_access_hash), vshort_name(_short_name), vtitle(_title), vdescription(_description), vurl(_url), vphoto(_photo), vdocument(_document) {
MTPDgame(const MTPflags<MTPDgame::Flags> &_flags, const MTPlong &_id, const MTPlong &_access_hash, const MTPstring &_short_name, const MTPstring &_title, const MTPstring &_description, const MTPPhoto &_photo, const MTPDocument &_document) : vflags(_flags), vid(_id), vaccess_hash(_access_hash), vshort_name(_short_name), vtitle(_title), vdescription(_description), vphoto(_photo), vdocument(_document) {
}
MTPflags<MTPDgame::Flags> vflags;
@ -15331,7 +15324,6 @@ public:
MTPstring vshort_name;
MTPstring vtitle;
MTPstring vdescription;
MTPstring vurl;
MTPPhoto vphoto;
MTPDocument vdocument;
};
@ -25161,8 +25153,8 @@ public:
inline static MTPinputBotInlineResult new_inputBotInlineResultDocument(const MTPflags<MTPDinputBotInlineResultDocument::Flags> &_flags, const MTPstring &_id, const MTPstring &_type, const MTPstring &_title, const MTPstring &_description, const MTPInputDocument &_document, const MTPInputBotInlineMessage &_send_message) {
return MTPinputBotInlineResult(new MTPDinputBotInlineResultDocument(_flags, _id, _type, _title, _description, _document, _send_message));
}
inline static MTPinputBotInlineResult new_inputBotInlineResultGame(const MTPflags<MTPDinputBotInlineResultGame::Flags> &_flags, const MTPstring &_id, const MTPstring &_short_name, const MTPInputBotInlineMessage &_send_message) {
return MTPinputBotInlineResult(new MTPDinputBotInlineResultGame(_flags, _id, _short_name, _send_message));
inline static MTPinputBotInlineResult new_inputBotInlineResultGame(const MTPstring &_id, const MTPstring &_short_name, const MTPInputBotInlineMessage &_send_message) {
return MTPinputBotInlineResult(new MTPDinputBotInlineResultGame(_id, _short_name, _send_message));
}
inline static MTPbotInlineMessage new_botInlineMessageMediaAuto(const MTPflags<MTPDbotInlineMessageMediaAuto::Flags> &_flags, const MTPstring &_caption, const MTPReplyMarkup &_reply_markup) {
return MTPbotInlineMessage(new MTPDbotInlineMessageMediaAuto(_flags, _caption, _reply_markup));
@ -25299,8 +25291,8 @@ public:
inline static MTPinputStickeredMedia new_inputStickeredMediaDocument(const MTPInputDocument &_id) {
return MTPinputStickeredMedia(new MTPDinputStickeredMediaDocument(_id));
}
inline static MTPgame new_game(const MTPflags<MTPDgame::Flags> &_flags, const MTPlong &_id, const MTPlong &_access_hash, const MTPstring &_short_name, const MTPstring &_title, const MTPstring &_description, const MTPstring &_url, const MTPPhoto &_photo, const MTPDocument &_document) {
return MTPgame(new MTPDgame(_flags, _id, _access_hash, _short_name, _title, _description, _url, _photo, _document));
inline static MTPgame new_game(const MTPflags<MTPDgame::Flags> &_flags, const MTPlong &_id, const MTPlong &_access_hash, const MTPstring &_short_name, const MTPstring &_title, const MTPstring &_description, const MTPPhoto &_photo, const MTPDocument &_document) {
return MTPgame(new MTPDgame(_flags, _id, _access_hash, _short_name, _title, _description, _photo, _document));
}
inline static MTPinputGame new_inputGameID(const MTPlong &_id, const MTPlong &_access_hash) {
return MTPinputGame(new MTPDinputGameID(_id, _access_hash));
@ -35831,7 +35823,7 @@ inline uint32 MTPinputBotInlineResult::innerLength() const {
}
case mtpc_inputBotInlineResultGame: {
const MTPDinputBotInlineResultGame &v(c_inputBotInlineResultGame());
return v.vflags.innerLength() + v.vid.innerLength() + v.vshort_name.innerLength() + v.vsend_message.innerLength();
return v.vid.innerLength() + v.vshort_name.innerLength() + v.vsend_message.innerLength();
}
}
return 0;
@ -35882,7 +35874,6 @@ inline void MTPinputBotInlineResult::read(const mtpPrime *&from, const mtpPrime
case mtpc_inputBotInlineResultGame: _type = cons; {
if (!data) setData(new MTPDinputBotInlineResultGame());
MTPDinputBotInlineResultGame &v(_inputBotInlineResultGame());
v.vflags.read(from, end);
v.vid.read(from, end);
v.vshort_name.read(from, end);
v.vsend_message.read(from, end);
@ -35927,7 +35918,6 @@ inline void MTPinputBotInlineResult::write(mtpBuffer &to) const {
} break;
case mtpc_inputBotInlineResultGame: {
const MTPDinputBotInlineResultGame &v(c_inputBotInlineResultGame());
v.vflags.write(to);
v.vid.write(to);
v.vshort_name.write(to);
v.vsend_message.write(to);
@ -35962,8 +35952,8 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(MTPDinputBotInlineResultDocument::Flags)
inline MTPinputBotInlineResult MTP_inputBotInlineResultDocument(const MTPflags<MTPDinputBotInlineResultDocument::Flags> &_flags, const MTPstring &_id, const MTPstring &_type, const MTPstring &_title, const MTPstring &_description, const MTPInputDocument &_document, const MTPInputBotInlineMessage &_send_message) {
return MTP::internal::TypeCreator::new_inputBotInlineResultDocument(_flags, _id, _type, _title, _description, _document, _send_message);
}
inline MTPinputBotInlineResult MTP_inputBotInlineResultGame(const MTPflags<MTPDinputBotInlineResultGame::Flags> &_flags, const MTPstring &_id, const MTPstring &_short_name, const MTPInputBotInlineMessage &_send_message) {
return MTP::internal::TypeCreator::new_inputBotInlineResultGame(_flags, _id, _short_name, _send_message);
inline MTPinputBotInlineResult MTP_inputBotInlineResultGame(const MTPstring &_id, const MTPstring &_short_name, const MTPInputBotInlineMessage &_send_message) {
return MTP::internal::TypeCreator::new_inputBotInlineResultGame(_id, _short_name, _send_message);
}
inline uint32 MTPbotInlineMessage::innerLength() const {
@ -37184,7 +37174,7 @@ inline MTPgame::MTPgame() : mtpDataOwner(new MTPDgame()) {
inline uint32 MTPgame::innerLength() const {
const MTPDgame &v(c_game());
return v.vflags.innerLength() + v.vid.innerLength() + v.vaccess_hash.innerLength() + v.vshort_name.innerLength() + v.vtitle.innerLength() + v.vdescription.innerLength() + v.vurl.innerLength() + v.vphoto.innerLength() + (v.has_document() ? v.vdocument.innerLength() : 0);
return v.vflags.innerLength() + v.vid.innerLength() + v.vaccess_hash.innerLength() + v.vshort_name.innerLength() + v.vtitle.innerLength() + v.vdescription.innerLength() + v.vphoto.innerLength() + (v.has_document() ? v.vdocument.innerLength() : 0);
}
inline mtpTypeId MTPgame::type() const {
return mtpc_game;
@ -37200,7 +37190,6 @@ inline void MTPgame::read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId
v.vshort_name.read(from, end);
v.vtitle.read(from, end);
v.vdescription.read(from, end);
v.vurl.read(from, end);
v.vphoto.read(from, end);
if (v.has_document()) { v.vdocument.read(from, end); } else { v.vdocument = MTPDocument(); }
}
@ -37212,15 +37201,14 @@ inline void MTPgame::write(mtpBuffer &to) const {
v.vshort_name.write(to);
v.vtitle.write(to);
v.vdescription.write(to);
v.vurl.write(to);
v.vphoto.write(to);
if (v.has_document()) v.vdocument.write(to);
}
inline MTPgame::MTPgame(MTPDgame *_data) : mtpDataOwner(_data) {
}
Q_DECLARE_OPERATORS_FOR_FLAGS(MTPDgame::Flags)
inline MTPgame MTP_game(const MTPflags<MTPDgame::Flags> &_flags, const MTPlong &_id, const MTPlong &_access_hash, const MTPstring &_short_name, const MTPstring &_title, const MTPstring &_description, const MTPstring &_url, const MTPPhoto &_photo, const MTPDocument &_document) {
return MTP::internal::TypeCreator::new_game(_flags, _id, _access_hash, _short_name, _title, _description, _url, _photo, _document);
inline MTPgame MTP_game(const MTPflags<MTPDgame::Flags> &_flags, const MTPlong &_id, const MTPlong &_access_hash, const MTPstring &_short_name, const MTPstring &_title, const MTPstring &_description, const MTPPhoto &_photo, const MTPDocument &_document) {
return MTP::internal::TypeCreator::new_game(_flags, _id, _access_hash, _short_name, _title, _description, _photo, _document);
}
inline uint32 MTPinputGame::innerLength() const {

View File

@ -33,6 +33,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "playerwidget.h"
#include "media/media_audio.h"
#include "localstorage.h"
#include "history/history_media_types.h"
namespace Overview {
namespace Layout {
@ -91,7 +92,7 @@ void RadialProgressItem::step_radial(uint64 ms, bool timer) {
void RadialProgressItem::ensureRadial() const {
if (!_radial) {
_radial = new RadialAnimation(animation(const_cast<RadialProgressItem*>(this), &RadialProgressItem::step_radial));
_radial = new Ui::RadialAnimation(animation(const_cast<RadialProgressItem*>(this), &RadialProgressItem::step_radial));
}
}

View File

@ -22,6 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "layout.h"
#include "core/click_handler_types.h"
#include "ui/effects/radial_animation.h"
namespace Overview {
namespace Layout {
@ -130,7 +131,7 @@ protected:
return false;
}
mutable RadialAnimation *_radial;
mutable Ui::RadialAnimation *_radial;
anim::fvalue a_iconOver;
mutable Animation _a_iconOver;

View File

@ -34,6 +34,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "application.h"
#include "playerwidget.h"
#include "overview/overview_layout.h"
#include "history/history_media_types.h"
// flick scroll taken from http://qt-project.org/doc/qt-4.8/demos-embedded-anomaly-src-flickcharm-cpp.html

View File

@ -30,6 +30,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "mainwidget.h"
#include "localstorage.h"
#include "media/media_audio.h"
#include "history/history_media_types.h"
PlayerWidget::PlayerWidget(QWidget *parent) : TWidget(parent)
, _a_state(animation(this, &PlayerWidget::step_state))

View File

@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#pragma once
#include "settings/settings_block_widget.h"
#include "ui/effects/radial_animation.h"
#include "ui/filedialog.h"
class LinkButton;
@ -61,7 +62,7 @@ private:
ChildWidget<LinkButton> _chooseFromGallery;
ChildWidget<LinkButton> _chooseFromFile;
RadialAnimation _radial;
Ui::RadialAnimation _radial;
};

View File

@ -33,6 +33,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "boxes/confirmbox.h"
#include "media/media_audio.h"
#include "localstorage.h"
#include "history/history_media_types.h"
namespace {
int peerColorIndex(const PeerId &peer) {
@ -1628,12 +1629,11 @@ WebPageData::WebPageData(const WebPageId &id, WebPageType type, const QString &u
, pendingTill(pendingTill) {
}
GameData::GameData(const GameId &id, const uint64 &accessHash, const QString &shortName, const QString &title, const QString &description, const QString &url, PhotoData *photo, DocumentData *document) : id(id)
GameData::GameData(const GameId &id, const uint64 &accessHash, const QString &shortName, const QString &title, const QString &description, PhotoData *photo, DocumentData *document) : id(id)
, accessHash(accessHash)
, shortName(shortName)
, title(title)
, description(description)
, url(url)
, photo(photo)
, document(document) {
}

View File

@ -1368,7 +1368,7 @@ struct WebPageData {
};
struct GameData {
GameData(const GameId &id, const uint64 &accessHash = 0, const QString &shortName = QString(), const QString &title = QString(), const QString &description = QString(), const QString &url = QString(), PhotoData *photo = nullptr, DocumentData *doc = nullptr);
GameData(const GameId &id, const uint64 &accessHash = 0, const QString &shortName = QString(), const QString &title = QString(), const QString &description = QString(), PhotoData *photo = nullptr, DocumentData *doc = nullptr);
void forget() {
if (document) document->forget();
@ -1377,7 +1377,7 @@ struct GameData {
GameId id;
uint64 accessHash;
QString shortName, title, description, url;
QString shortName, title, description;
PhotoData *photo;
DocumentData *document;

View File

@ -0,0 +1,96 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#include "stdafx.h"
#include "ui/effects/radial_animation.h"
namespace Ui {
RadialAnimation::RadialAnimation(AnimationCallbacks &&callbacks)
: a_arcEnd(0, 0)
, a_arcStart(0, FullArcLength)
, _animation(std_::move(callbacks)) {
}
void RadialAnimation::start(float64 prg) {
_firstStart = _lastStart = _lastTime = getms();
int32 iprg = qRound(qMax(prg, 0.0001) * AlmostFullArcLength), iprgstrict = qRound(prg * AlmostFullArcLength);
a_arcEnd = anim::ivalue(iprgstrict, iprg);
_animation.start();
}
void RadialAnimation::update(float64 prg, bool finished, uint64 ms) {
int32 iprg = qRound(qMax(prg, 0.0001) * AlmostFullArcLength);
if (iprg != a_arcEnd.to()) {
a_arcEnd.start(iprg);
_lastStart = _lastTime;
}
_lastTime = ms;
float64 dt = float64(ms - _lastStart), fulldt = float64(ms - _firstStart);
_opacity = qMin(fulldt / st::radialDuration, 1.);
if (!finished) {
a_arcEnd.update(1. - (st::radialDuration / (st::radialDuration + dt)), anim::linear);
} else if (dt >= st::radialDuration) {
a_arcEnd.update(1, anim::linear);
stop();
} else {
float64 r = dt / st::radialDuration;
a_arcEnd.update(r, anim::linear);
_opacity *= 1 - r;
}
float64 fromstart = fulldt / st::radialPeriod;
a_arcStart.update(fromstart - std::floor(fromstart), anim::linear);
}
void RadialAnimation::stop() {
_firstStart = _lastStart = _lastTime = 0;
a_arcEnd = anim::ivalue(0, 0);
_animation.stop();
}
void RadialAnimation::step(uint64 ms) {
_animation.step(ms);
}
void RadialAnimation::draw(Painter &p, const QRect &inner, int32 thickness, const style::color &color) {
float64 o = p.opacity();
p.setOpacity(o * _opacity);
QPen pen(color->p), was(p.pen());
pen.setWidth(thickness);
p.setPen(pen);
int32 len = MinArcLength + a_arcEnd.current();
int32 from = QuarterArcLength - a_arcStart.current() - len;
if (rtl()) {
from = QuarterArcLength - (from - QuarterArcLength) - len;
if (from < 0) from += FullArcLength;
}
p.setRenderHint(QPainter::HighQualityAntialiasing);
p.drawArc(inner, from, len);
p.setRenderHint(QPainter::HighQualityAntialiasing, false);
p.setPen(was);
p.setOpacity(o);
}
} // namespace Ui

View File

@ -0,0 +1,57 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
namespace Ui {
class RadialAnimation {
public:
RadialAnimation(AnimationCallbacks &&callbacks);
float64 opacity() const {
return _opacity;
}
bool animating() const {
return _animation.animating();
}
void start(float64 prg);
void update(float64 prg, bool finished, uint64 ms);
void stop();
void step(uint64 ms);
void step() {
step(getms());
}
void draw(Painter &p, const QRect &inner, int32 thickness, const style::color &color);
private:
uint64 _firstStart = 0;
uint64 _lastStart = 0;
uint64 _lastTime = 0;
float64 _opacity = 0.;
anim::ivalue a_arcEnd, a_arcStart;
Animation _animation;
};
} // namespace Ui

View File

@ -2891,6 +2891,10 @@ TextSelection Text::adjustSelection(TextSelection selection, TextSelectType sele
return { from, to };
}
bool Text::isEmpty() const {
return _blocks.empty() || _blocks[0]->type() == TextBlockTSkip;
}
template <typename AppendPartCallback, typename ClickHandlerStartCallback, typename ClickHandlerFinishCallback, typename FlagsChangeCallback>
void Text::enumerateText(TextSelection selection, AppendPartCallback appendPartCallback, ClickHandlerStartCallback clickHandlerStartCallback, ClickHandlerFinishCallback clickHandlerFinishCallback, FlagsChangeCallback flagsChangeCallback) const {
if (isEmpty() || selection.empty()) {

View File

@ -173,9 +173,7 @@ public:
return (selection.from == 0) && (selection.to >= _text.size());
}
bool isEmpty() const {
return _text.isEmpty();
}
bool isEmpty() const;
bool isNull() const {
return !_font;
}

View File

@ -222,8 +222,9 @@
'<(src_loc)/history/history_item.h',
'<(src_loc)/history/history_location_manager.cpp',
'<(src_loc)/history/history_location_manager.h',
'<(src_loc)/history/history_media.cpp',
'<(src_loc)/history/history_media.h',
'<(src_loc)/history/history_media_types.cpp',
'<(src_loc)/history/history_media_types.h',
'<(src_loc)/history/history_message.cpp',
'<(src_loc)/history/history_message.h',
'<(src_loc)/history/history_service_layout.cpp',
@ -404,6 +405,8 @@
'<(src_loc)/ui/buttons/round_button.h',
'<(src_loc)/ui/effects/fade_animation.cpp',
'<(src_loc)/ui/effects/fade_animation.h',
'<(src_loc)/ui/effects/radial_animation.cpp',
'<(src_loc)/ui/effects/radial_animation.h',
'<(src_loc)/ui/style/style_core.cpp',
'<(src_loc)/ui/style/style_core.h',
'<(src_loc)/ui/style/style_core_color.cpp',