Game sharing and inline results supported. Qt patch updated.

Qt patch now disables QT_SCALE_FACTOR and other HighDpi
environment variables reading because tdesktop doesn't support them.
This commit is contained in:
John Preston 2016-09-28 19:23:25 +03:00
parent 5529e24000
commit 344890c533
24 changed files with 546 additions and 83 deletions

View File

@ -47,6 +47,20 @@ index 14e4fd1..c31c62b 100644
{ 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 14, 9, 11, 11 }, { 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 14, 9, 11, 11 },
{ 3, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 14, 9, 11, 11 }, { 3, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 14, 9, 11, 11 },
{ 3, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 14, 9, 11, 11 }, { 3, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 14, 9, 11, 11 },
diff --git a/src/gui/kernel/qhighdpiscaling.cpp b/src/gui/kernel/qhighdpiscaling.cpp
index b0ef2a2..7d5f7bc 100644
--- a/src/gui/kernel/qhighdpiscaling.cpp
+++ b/src/gui/kernel/qhighdpiscaling.cpp
@@ -51,6 +51,9 @@ static const char screenFactorsEnvVar[] = "QT_SCREEN_SCALE_FACTORS";
static inline qreal initialGlobalScaleFactor()
{
+ // Disable environment variable dpi scaling changing.
+ // It is not supported by Telegram Desktop :(
+ return 1.;
qreal result = 1;
if (qEnvironmentVariableIsSet(scaleFactorEnvVar)) {
diff --git a/src/gui/kernel/qplatformdialoghelper.h b/src/gui/kernel/qplatformdialoghelper.h diff --git a/src/gui/kernel/qplatformdialoghelper.h b/src/gui/kernel/qplatformdialoghelper.h
index 5b2f4ec..346a26f 100644 index 5b2f4ec..346a26f 100644
--- a/src/gui/kernel/qplatformdialoghelper.h --- a/src/gui/kernel/qplatformdialoghelper.h

View File

@ -580,6 +580,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_action_pinned_media_contact" = "a contact information"; "lng_action_pinned_media_contact" = "a contact information";
"lng_action_pinned_media_location" = "a location mark"; "lng_action_pinned_media_location" = "a location mark";
"lng_action_pinned_media_sticker" = "a sticker"; "lng_action_pinned_media_sticker" = "a sticker";
"lng_action_pinned_media_emoji_sticker" = "a {emoji} sticker";
"lng_action_pinned_media_game" = "a game «{game}»";
"lng_action_game_score" = "{from} scored {count:#|#|#} in {game}"; "lng_action_game_score" = "{from} scored {count:#|#|#} in {game}";
"lng_profile_migrate_reached" = "{count:_not_used_|# member|# members} limit reached"; "lng_profile_migrate_reached" = "{count:_not_used_|# member|# members} limit reached";
@ -774,6 +776,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
"lng_bot_groups_not_found" = "No groups found"; "lng_bot_groups_not_found" = "No groups found";
"lng_bot_sure_invite" = "Add the bot to «{group}»?"; "lng_bot_sure_invite" = "Add the bot to «{group}»?";
"lng_bot_already_in_group" = "The bot is already a member of the group."; "lng_bot_already_in_group" = "The bot is already a member of the group.";
"lng_bot_choose_chat" = "Choose Chat";
"lng_bot_no_chats" = "You have no chats";
"lng_bot_chats_not_found" = "No chats found";
"lng_bot_sure_share_game" = "Share the game with {user}?";
"lng_bot_sure_share_game_group" = "Share the game with «{group}»?";
"lng_typing" = "typing"; "lng_typing" = "typing";
"lng_user_typing" = "{user} is typing"; "lng_user_typing" = "{user} is typing";

View File

@ -1856,8 +1856,9 @@ namespace {
gamesData.erase(i); gamesData.erase(i);
} }
convert->id = game; convert->id = game;
convert->accessHash = 0;
} }
if (convert->shortName.isEmpty() && !shortName.isEmpty()) { if (!convert->accessHash && accessHash) {
convert->accessHash = accessHash; convert->accessHash = accessHash;
convert->shortName = textClean(shortName); convert->shortName = textClean(shortName);
convert->title = textOneLine(textClean(title)); convert->title = textOneLine(textClean(title));
@ -1879,7 +1880,7 @@ namespace {
} else { } else {
result = i.value(); result = i.value();
if (result != convert) { if (result != convert) {
if (result->shortName.isEmpty() && !shortName.isEmpty()) { if (!result->accessHash && accessHash) {
result->accessHash = accessHash; result->accessHash = accessHash;
result->shortName = textClean(shortName); result->shortName = textClean(shortName);
result->title = textOneLine(textClean(title)); result->title = textOneLine(textClean(title));

View File

@ -650,9 +650,17 @@ namespace Sandbox {
cSetScreenScale(dbisTwo); cSetScreenScale(dbisTwo);
} }
if (application()->devicePixelRatio() > 1) { auto devicePixelRatio = application()->devicePixelRatio();
if (devicePixelRatio > 1.) {
if ((cPlatform() != dbipMac && cPlatform() != dbipMacOld) || (devicePixelRatio != 2.)) {
LOG(("Found non-trivial Device Pixel Ratio: %1").arg(devicePixelRatio));
LOG(("Environmental variables: QT_DEVICE_PIXEL_RATIO='%1'").arg(QString::fromLatin1(qgetenv("QT_DEVICE_PIXEL_RATIO"))));
LOG(("Environmental variables: QT_SCALE_FACTOR='%1'").arg(QString::fromLatin1(qgetenv("QT_SCALE_FACTOR"))));
LOG(("Environmental variables: QT_AUTO_SCREEN_SCALE_FACTOR='%1'").arg(QString::fromLatin1(qgetenv("QT_AUTO_SCREEN_SCALE_FACTOR"))));
LOG(("Environmental variables: QT_SCREEN_SCALE_FACTORS='%1'").arg(QString::fromLatin1(qgetenv("QT_SCREEN_SCALE_FACTORS"))));
}
cSetRetina(true); cSetRetina(true);
cSetRetinaFactor(application()->devicePixelRatio()); cSetRetinaFactor(devicePixelRatio);
cSetIntRetinaFactor(int32(cRetinaFactor())); cSetIntRetinaFactor(int32(cRetinaFactor()));
cSetConfigScale(dbisOne); cSetConfigScale(dbisOne);
cSetRealScale(dbisOne); cSetRealScale(dbisOne);

View File

@ -86,6 +86,17 @@ ContactsInner::ContactsInner(ChatData *chat, MembersFilter membersFilter) : TWid
init(); init();
} }
template <typename FilterCallback>
void ContactsInner::addDialogsToList(FilterCallback callback) {
auto v = App::main()->dialogsList();
for_const (auto row, *v) {
auto peer = row->history()->peer;
if (callback(peer)) {
_contacts->addToEnd(row->history());
}
}
}
ContactsInner::ContactsInner(UserData *bot) : TWidget() ContactsInner::ContactsInner(UserData *bot) : TWidget()
, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom()) , _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom())
, _bot(bot) , _bot(bot)
@ -93,14 +104,25 @@ ContactsInner::ContactsInner(UserData *bot) : TWidget()
, _customList(std_::make_unique<Dialogs::IndexedList>(Dialogs::SortMode::Add)) , _customList(std_::make_unique<Dialogs::IndexedList>(Dialogs::SortMode::Add))
, _contacts(_customList.get()) , _contacts(_customList.get())
, _addContactLnk(this, lang(lng_add_contact_button)) { , _addContactLnk(this, lang(lng_add_contact_button)) {
auto v = App::main()->dialogsList(); if (sharingBotGame()) {
for_const (auto row, *v) { addDialogsToList([](PeerData *peer) {
auto peer = row->history()->peer; if (peer->canWrite()) {
if (peer->isChat() && peer->asChat()->canEdit()) { if (auto channel = peer->asChannel()) {
_contacts->addToEnd(row->history()); return !channel->isBroadcast();
} else if (peer->isMegagroup() && (peer->asChannel()->amCreator() || peer->asChannel()->amEditor())) { }
_contacts->addToEnd(row->history()); return true;
} }
return false;
});
} else {
addDialogsToList([](PeerData *peer) {
if (peer->isChat() && peer->asChat()->canEdit()) {
return true;
} else if (peer->isMegagroup() && (peer->asChannel()->amCreator() || peer->asChannel()->amEditor())) {
return true;
}
return false;
});
} }
init(); init();
} }
@ -166,8 +188,22 @@ void ContactsInner::onPeerNameChanged(PeerData *peer, const PeerData::Names &old
} }
void ContactsInner::onAddBot() { void ContactsInner::onAddBot() {
if (_bot->botInfo && !_bot->botInfo->startGroupToken.isEmpty()) { if (auto &info = _bot->botInfo) {
MTP::send(MTPmessages_StartBot(_bot->inputUser, _addToPeer->input, MTP_long(rand_value<uint64>()), MTP_string(_bot->botInfo->startGroupToken)), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::addParticipantFail, _bot)); if (!info->shareGameShortName.isEmpty()) {
MTPmessages_SendMedia::Flags sendFlags = 0;
auto history = App::historyLoaded(_addToPeer);
auto afterRequestId = history ? history->sendRequestId : 0;
auto randomId = rand_value<uint64>();
auto requestId = MTP::send(MTPmessages_SendMedia(MTP_flags(sendFlags), _addToPeer->input, MTP_int(0), MTP_inputMediaGame(MTP_inputGameShortName(_bot->inputUser, MTP_string(info->shareGameShortName))), MTP_long(randomId), MTPnullMarkup), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::sendMessageFail), 0, 0, afterRequestId);
if (history) {
history->sendRequestId = requestId;
}
} else if (!info->startGroupToken.isEmpty()) {
MTP::send(MTPmessages_StartBot(_bot->inputUser, _addToPeer->input, MTP_long(rand_value<uint64>()), MTP_string(info->startGroupToken)), App::main()->rpcDone(&MainWidget::sentUpdatesReceived), App::main()->rpcFail(&MainWidget::addParticipantFail, _bot));
} else {
App::main()->addParticipants(_addToPeer, QVector<UserData*>(1, _bot));
}
} else { } else {
App::main()->addParticipants(_addToPeer, QVector<UserData*>(1, _bot)); App::main()->addParticipants(_addToPeer, QVector<UserData*>(1, _bot));
} }
@ -511,7 +547,7 @@ void ContactsInner::paintEvent(QPaintEvent *e) {
QString text; QString text;
int32 skip = 0; int32 skip = 0;
if (bot()) { if (bot()) {
text = lang(cDialogsReceived() ? lng_bot_no_groups : lng_contacts_loading); text = lang((cDialogsReceived() && !_searching) ? (sharingBotGame() ? lng_bot_no_chats : lng_bot_no_groups) : lng_contacts_loading);
} else if (_chat && _membersFilter == MembersFilterAdmins) { } else if (_chat && _membersFilter == MembersFilterAdmins) {
text = lang(lng_contacts_loading); text = lang(lng_contacts_loading);
p.fillRect(0, 0, width(), _newItemHeight - st::contactsPadding.bottom() - st::lineWidth, st::contactsAboutBg); p.fillRect(0, 0, width(), _newItemHeight - st::contactsPadding.bottom() - st::lineWidth, st::contactsAboutBg);
@ -538,7 +574,7 @@ void ContactsInner::paintEvent(QPaintEvent *e) {
p.setPen(st::noContactsColor->p); p.setPen(st::noContactsColor->p);
QString text; QString text;
if (bot()) { if (bot()) {
text = lang(cDialogsReceived() ? lng_bot_groups_not_found : lng_contacts_loading); text = lang((cDialogsReceived() && !_searching) ? (sharingBotGame() ? lng_bot_chats_not_found : lng_bot_groups_not_found) : lng_contacts_loading);
} else if (_chat && _membersFilter == MembersFilterAdmins) { } else if (_chat && _membersFilter == MembersFilterAdmins) {
text = lang(_chat->participants.isEmpty() ? lng_contacts_loading : lng_contacts_not_found); text = lang(_chat->participants.isEmpty() ? lng_contacts_loading : lng_contacts_not_found);
} else { } else {
@ -702,11 +738,22 @@ void ContactsInner::chooseParticipant() {
connect(_addAdminBox, SIGNAL(confirmed()), this, SLOT(onAddAdmin())); connect(_addAdminBox, SIGNAL(confirmed()), this, SLOT(onAddAdmin()));
connect(_addAdminBox, SIGNAL(destroyed(QObject*)), this, SLOT(onNoAddAdminBox(QObject*))); connect(_addAdminBox, SIGNAL(destroyed(QObject*)), this, SLOT(onNoAddAdminBox(QObject*)));
Ui::showLayer(_addAdminBox, KeepOtherLayers); Ui::showLayer(_addAdminBox, KeepOtherLayers);
} else if (sharingBotGame()) {
_addToPeer = peer;
auto confirmText = [peer] {
if (peer->isUser()) {
return lng_bot_sure_share_game(lt_user, App::peerName(peer));
}
return lng_bot_sure_share_game_group(lt_group, peer->name);
};
auto box = std_::make_unique<ConfirmBox>(confirmText());
connect(box.get(), SIGNAL(confirmed()), this, SLOT(onAddBot()));
Ui::showLayer(box.release(), KeepOtherLayers);
} else if (bot() && (peer->isChat() || peer->isMegagroup())) { } else if (bot() && (peer->isChat() || peer->isMegagroup())) {
_addToPeer = peer; _addToPeer = peer;
ConfirmBox *box = new ConfirmBox(lng_bot_sure_invite(lt_group, peer->name)); auto box = std_::make_unique<ConfirmBox>(lng_bot_sure_invite(lt_group, peer->name));
connect(box, SIGNAL(confirmed()), this, SLOT(onAddBot())); connect(box.get(), SIGNAL(confirmed()), this, SLOT(onAddBot()));
Ui::showLayer(box, KeepOtherLayers); Ui::showLayer(box.release(), KeepOtherLayers);
} else { } else {
Ui::hideSettingsAndLayer(true); Ui::hideSettingsAndLayer(true);
App::main()->choosePeer(peer->id, ShowAtUnreadMsgId); App::main()->choosePeer(peer->id, ShowAtUnreadMsgId);
@ -899,7 +946,7 @@ void ContactsInner::updateFilter(QString filter) {
_mouseSel = false; _mouseSel = false;
refresh(); refresh();
if (!bot() && (!_chat || _membersFilter != MembersFilterAdmins)) { if ((!bot() || sharingBotGame()) && (!_chat || _membersFilter != MembersFilterAdmins)) {
_searching = true; _searching = true;
emit searchByUsername(); emit searchByUsername();
} }
@ -966,6 +1013,15 @@ void ContactsInner::peopleReceived(const QString &query, const QVector<MTPPeer>
} else { } else {
continue; // skip continue; // skip
} }
} else if (sharingBotGame()) {
if (!p->canWrite()) {
continue;
}
if (auto channel = p->asChannel()) {
if (channel->isBroadcast()) {
continue;
}
}
} }
ContactData *d = new ContactData(); ContactData *d = new ContactData();
@ -1032,6 +1088,10 @@ UserData *ContactsInner::bot() const {
return _bot; return _bot;
} }
bool ContactsInner::sharingBotGame() const {
return (_bot && _bot->botInfo) ? !_bot->botInfo->shareGameShortName.isEmpty() : false;
}
CreatingGroupType ContactsInner::creating() const { CreatingGroupType ContactsInner::creating() const {
return _creating; return _creating;
} }
@ -1040,8 +1100,11 @@ ContactsInner::~ContactsInner() {
for (ContactsData::iterator i = _contactsData.begin(), e = _contactsData.end(); i != e; ++i) { for (ContactsData::iterator i = _contactsData.begin(), e = _contactsData.end(); i != e; ++i) {
delete *i; delete *i;
} }
if (_bot || (_chat && _membersFilter == MembersFilterAdmins)) { if (_bot) {
if (_bot && _bot->botInfo) _bot->botInfo->startGroupToken = QString(); if (auto &info = _bot->botInfo) {
info->startGroupToken = QString();
info->shareGameShortName = QString();
}
} }
} }
@ -1499,6 +1562,8 @@ void ContactsBox::paintEvent(QPaintEvent *e) {
QString title(lang(addingAdmin ? lng_channel_add_admin : lng_profile_add_participant)); QString title(lang(addingAdmin ? lng_channel_add_admin : lng_profile_add_participant));
QString additional((addingAdmin || (_inner.channel() && !_inner.channel()->isMegagroup())) ? QString() : QString("%1 / %2").arg(_inner.selectedCount()).arg(Global::MegagroupSizeMax())); QString additional((addingAdmin || (_inner.channel() && !_inner.channel()->isMegagroup())) ? QString() : QString("%1 / %2").arg(_inner.selectedCount()).arg(Global::MegagroupSizeMax()));
paintTitle(p, title, additional); paintTitle(p, title, additional);
} else if (_inner.sharingBotGame()) {
paintTitle(p, lang(lng_bot_choose_chat));
} else if (_inner.bot()) { } else if (_inner.bot()) {
paintTitle(p, lang(lng_bot_choose_group)); paintTitle(p, lang(lng_bot_choose_group));
} else { } else {

View File

@ -78,6 +78,8 @@ public:
UserData *bot() const; UserData *bot() const;
CreatingGroupType creating() const; CreatingGroupType creating() const;
bool sharingBotGame() const;
int32 selectedCount() const; int32 selectedCount() const;
bool hasAlreadyMembersInChannel() const { bool hasAlreadyMembersInChannel() const {
return !_already.isEmpty(); return !_already.isEmpty();
@ -121,6 +123,9 @@ private:
void addAdminDone(const MTPUpdates &result, mtpRequestId req); void addAdminDone(const MTPUpdates &result, mtpRequestId req);
bool addAdminFail(const RPCError &error, mtpRequestId req); bool addAdminFail(const RPCError &error, mtpRequestId req);
template <typename FilterCallback>
void addDialogsToList(FilterCallback callback);
int32 _rowHeight; int32 _rowHeight;
int _newItemHeight = 0; int _newItemHeight = 0;
bool _newItemSel = false; bool _newItemSel = false;

View File

@ -87,7 +87,7 @@ void ReportBox::onChange() {
_reasonOtherText.destroy(); _reasonOtherText.destroy();
updateMaxHeight(); updateMaxHeight();
} }
if (App::wnd()) App::wnd()->setInnerFocus(); _reasonOtherText->setFocus();
} }
void ReportBox::doSetInnerFocus() { void ReportBox::doSetInnerFocus() {

View File

@ -32,11 +32,12 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "boxes/confirmbox.h" #include "boxes/confirmbox.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "ui/toast/toast.h" #include "ui/toast/toast.h"
#include "history/history_media_types.h"
ShareBox::ShareBox(CopyCallback &&copyCallback, SubmitCallback &&submitCallback) : ItemListBox(st::boxScroll) ShareBox::ShareBox(CopyCallback &&copyCallback, SubmitCallback &&submitCallback, FilterCallback &&filterCallback) : ItemListBox(st::boxScroll)
, _copyCallback(std_::move(copyCallback)) , _copyCallback(std_::move(copyCallback))
, _submitCallback(std_::move(submitCallback)) , _submitCallback(std_::move(submitCallback))
, _inner(this) , _inner(this, std_::move(filterCallback))
, _filter(this, st::boxSearchField, lang(lng_participant_filter)) , _filter(this, st::boxSearchField, lang(lng_participant_filter))
, _filterCancel(this, st::boxSearchCancel) , _filterCancel(this, st::boxSearchCancel)
, _copy(this, lang(lng_share_copy_link), st::defaultBoxButton) , _copy(this, lang(lng_share_copy_link), st::defaultBoxButton)
@ -241,7 +242,8 @@ void ShareBox::onScroll() {
namespace internal { namespace internal {
ShareInner::ShareInner(QWidget *parent) : ScrolledWidget(parent) ShareInner::ShareInner(QWidget *parent, ShareBox::FilterCallback &&filterCallback) : ScrolledWidget(parent)
, _filterCallback(std_::move(filterCallback))
, _chatsIndexed(std_::make_unique<Dialogs::IndexedList>(Dialogs::SortMode::Add)) { , _chatsIndexed(std_::make_unique<Dialogs::IndexedList>(Dialogs::SortMode::Add)) {
_rowsTop = st::shareRowsTop; _rowsTop = st::shareRowsTop;
_rowHeight = st::shareRowHeight; _rowHeight = st::shareRowHeight;
@ -250,7 +252,7 @@ ShareInner::ShareInner(QWidget *parent) : ScrolledWidget(parent)
auto dialogs = App::main()->dialogsList(); auto dialogs = App::main()->dialogsList();
for_const (auto row, dialogs->all()) { for_const (auto row, dialogs->all()) {
auto history = row->history(); auto history = row->history();
if (history->peer->canWrite()) { if (_filterCallback(history->peer)) {
_chatsIndexed->addToEnd(history); _chatsIndexed->addToEnd(history);
} }
} }
@ -863,7 +865,7 @@ void ShareInner::peopleReceived(const QString &query, const QVector<MTPPeer> &pe
} }
if (j == already) { if (j == already) {
auto *peer = App::peer(peerId); auto *peer = App::peer(peerId);
if (!peer || !peer->canWrite()) continue; if (!peer || !_filterCallback(peer)) continue;
auto chat = new Chat(peer); auto chat = new Chat(peer);
updateChatName(chat, peer); updateChatName(chat, peer);
@ -958,24 +960,15 @@ void shareGameScoreFromItem(HistoryItem *item) {
if (auto main = App::main()) { if (auto main = App::main()) {
if (auto item = App::histItemById(data->msgId)) { if (auto item = App::histItemById(data->msgId)) {
if (auto bot = item->getMessageBot()) { if (auto bot = item->getMessageBot()) {
if (auto markup = item->Get<HistoryMessageReplyMarkup>()) { if (auto media = item->getMedia()) {
for (int i = 0, rowsCount = markup->rows.size(); i != rowsCount; ++i) { if (media->type() == MediaTypeGame) {
auto &row = markup->rows[i]; auto shortName = static_cast<HistoryGame*>(media)->game()->shortName;
for (int j = 0, buttonsCount = row.size(); j != buttonsCount; ++j) {
auto &button = row[j];
if (button.type == HistoryMessageReplyMarkup::Button::Type::Game) {
auto strData = QString::fromUtf8(button.data);
auto parts = strData.split(',');
t_assert(parts.size() > 1);
QApplication::clipboard()->setText(qsl("https://telegram.me/") + bot->username + qsl("?start=") + parts[1]); QApplication::clipboard()->setText(qsl("https://telegram.me/") + bot->username + qsl("?game=") + shortName);
Ui::Toast::Config toast; Ui::Toast::Config toast;
toast.text = lang(lng_share_game_link_copied); toast.text = lang(lng_share_game_link_copied);
Ui::Toast::Show(App::wnd(), toast); Ui::Toast::Show(App::wnd(), toast);
return;
}
}
} }
} }
} }
@ -1015,7 +1008,16 @@ void shareGameScoreFromItem(HistoryItem *item) {
} }
} }
}; };
Ui::showLayer(new ShareBox(std_::move(copyCallback), std_::move(submitCallback))); auto filterCallback = [](PeerData *peer) {
if (peer->canWrite()) {
if (auto channel = peer->asChannel()) {
return !channel->isBroadcast();
}
return true;
}
return false;
};
Ui::showLayer(new ShareBox(std_::move(copyCallback), std_::move(submitCallback), std_::move(filterCallback)));
} }
} // namespace } // namespace

View File

@ -47,7 +47,8 @@ class ShareBox : public ItemListBox, public RPCSender {
public: public:
using CopyCallback = base::lambda_unique<void()>; using CopyCallback = base::lambda_unique<void()>;
using SubmitCallback = base::lambda_unique<void(const QVector<PeerData*> &)>; using SubmitCallback = base::lambda_unique<void(const QVector<PeerData*> &)>;
ShareBox(CopyCallback &&copyCallback, SubmitCallback &&submitCallback); using FilterCallback = base::lambda_unique<bool(PeerData*)>;
ShareBox(CopyCallback &&copyCallback, SubmitCallback &&submitCallback, FilterCallback &&filterCallback);
private slots: private slots:
void onFilterUpdate(); void onFilterUpdate();
@ -112,7 +113,7 @@ class ShareInner : public ScrolledWidget, public RPCSender, private base::Subscr
Q_OBJECT Q_OBJECT
public: public:
ShareInner(QWidget *parent); ShareInner(QWidget *parent, ShareBox::FilterCallback &&filterCallback);
QVector<PeerData*> selected() const; QVector<PeerData*> selected() const;
bool hasSelected() const; bool hasSelected() const;
@ -197,6 +198,7 @@ private:
int _active = -1; int _active = -1;
int _upon = -1; int _upon = -1;
ShareBox::FilterCallback _filterCallback;
std_::unique_ptr<Dialogs::IndexedList> _chatsIndexed; std_::unique_ptr<Dialogs::IndexedList> _chatsIndexed;
QString _filter; QString _filter;
using FilteredDialogs = QVector<Dialogs::Row*>; using FilteredDialogs = QVector<Dialogs::Row*>;

View File

@ -881,6 +881,10 @@ HistoryItem *History::createItemPhoto(MsgId id, MTPDmessage::Flags flags, int32
return HistoryMessage::create(this, id, flags, replyTo, viaBotId, date, from, photo, caption, markup); return HistoryMessage::create(this, id, flags, replyTo, viaBotId, date, from, photo, caption, markup);
} }
HistoryItem *History::createItemGame(MsgId id, MTPDmessage::Flags flags, int32 viaBotId, MsgId replyTo, QDateTime date, int32 from, GameData *game, const MTPReplyMarkup &markup) {
return HistoryMessage::create(this, id, flags, replyTo, viaBotId, date, from, game, markup);
}
HistoryItem *History::addNewService(MsgId msgId, QDateTime date, const QString &text, MTPDmessage::Flags flags, bool newMsg) { HistoryItem *History::addNewService(MsgId msgId, QDateTime date, const QString &text, MTPDmessage::Flags flags, bool newMsg) {
return addNewItem(HistoryService::create(this, msgId, date, text, flags), newMsg); return addNewItem(HistoryService::create(this, msgId, date, text, flags), newMsg);
} }
@ -928,6 +932,10 @@ HistoryItem *History::addNewPhoto(MsgId id, MTPDmessage::Flags flags, int32 viaB
return addNewItem(createItemPhoto(id, flags, viaBotId, replyTo, date, from, photo, caption, markup), true); return addNewItem(createItemPhoto(id, flags, viaBotId, replyTo, date, from, photo, caption, markup), true);
} }
HistoryItem *History::addNewGame(MsgId id, MTPDmessage::Flags flags, int32 viaBotId, MsgId replyTo, QDateTime date, int32 from, GameData *game, const MTPReplyMarkup &markup) {
return addNewItem(createItemGame(id, flags, viaBotId, replyTo, date, from, game, markup), true);
}
bool History::addToOverview(MediaOverviewType type, MsgId msgId, AddToOverviewMethod method) { bool History::addToOverview(MediaOverviewType type, MsgId msgId, AddToOverviewMethod method) {
bool adding = false; bool adding = false;
switch (method) { switch (method) {

View File

@ -205,6 +205,7 @@ public:
HistoryItem *addNewForwarded(MsgId id, MTPDmessage::Flags flags, QDateTime date, int32 from, HistoryMessage *item); HistoryItem *addNewForwarded(MsgId id, MTPDmessage::Flags flags, QDateTime date, int32 from, HistoryMessage *item);
HistoryItem *addNewDocument(MsgId id, MTPDmessage::Flags flags, int32 viaBotId, MsgId replyTo, QDateTime date, int32 from, DocumentData *doc, const QString &caption, const MTPReplyMarkup &markup); HistoryItem *addNewDocument(MsgId id, MTPDmessage::Flags flags, int32 viaBotId, MsgId replyTo, QDateTime date, int32 from, DocumentData *doc, const QString &caption, const MTPReplyMarkup &markup);
HistoryItem *addNewPhoto(MsgId id, MTPDmessage::Flags flags, int32 viaBotId, MsgId replyTo, QDateTime date, int32 from, PhotoData *photo, const QString &caption, const MTPReplyMarkup &markup); HistoryItem *addNewPhoto(MsgId id, MTPDmessage::Flags flags, int32 viaBotId, MsgId replyTo, QDateTime date, int32 from, PhotoData *photo, const QString &caption, const MTPReplyMarkup &markup);
HistoryItem *addNewGame(MsgId id, MTPDmessage::Flags flags, int32 viaBotId, MsgId replyTo, QDateTime date, int32 from, GameData *game, const MTPReplyMarkup &markup);
void addOlderSlice(const QVector<MTPMessage> &slice); void addOlderSlice(const QVector<MTPMessage> &slice);
void addNewerSlice(const QVector<MTPMessage> &slice); void addNewerSlice(const QVector<MTPMessage> &slice);
@ -463,6 +464,7 @@ protected:
HistoryItem *createItemForwarded(MsgId id, MTPDmessage::Flags flags, QDateTime date, int32 from, HistoryMessage *msg); HistoryItem *createItemForwarded(MsgId id, MTPDmessage::Flags flags, QDateTime date, int32 from, HistoryMessage *msg);
HistoryItem *createItemDocument(MsgId id, MTPDmessage::Flags flags, int32 viaBotId, MsgId replyTo, QDateTime date, int32 from, DocumentData *doc, const QString &caption, const MTPReplyMarkup &markup); HistoryItem *createItemDocument(MsgId id, MTPDmessage::Flags flags, int32 viaBotId, MsgId replyTo, QDateTime date, int32 from, DocumentData *doc, const QString &caption, const MTPReplyMarkup &markup);
HistoryItem *createItemPhoto(MsgId id, MTPDmessage::Flags flags, int32 viaBotId, MsgId replyTo, QDateTime date, int32 from, PhotoData *photo, const QString &caption, const MTPReplyMarkup &markup); HistoryItem *createItemPhoto(MsgId id, MTPDmessage::Flags flags, int32 viaBotId, MsgId replyTo, QDateTime date, int32 from, PhotoData *photo, const QString &caption, const MTPReplyMarkup &markup);
HistoryItem *createItemGame(MsgId id, MTPDmessage::Flags flags, int32 viaBotId, MsgId replyTo, QDateTime date, int32 from, GameData *game, const MTPReplyMarkup &markup);
HistoryItem *addNewItem(HistoryItem *adding, bool newMsg); HistoryItem *addNewItem(HistoryItem *adding, bool newMsg);
HistoryItem *addNewInTheMiddle(HistoryItem *newItem, int32 blockIndex, int32 itemIndex); HistoryItem *addNewInTheMiddle(HistoryItem *newItem, int32 blockIndex, int32 itemIndex);

View File

@ -69,7 +69,7 @@ inline void initTextOptions() {
bool needReSetInlineResultDocument(const MTPMessageMedia &media, DocumentData *existing) { bool needReSetInlineResultDocument(const MTPMessageMedia &media, DocumentData *existing) {
if (media.type() == mtpc_messageMediaDocument) { if (media.type() == mtpc_messageMediaDocument) {
if (DocumentData *document = App::feedDocument(media.c_messageMediaDocument().vdocument)) { if (auto document = App::feedDocument(media.c_messageMediaDocument().vdocument)) {
if (document == existing) { if (document == existing) {
return false; return false;
} else { } else {
@ -1964,8 +1964,6 @@ private:
} // namespace } // namespace
HistorySticker::HistorySticker(HistoryItem *parent, DocumentData *document) : HistoryMedia(parent) HistorySticker::HistorySticker(HistoryItem *parent, DocumentData *document) : HistoryMedia(parent)
, _pixw(1)
, _pixh(1)
, _data(document) , _data(document)
, _emoji(_data->sticker()->alt) { , _emoji(_data->sticker()->alt) {
_data->thumb->load(); _data->thumb->load();
@ -3103,11 +3101,12 @@ void HistoryGame::draw(Painter &p, const QRect &r, TextSelection selection, uint
p.translate(attachLeft, attachTop); p.translate(attachLeft, attachTop);
_attach->draw(p, r.translated(-attachLeft, -attachTop), attachSelection, ms); _attach->draw(p, r.translated(-attachLeft, -attachTop), attachSelection, ms);
auto pixwidth = _attach->currentWidth(); auto pixwidth = _attach->currentWidth();
auto pixheight = _attach->height();
auto gameX = st::msgDateImgDelta;
auto gameY = st::msgDateImgDelta;
auto gameW = _gameTagWidth + 2 * st::msgDateImgPadding.x(); auto gameW = _gameTagWidth + 2 * st::msgDateImgPadding.x();
auto gameH = st::msgDateFont->height + 2 * st::msgDateImgPadding.y(); auto gameH = st::msgDateFont->height + 2 * st::msgDateImgPadding.y();
auto gameX = pixwidth - st::msgDateImgDelta - gameW;
auto gameY = pixheight - st::msgDateImgDelta - gameH;
App::roundRect(p, rtlrect(gameX, gameY, gameW, gameH, pixwidth), selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners); App::roundRect(p, rtlrect(gameX, gameY, gameW, gameH, pixwidth), selected ? st::msgDateImgBgSelected : st::msgDateImgBg, selected ? DateSelectedCorners : DateCorners);
@ -3214,12 +3213,25 @@ void HistoryGame::detachFromParent() {
if (_attach) _attach->detachFromParent(); if (_attach) _attach->detachFromParent();
} }
QString HistoryGame::notificationText() const { void HistoryGame::updateSentMedia(const MTPMessageMedia &media) {
return _data->title; if (media.type() == mtpc_messageMediaGame) {
auto &game = media.c_messageMediaGame().vgame;
if (game.type() == mtpc_game) {
App::feedGame(game.c_game(), _data);
}
}
} }
QString HistoryGame::inDialogsText() const { bool HistoryGame::needReSetInlineResultMedia(const MTPMessageMedia &media) {
return textcmdLink(1, _data->title); updateSentMedia(media);
return false;
}
QString HistoryGame::notificationText() const {
QString result; // add a game controller emoji before game title
result.reserve(_data->title.size() + 3);
result.append(QChar(0xD83C)).append(QChar(0xDFAE)).append(QChar(' ')).append(_data->title);
return result;
} }
TextWithEntities HistoryGame::selectedText(TextSelection selection) const { TextWithEntities HistoryGame::selectedText(TextSelection selection) const {

View File

@ -552,6 +552,9 @@ public:
bool customInfoLayout() const override { bool customInfoLayout() const override {
return true; return true;
} }
QString emoji() const {
return _emoji;
}
private: private:
int additionalWidth(const HistoryMessageVia *via, const HistoryMessageReply *reply) const; int additionalWidth(const HistoryMessageVia *via, const HistoryMessageReply *reply) const;
@ -560,7 +563,8 @@ private:
} }
QString toString() const; QString toString() const;
int16 _pixw, _pixh; int16 _pixw = 1;
int16 _pixh = 1;
ClickHandlerPtr _packLink; ClickHandlerPtr _packLink;
DocumentData *_data; DocumentData *_data;
QString _emoji; QString _emoji;
@ -762,7 +766,6 @@ public:
} }
QString notificationText() const override; QString notificationText() const override;
QString inDialogsText() const override;
TextWithEntities selectedText(TextSelection selection) const override; TextWithEntities selectedText(TextSelection selection) const override;
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override; void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
@ -793,6 +796,9 @@ public:
return _data; return _data;
} }
void updateSentMedia(const MTPMessageMedia &media) override;
bool needReSetInlineResultMedia(const MTPMessageMedia &media) override;
bool needsBubble() const override { bool needsBubble() const override {
return true; return true;
} }

View File

@ -49,7 +49,7 @@ MediaOverviewType messageMediaToOverviewType(HistoryMedia *media) {
case MediaTypePhoto: return OverviewPhotos; case MediaTypePhoto: return OverviewPhotos;
case MediaTypeVideo: return OverviewVideos; case MediaTypeVideo: return OverviewVideos;
case MediaTypeFile: return OverviewFiles; case MediaTypeFile: return OverviewFiles;
case MediaTypeMusicFile: return media->getDocument()->isMusic() ? OverviewMusicFiles : OverviewFiles; case MediaTypeMusicFile: return media->getDocument()->isMusic() ? OverviewMusicFiles : OverviewCount;
case MediaTypeVoiceFile: return OverviewVoiceFiles; case MediaTypeVoiceFile: return OverviewVoiceFiles;
case MediaTypeGif: return media->getDocument()->isGifv() ? OverviewCount : OverviewFiles; case MediaTypeGif: return media->getDocument()->isGifv() ? OverviewCount : OverviewFiles;
default: break; default: break;
@ -465,6 +465,14 @@ HistoryMessage::HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags
setText(TextWithEntities()); setText(TextWithEntities());
} }
HistoryMessage::HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, GameData *game, const MTPReplyMarkup &markup)
: HistoryItem(history, msgId, flags, date, (flags & MTPDmessage::Flag::f_from_id) ? from : 0) {
createComponentsHelper(flags, replyTo, viaBotId, markup);
_media.reset(new HistoryGame(this, game));
setText(TextWithEntities());
}
void HistoryMessage::createComponentsHelper(MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, const MTPReplyMarkup &markup) { void HistoryMessage::createComponentsHelper(MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, const MTPReplyMarkup &markup) {
CreateConfig config; CreateConfig config;
@ -1899,7 +1907,7 @@ bool HistoryService::preparePinnedText(const QString &from, QString *outText, Li
ClickHandlerPtr second; ClickHandlerPtr second;
auto pinned = Get<HistoryServicePinned>(); auto pinned = Get<HistoryServicePinned>();
if (pinned && pinned->msg) { if (pinned && pinned->msg) {
HistoryMedia *media = pinned->msg->getMedia(); auto media = pinned->msg->getMedia();
QString mediaText; QString mediaText;
switch (media ? media->type() : MediaTypeCount) { switch (media ? media->type() : MediaTypeCount) {
case MediaTypePhoto: mediaText = lang(lng_action_pinned_media_photo); break; case MediaTypePhoto: mediaText = lang(lng_action_pinned_media_photo); break;
@ -1907,10 +1915,21 @@ bool HistoryService::preparePinnedText(const QString &from, QString *outText, Li
case MediaTypeContact: mediaText = lang(lng_action_pinned_media_contact); break; case MediaTypeContact: mediaText = lang(lng_action_pinned_media_contact); break;
case MediaTypeFile: mediaText = lang(lng_action_pinned_media_file); break; case MediaTypeFile: mediaText = lang(lng_action_pinned_media_file); break;
case MediaTypeGif: mediaText = lang(lng_action_pinned_media_gif); break; case MediaTypeGif: mediaText = lang(lng_action_pinned_media_gif); break;
case MediaTypeSticker: mediaText = lang(lng_action_pinned_media_sticker); break; case MediaTypeSticker: {
auto emoji = static_cast<HistorySticker*>(media)->emoji();
if (emoji.isEmpty()) {
mediaText = lang(lng_action_pinned_media_sticker);
} else {
mediaText = lng_action_pinned_media_emoji_sticker(lt_emoji, emoji);
}
} break;
case MediaTypeLocation: mediaText = lang(lng_action_pinned_media_location); break; case MediaTypeLocation: mediaText = lang(lng_action_pinned_media_location); break;
case MediaTypeMusicFile: mediaText = lang(lng_action_pinned_media_audio); break; case MediaTypeMusicFile: mediaText = lang(lng_action_pinned_media_audio); break;
case MediaTypeVoiceFile: mediaText = lang(lng_action_pinned_media_voice); break; case MediaTypeVoiceFile: mediaText = lang(lng_action_pinned_media_voice); break;
case MediaTypeGame: {
auto title = static_cast<HistoryGame*>(media)->game()->title;
mediaText = lng_action_pinned_media_game(lt_game, title);
} break;
} }
if (mediaText.isEmpty()) { if (mediaText.isEmpty()) {
QString original = pinned->msg->originalText().text; QString original = pinned->msg->originalText().text;

View File

@ -39,6 +39,9 @@ public:
static HistoryMessage *create(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, PhotoData *photo, const QString &caption, const MTPReplyMarkup &markup) { static HistoryMessage *create(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, PhotoData *photo, const QString &caption, const MTPReplyMarkup &markup) {
return _create(history, msgId, flags, replyTo, viaBotId, date, from, photo, caption, markup); return _create(history, msgId, flags, replyTo, viaBotId, date, from, photo, caption, markup);
} }
static HistoryMessage *create(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, GameData *game, const MTPReplyMarkup &markup) {
return _create(history, msgId, flags, replyTo, viaBotId, date, from, game, markup);
}
void initTime(); void initTime();
void initMedia(const MTPMessageMedia *media); void initMedia(const MTPMessageMedia *media);
@ -164,6 +167,7 @@ private:
HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, const TextWithEntities &textWithEntities); // local message HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, const TextWithEntities &textWithEntities); // local message
HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, DocumentData *doc, const QString &caption, const MTPReplyMarkup &markup); // local document HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, DocumentData *doc, const QString &caption, const MTPReplyMarkup &markup); // local document
HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, PhotoData *photo, const QString &caption, const MTPReplyMarkup &markup); // local photo HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, PhotoData *photo, const QString &caption, const MTPReplyMarkup &markup); // local photo
HistoryMessage(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from, GameData *game, const MTPReplyMarkup &markup); // local game
friend class HistoryItemInstantiated<HistoryMessage>; friend class HistoryItemInstantiated<HistoryMessage>;
void setEmptyText(); void setEmptyText();

View File

@ -134,7 +134,7 @@ void Gif::paint(Painter &p, const QRect &clip, const PaintContext *context) cons
bool loaded = document->loaded(), loading = document->loading(), displayLoading = document->displayLoading(); bool loaded = document->loaded(), loading = document->loading(), displayLoading = document->displayLoading();
if (loaded && !gif() && _gif != Media::Clip::BadReader) { if (loaded && !gif() && _gif != Media::Clip::BadReader) {
Gif *that = const_cast<Gif*>(this); auto that = const_cast<Gif*>(this);
that->_gif = new Media::Clip::Reader(document->location(), document->data(), [that](Media::Clip::Notification notification) { that->_gif = new Media::Clip::Reader(document->location(), document->data(), [that](Media::Clip::Notification notification) {
that->clipCallback(notification); that->clipCallback(notification);
}); });
@ -276,7 +276,6 @@ QSize Gif::countFrameSize() const {
Gif::~Gif() { Gif::~Gif() {
if (gif()) deleteAndMark(_gif); if (gif()) deleteAndMark(_gif);
deleteAndMark(_animation);
} }
void Gif::prepareThumb(int32 width, int32 height, const QSize &frame) const { void Gif::prepareThumb(int32 width, int32 height, const QSize &frame) const {
@ -306,7 +305,7 @@ void Gif::prepareThumb(int32 width, int32 height, const QSize &frame) const {
void Gif::ensureAnimation() const { void Gif::ensureAnimation() const {
if (!_animation) { if (!_animation) {
_animation = new AnimationData(animation(const_cast<Gif*>(this), &Gif::step_radial)); _animation = std_::make_unique<AnimationData>(animation(const_cast<Gif*>(this), &Gif::step_radial));
} }
} }
@ -324,8 +323,7 @@ void Gif::step_radial(uint64 ms, bool timer) {
DocumentData *document = getShownDocument(); DocumentData *document = getShownDocument();
_animation->radial.update(document->progress(), !document->loading() || document->loaded(), ms); _animation->radial.update(document->progress(), !document->loading() || document->loaded(), ms);
if (!_animation->radial.animating() && document->loaded()) { if (!_animation->radial.animating() && document->loaded()) {
delete _animation; _animation.reset();
_animation = nullptr;
} }
} }
} }
@ -1140,6 +1138,239 @@ void Article::prepareThumb(int width, int height) const {
} }
} }
Game::Game(Result *result) : ItemBase(result)
, _title(st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft - st::inlineThumbSize - st::inlineThumbSkip)
, _description(st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft - st::inlineThumbSize - st::inlineThumbSkip) {
countFrameSize();
}
void Game::countFrameSize() {
if (auto document = getResultDocument()) {
if (document->isAnimation()) {
auto documentSize = document->dimensions;
if (documentSize.isEmpty()) {
documentSize = QSize(st::inlineThumbSize, st::inlineThumbSize);
}
auto resizeByHeight1 = (documentSize.width() > documentSize.height()) && (documentSize.height() >= st::inlineThumbSize);
auto resizeByHeight2 = (documentSize.height() >= documentSize.width()) && (documentSize.width() < st::inlineThumbSize);
if (resizeByHeight1 || resizeByHeight2) {
if (documentSize.height() > st::inlineThumbSize) {
_frameSize = QSize((documentSize.width() * st::inlineThumbSize) / documentSize.height(), st::inlineThumbSize);
}
} else {
if (documentSize.width() > st::inlineThumbSize) {
_frameSize = QSize(st::inlineThumbSize, (documentSize.height() * st::inlineThumbSize) / documentSize.width());
}
}
if (!_frameSize.width()) {
_frameSize.setWidth(1);
}
if (!_frameSize.height()) {
_frameSize.setHeight(1);
}
}
}
}
void Game::initDimensions() {
_maxw = st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft;
int32 textWidth = _maxw - (st::inlineThumbSize + st::inlineThumbSkip);
TextParseOptions titleOpts = { 0, _maxw, 2 * st::semiboldFont->height, Qt::LayoutDirectionAuto };
_title.setText(st::semiboldFont, textOneLine(_result->getLayoutTitle()), titleOpts);
int32 titleHeight = qMin(_title.countHeight(_maxw), 2 * st::semiboldFont->height);
int32 descriptionLines = 2;
QString description = _result->getLayoutDescription();
TextParseOptions descriptionOpts = { TextParseMultiline, _maxw, descriptionLines * st::normalFont->height, Qt::LayoutDirectionAuto };
_description.setText(st::normalFont, description, descriptionOpts);
int32 descriptionHeight = qMin(_description.countHeight(_maxw), descriptionLines * st::normalFont->height);
_minh = titleHeight + descriptionHeight;
accumulate_max(_minh, st::inlineThumbSize);
_minh += st::inlineRowMargin * 2 + st::inlineRowBorder;
}
void Game::setPosition(int32 position) {
ItemBase::setPosition(position);
if (_position < 0) {
if (gif()) delete _gif;
_gif = 0;
}
}
void Game::paint(Painter &p, const QRect &clip, const PaintContext *context) const {
int32 left = st::emojiPanHeaderLeft - st::inlineResultsLeft;
left = st::inlineThumbSize + st::inlineThumbSkip;
auto rthumb = rtlrect(0, st::inlineRowMargin, st::inlineThumbSize, st::inlineThumbSize, _width);
// Gif thumb
auto thumbDisplayed = false, radial = false;
auto document = getResultDocument();
auto animatedThumb = document && document->isAnimation();
if (animatedThumb) {
document->automaticLoad(nullptr);
bool loaded = document->loaded(), loading = document->loading(), displayLoading = document->displayLoading();
if (loaded && !gif() && _gif != Media::Clip::BadReader) {
auto that = const_cast<Game*>(this);
that->_gif = new Media::Clip::Reader(document->location(), document->data(), [that](Media::Clip::Notification notification) {
that->clipCallback(notification);
});
if (gif()) _gif->setAutoplay();
}
bool animating = (gif() && _gif->started());
if (displayLoading) {
if (!_radial) {
_radial = std_::make_unique<Ui::RadialAnimation>(animation(const_cast<Game*>(this), &Game::step_radial));
}
if (!_radial->animating()) {
_radial->start(document->progress());
}
}
radial = isRadialAnimation(context->ms);
if (animating) {
if (!_thumb.isNull()) _thumb = QPixmap();
auto animationThumb = _gif->current(_frameSize.width(), _frameSize.height(), st::inlineThumbSize, st::inlineThumbSize, context->paused ? 0 : context->ms);
p.drawPixmapLeft(rthumb.topLeft(), _width, animationThumb);
thumbDisplayed = true;
}
}
if (!thumbDisplayed) {
prepareThumb(st::inlineThumbSize, st::inlineThumbSize);
if (_thumb.isNull()) {
p.fillRect(rthumb, st::overviewPhotoBg);
} else {
p.drawPixmapLeft(rthumb.topLeft(), _width, _thumb);
}
}
if (radial) {
p.fillRect(rthumb, st::msgDateImgBg);
QRect inner((st::inlineThumbSize - st::msgFileSize) / 2, (st::inlineThumbSize - st::msgFileSize) / 2, st::msgFileSize, st::msgFileSize);
if (radial) {
p.setOpacity(1);
QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine)));
_radial->draw(p, rinner, st::msgFileRadialLine, st::msgInBg);
}
}
p.setPen(st::black);
_title.drawLeftElided(p, left, st::inlineRowMargin, _width - left, _width, 2);
int32 titleHeight = qMin(_title.countHeight(_width - left), st::semiboldFont->height * 2);
p.setPen(st::inlineDescriptionFg);
int32 descriptionLines = 2;
_description.drawLeftElided(p, left, st::inlineRowMargin + titleHeight, _width - left, _width, descriptionLines);
if (!context->lastRow) {
p.fillRect(rtlrect(left, _height - st::inlineRowBorder, _width - left, st::inlineRowBorder, _width), st::inlineRowBorderFg);
}
}
void Game::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int x, int y) const {
int left = st::inlineThumbSize + st::inlineThumbSkip;
if (x >= 0 && x < left - st::inlineThumbSkip && y >= st::inlineRowMargin && y < st::inlineRowMargin + st::inlineThumbSize) {
link = _send;
return;
}
if (x >= left && x < _width && y >= 0 && y < _height) {
link = _send;
return;
}
}
void Game::prepareThumb(int width, int height) const {
auto thumb = ([this]() {
if (auto photo = getResultPhoto()) {
return photo->medium;
} else if (auto document = getResultDocument()) {
return document->thumb;
}
return ImagePtr();
})();
if (thumb->isNull()) {
return;
}
if (thumb->loaded()) {
if (_thumb.width() != width * cIntRetinaFactor() || _thumb.height() != height * cIntRetinaFactor()) {
int w = qMax(convertScale(thumb->width()), 1), h = qMax(convertScale(thumb->height()), 1);
auto resizeByHeight1 = (w * height > h * width) && (h >= height);
auto resizeByHeight2 = (h * width >= w * height) && (w < width);
if (resizeByHeight1 || resizeByHeight2) {
if (h > height) {
w = w * height / h;
h = height;
}
} else {
if (w > width) {
h = h * width / w;
w = width;
}
}
_thumb = thumb->pixNoCache(w * cIntRetinaFactor(), h * cIntRetinaFactor(), ImagePixSmooth, width, height);
}
} else {
thumb->load();
}
}
bool Game::isRadialAnimation(uint64 ms) const {
if (!_radial || !_radial->animating()) return false;
_radial->step(ms);
return _radial && _radial->animating();
}
void Game::step_radial(uint64 ms, bool timer) {
if (timer) {
update();
} else {
auto document = getResultDocument();
_radial->update(document->progress(), !document->loading() || document->loaded(), ms);
if (!_radial->animating() && document->loaded()) {
_radial.reset();
}
}
}
void Game::clipCallback(Media::Clip::Notification notification) {
using namespace Media::Clip;
switch (notification) {
case NotificationReinit: {
if (gif()) {
if (_gif->state() == State::Error) {
delete _gif;
_gif = BadReader;
getResultDocument()->forget();
} else if (_gif->ready() && !_gif->started()) {
_gif->start(_frameSize.width(), _frameSize.height(), st::inlineThumbSize, st::inlineThumbSize, ImageRoundRadius::None);
} else if (_gif->autoPausedGif() && !Ui::isInlineItemVisible(this)) {
delete _gif;
_gif = nullptr;
getResultDocument()->forget();
}
}
update();
} break;
case NotificationRepaint: {
if (gif() && !_gif->currentDisplayed()) {
update();
}
} break;
}
}
Game::~Game() {
if (gif()) deleteAndMark(_gif);
}
} // namespace internal } // namespace internal
} // namespace Layout } // namespace Layout
} // namespace InlineBots } // namespace InlineBots

View File

@ -116,7 +116,7 @@ private:
FloatAnimation _a_over; FloatAnimation _a_over;
Ui::RadialAnimation radial; Ui::RadialAnimation radial;
}; };
mutable AnimationData *_animation = nullptr; mutable std_::unique_ptr<AnimationData> _animation;
mutable FloatAnimation _a_deleteOver; mutable FloatAnimation _a_deleteOver;
}; };
@ -339,6 +339,40 @@ private:
}; };
class Game : public ItemBase {
public:
Game(Result *result);
void setPosition(int32 position) override;
void initDimensions() override;
void paint(Painter &p, const QRect &clip, const PaintContext *context) const override;
void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, int x, int y) const override;
~Game();
private:
void countFrameSize();
bool gif() const {
return (!_gif || _gif == Media::Clip::BadReader) ? false : true;
}
void prepareThumb(int32 width, int32 height) const;
bool isRadialAnimation(uint64 ms) const;
void step_radial(uint64 ms, bool timer);
void clipCallback(Media::Clip::Notification notification);
Media::Clip::Reader *_gif = nullptr;
mutable QPixmap _thumb;
mutable std_::unique_ptr<Ui::RadialAnimation> _radial;
Text _title, _description;
QSize _frameSize;
};
} // namespace internal } // namespace internal
} // namespace Layout } // namespace Layout
} // namespace InlineBots } // namespace InlineBots

View File

@ -113,6 +113,7 @@ std_::unique_ptr<ItemBase> ItemBase::createLayout(Result *result, bool forceThum
case Type::Article: case Type::Article:
case Type::Geo: case Type::Geo:
case Type::Venue: return std_::make_unique<internal::Article>(result, forceThumb); break; case Type::Venue: return std_::make_unique<internal::Article>(result, forceThumb); break;
case Type::Game: return std_::make_unique<internal::Game>(result); break;
case Type::Contact: return std_::make_unique<internal::Contact>(result); break; case Type::Contact: return std_::make_unique<internal::Contact>(result); break;
} }
return std_::unique_ptr<ItemBase>(); return std_::unique_ptr<ItemBase>();

View File

@ -47,6 +47,7 @@ std_::unique_ptr<Result> Result::create(uint64 queryId, const MTPBotInlineResult
result->insert(qsl("contact"), Result::Type::Contact); result->insert(qsl("contact"), Result::Type::Contact);
result->insert(qsl("venue"), Result::Type::Venue); result->insert(qsl("venue"), Result::Type::Venue);
result->insert(qsl("geo"), Result::Type::Geo); result->insert(qsl("geo"), Result::Type::Geo);
result->insert(qsl("game"), Result::Type::Game);
return result.release(); return result.release();
})() }; })() };
@ -122,6 +123,9 @@ std_::unique_ptr<Result> Result::create(uint64 queryId, const MTPBotInlineResult
if (result->_type == Type::Photo) { if (result->_type == Type::Photo) {
result->createPhoto(); result->createPhoto();
result->sendData.reset(new internal::SendPhoto(result->_photo, qs(r.vcaption))); result->sendData.reset(new internal::SendPhoto(result->_photo, qs(r.vcaption)));
} else if (result->_type == Type::Game) {
result->createGame();
result->sendData.reset(new internal::SendGame(result->_game));
} else { } else {
result->createDocument(); result->createDocument();
result->sendData.reset(new internal::SendFile(result->_document, qs(r.vcaption))); result->sendData.reset(new internal::SendFile(result->_document, qs(r.vcaption)));
@ -313,7 +317,7 @@ void Result::createPhoto() {
ImagePtr medium = ImagePtr(mediumsize.width(), mediumsize.height()); ImagePtr medium = ImagePtr(mediumsize.width(), mediumsize.height());
ImagePtr full = ImagePtr(_content_url, _width, _height); ImagePtr full = ImagePtr(_content_url, _width, _height);
uint64 photoId = rand_value<uint64>(); auto photoId = rand_value<PhotoId>();
_photo = App::photoSet(photoId, 0, 0, unixtime(), _thumb, medium, full); _photo = App::photoSet(photoId, 0, 0, unixtime(), _thumb, medium, full);
_photo->thumb = _thumb; _photo->thumb = _thumb;
} }
@ -321,8 +325,6 @@ void Result::createPhoto() {
void Result::createDocument() { void Result::createDocument() {
if (_document) return; if (_document) return;
uint64 docId = rand_value<uint64>();
if (!_thumb_url.isEmpty()) { if (!_thumb_url.isEmpty()) {
_thumb = ImagePtr(_thumb_url, QSize(90, 90)); _thumb = ImagePtr(_thumb_url, QSize(90, 90));
} }
@ -352,16 +354,16 @@ void Result::createDocument() {
attributes.push_back(MTP_documentAttributeAudio(MTP_flags(flags), MTP_int(_duration), MTPstring(), MTPstring(), MTPbytes())); attributes.push_back(MTP_documentAttributeAudio(MTP_flags(flags), MTP_int(_duration), MTPstring(), MTPstring(), MTPbytes()));
} }
MTPDocument document = MTP_document(MTP_long(docId), MTP_long(0), MTP_int(unixtime()), MTP_string(mime), MTP_int(0), MTP_photoSizeEmpty(MTP_string("")), MTP_int(MTP::maindc()), MTP_int(0), MTP_vector<MTPDocumentAttribute>(attributes)); auto documentId = rand_value<DocumentId>();
_document = App::documentSet(documentId, nullptr, 0, 0, unixtime(), attributes, mime, _thumb, MTP::maindc(), 0, StorageImageLocation());
_document = App::feedDocument(document);
_document->setContentUrl(_content_url); _document->setContentUrl(_content_url);
if (!_thumb->isNull()) {
_document->thumb = _thumb;
}
} }
Result::~Result() { void Result::createGame() {
if (_game) return;
auto gameId = rand_value<GameId>();
_game = App::gameSet(gameId, nullptr, 0, QString(), _title, _description, _photo, _document);
} }
} // namespace InlineBots } // namespace InlineBots

View File

@ -74,11 +74,10 @@ public:
QString getLayoutTitle() const; QString getLayoutTitle() const;
QString getLayoutDescription() const; QString getLayoutDescription() const;
~Result();
private: private:
void createPhoto(); void createPhoto();
void createDocument(); void createDocument();
void createGame();
enum class Type { enum class Type {
Unknown, Unknown,
@ -92,6 +91,7 @@ private:
Contact, Contact,
Geo, Geo,
Venue, Venue,
Game,
}; };
friend class internal::SendData; friend class internal::SendData;
@ -112,6 +112,7 @@ private:
DocumentData *_document = nullptr; DocumentData *_document = nullptr;
PhotoData *_photo = nullptr; PhotoData *_photo = nullptr;
GameData *_game = nullptr;
std_::unique_ptr<MTPReplyMarkup> _mtpKeyboard; std_::unique_ptr<MTPReplyMarkup> _mtpKeyboard;

View File

@ -90,5 +90,11 @@ UserId viaBotId, MsgId replyToId, const MTPReplyMarkup &markup) const {
history->addNewDocument(msgId, flags, viaBotId, replyToId, date(mtpDate), fromId, _document, _caption, markup); history->addNewDocument(msgId, flags, viaBotId, replyToId, date(mtpDate), fromId, _document, _caption, markup);
} }
void SendGame::addToHistory(const Result *owner, History *history,
MTPDmessage::Flags flags, MsgId msgId, UserId fromId, MTPint mtpDate,
UserId viaBotId, MsgId replyToId, const MTPReplyMarkup &markup) const {
history->addNewGame(msgId, flags, viaBotId, replyToId, date(mtpDate), fromId, _game, markup);
}
} // namespace internal } // namespace internal
} // namespace InlineBots } // namespace InlineBots

View File

@ -221,5 +221,25 @@ private:
}; };
// Message with game.
class SendGame : public SendData {
public:
SendGame(GameData *game)
: _game(game) {
}
bool isValid() const override {
return _game != nullptr;
}
void addToHistory(const Result *owner, History *history,
MTPDmessage::Flags flags, MsgId msgId, UserId fromId, MTPint mtpDate,
UserId viaBotId, MsgId replyToId, const MTPReplyMarkup &markup) const override;
private:
GameData *_game;
};
} // namespace internal } // namespace internal
} // namespace InlineBots } // namespace InlineBots

View File

@ -3349,7 +3349,7 @@ void MainWidget::openLocalUrl(const QString &url) {
} else if (auto usernameMatch = regex_match(qsl("^resolve/?\\?(.+)(#|$)"), command, matchOptions)) { } else if (auto usernameMatch = regex_match(qsl("^resolve/?\\?(.+)(#|$)"), command, matchOptions)) {
auto params = url_parse_params(usernameMatch->captured(1), UrlParamNameTransform::ToLower); auto params = url_parse_params(usernameMatch->captured(1), UrlParamNameTransform::ToLower);
auto domain = params.value(qsl("domain")); auto domain = params.value(qsl("domain"));
if (auto domainMatch = regex_match(qsl("^[a-zA-Z0-9\\.\\_]+$"), domain, matchOptions)) { if (regex_match(qsl("^[a-zA-Z0-9\\.\\_]+$"), domain, matchOptions)) {
auto start = qsl("start"); auto start = qsl("start");
auto startToken = params.value(start); auto startToken = params.value(start);
if (startToken.isEmpty()) { if (startToken.isEmpty()) {
@ -3364,6 +3364,11 @@ void MainWidget::openLocalUrl(const QString &url) {
if (auto postId = postParam.toInt()) { if (auto postId = postParam.toInt()) {
post = postId; post = postId;
} }
auto gameParam = params.value(qsl("game"));
if (!gameParam.isEmpty() && regex_match(qsl("^[a-zA-Z0-9\\.\\_]+$"), gameParam, matchOptions)) {
startToken = gameParam;
post = ShowAtGameShareMsgId;
}
openPeerByName(domain, post, startToken); openPeerByName(domain, post, startToken);
} }
} else if (auto shareGameScoreMatch = regex_match(qsl("^share_game_score/?\\?(.+)(#|$)"), command, matchOptions)) { } else if (auto shareGameScoreMatch = regex_match(qsl("^share_game_score/?\\?(.+)(#|$)"), command, matchOptions)) {
@ -3377,7 +3382,14 @@ void MainWidget::openPeerByName(const QString &username, MsgId msgId, const QStr
PeerData *peer = App::peerByName(username); PeerData *peer = App::peerByName(username);
if (peer) { if (peer) {
if (msgId == ShowAtProfileMsgId && !peer->isChannel()) { if (msgId == ShowAtGameShareMsgId) {
if (peer->isUser() && peer->asUser()->botInfo && !startToken.isEmpty()) {
peer->asUser()->botInfo->shareGameShortName = startToken;
Ui::showLayer(new ContactsBox(peer->asUser()));
} else {
Ui::showPeerHistoryAsync(peer->id, ShowAtUnreadMsgId, Ui::ShowWay::Forward);
}
} else if (msgId == ShowAtProfileMsgId && !peer->isChannel()) {
if (peer->isUser() && peer->asUser()->botInfo && !peer->asUser()->botInfo->cantJoinGroups && !startToken.isEmpty()) { if (peer->isUser() && peer->asUser()->botInfo && !peer->asUser()->botInfo->cantJoinGroups && !startToken.isEmpty()) {
peer->asUser()->botInfo->startGroupToken = startToken; peer->asUser()->botInfo->startGroupToken = startToken;
Ui::showLayer(new ContactsBox(peer->asUser())); Ui::showLayer(new ContactsBox(peer->asUser()));

View File

@ -163,6 +163,7 @@ constexpr const MsgId ShowAtTheEndMsgId = -0x40000000;
constexpr const MsgId SwitchAtTopMsgId = -0x3FFFFFFF; constexpr const MsgId SwitchAtTopMsgId = -0x3FFFFFFF;
constexpr const MsgId ShowAtProfileMsgId = -0x3FFFFFFE; constexpr const MsgId ShowAtProfileMsgId = -0x3FFFFFFE;
constexpr const MsgId ShowAndStartBotMsgId = -0x3FFFFFD; constexpr const MsgId ShowAndStartBotMsgId = -0x3FFFFFD;
constexpr const MsgId ShowAtGameShareMsgId = -0x3FFFFFC;
constexpr const MsgId ServerMaxMsgId = 0x3FFFFFFF; constexpr const MsgId ServerMaxMsgId = 0x3FFFFFFF;
constexpr const MsgId ShowAtUnreadMsgId = 0; constexpr const MsgId ShowAtUnreadMsgId = 0;
@ -385,7 +386,7 @@ struct BotInfo {
QList<BotCommand> commands; QList<BotCommand> commands;
Text text = Text{ int(st::msgMinWidth) }; // description Text text = Text{ int(st::msgMinWidth) }; // description
QString startToken, startGroupToken; QString startToken, startGroupToken, shareGameShortName;
PeerId inlineReturnPeerId = 0; PeerId inlineReturnPeerId = 0;
}; };
@ -1141,7 +1142,7 @@ public:
return (type == AnimatedDocument) && !mime.compare(qstr("video/mp4"), Qt::CaseInsensitive); return (type == AnimatedDocument) && !mime.compare(qstr("video/mp4"), Qt::CaseInsensitive);
} }
bool isMusic() const { bool isMusic() const {
return (type == SongDocument) ? !static_cast<SongData*>(_additional.get())->title.isEmpty() : false; return (type == SongDocument) ? (static_cast<SongData*>(_additional.get())->duration != 0) : false;
} }
bool isVideo() const { bool isVideo() const {
return (type == VideoDocument); return (type == VideoDocument);