bot description displayed in message history, bot commands are highlighted and sent by click

This commit is contained in:
John Preston 2015-06-10 18:54:24 +03:00
parent 85635dbefd
commit 83744e77d1
15 changed files with 286 additions and 50 deletions

View File

@ -456,6 +456,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
"lng_willbe_history" = "Please select chat to start messaging";
"lng_message_with_from" = "[c]{from}:[/c] {message}";
"lng_from_you" = "You";
"lng_bot_description" = "What can this bot do?";
"lng_typing" = "typing";
"lng_user_typing" = "{user} is typing";

View File

@ -1915,3 +1915,5 @@ webPageDescriptionFont: font(fsize);
webPagePhotoSkip: 5px;
webPagePhotoSize: 100px;
webPagePhotoDelta: 8px;
botDescSkip: 8px;

View File

@ -142,6 +142,8 @@ void ApiWrap::gotUserFull(PeerData *peer, const MTPUserFull &result) {
App::feedUserLink(MTP_int(App::userFromPeer(peer->id)), d.vlink.c_contacts_link().vmy_link, d.vlink.c_contacts_link().vforeign_link);
App::main()->gotNotifySetting(MTP_inputNotifyPeer(peer->input), d.vnotify_settings);
peer->asUser()->setBotInfo(d.vbot_info);
_fullRequests.remove(peer);
emit fullPeerLoaded(peer);
}

View File

@ -1946,6 +1946,12 @@ namespace App {
}
}
void sendBotCommand(const QString &cmd) {
if (App::main()) {
App::main()->sendBotCommand(cmd);
}
}
void searchByHashtag(const QString &tag) {
if (App::main()) {
App::main()->searchMessages(tag + ' ');

View File

@ -225,6 +225,7 @@ namespace App {
void setProxySettings(QNetworkAccessManager &manager);
void setProxySettings(QTcpSocket &socket);
void sendBotCommand(const QString &cmd);
void searchByHashtag(const QString &tag);
void openUserByName(const QString &username, bool toProfile = false);
void joinGroupByHash(const QString &hash);

View File

@ -30,6 +30,7 @@ namespace {
const QRegularExpression _reMailStart(qsl("^[a-zA-Z\\-_\\.0-9]{1,256}\\@"));
const QRegularExpression _reHashtag(qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10])#[\\w]{2,64}([\\W]|$)"), QRegularExpression::UseUnicodePropertiesOption);
const QRegularExpression _reMention(qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10])@[A-Za-z_0-9]{5,32}([\\W]|$)"), QRegularExpression::UseUnicodePropertiesOption);
const QRegularExpression _reBotCommand(qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10])/[\\w]{1,64}([\\W]|$)"), QRegularExpression::UseUnicodePropertiesOption);
QSet<int32> _validProtocols, _validTopDomains;
const style::textStyle *_textStyle = 0;
@ -61,6 +62,10 @@ const QRegularExpression &reHashtag() {
return _reHashtag;
}
const QRegularExpression &reBotCommand() {
return _reBotCommand;
}
const style::textStyle *textstyleCurrent() {
return _textStyle;
}
@ -302,7 +307,10 @@ public:
}
void getLinkData(const QString &original, QString &result, int32 &fullDisplayed) {
if (!original.isEmpty() && original.at(0) == '@') {
if (!original.isEmpty() && original.at(0) == '/') {
result = original;
fullDisplayed = -4; // bot command
} else if (!original.isEmpty() && original.at(0) == '@') {
result = original;
fullDisplayed = -3; // mention
} else if (!original.isEmpty() && original.at(0) == '#') {
@ -567,7 +575,9 @@ public:
_t->_links.resize(lnkIndex);
const TextLinkData &data(links[lnkIndex - maxLnkIndex - 1]);
TextLinkPtr lnk;
if (data.fullDisplayed < -2) { // mention
if (data.fullDisplayed < -3) { // bot command
lnk = TextLinkPtr(new BotCommandLink(data.url));
} else if (data.fullDisplayed < -2) { // mention
if (options.flags & TextTwitterMentions) {
lnk = TextLinkPtr(new TextLink(qsl("https://twitter.com/") + data.url.mid(1), true));
} else if (options.flags & TextInstagramMentions) {
@ -612,7 +622,7 @@ private:
TextLinkData(const QString &url = QString(), int32 fullDisplayed = 1) : url(url), fullDisplayed(fullDisplayed) {
}
QString url;
int32 fullDisplayed; // -3 - mention, -2 - hashtag, -1 - email
int32 fullDisplayed; // -4 - bot command, -3 - mention, -2 - hashtag, -1 - email
};
typedef QVector<TextLinkData> TextLinks;
TextLinks links;
@ -763,6 +773,12 @@ void HashtagLink::onClick(Qt::MouseButton button) const {
}
}
void BotCommandLink::onClick(Qt::MouseButton button) const {
if (button == Qt::LeftButton || button == Qt::MiddleButton) {
App::sendBotCommand(_cmd);
}
}
class TextPainter {
public:
@ -4071,7 +4087,9 @@ bool textSplit(QString &sendingText, QString &leftText, int32 limit) {
LinkRanges textParseLinks(const QString &text, int32 flags, bool rich) { // some code is duplicated in flattextarea.cpp!
LinkRanges lnkRanges;
bool withHashtags = (flags & TextParseHashtags), withMentions = (flags & TextParseMentions);
bool withHashtags = (flags & TextParseHashtags);
bool withMentions = (flags & TextParseMentions);
bool withBotCommands = (flags & TextParseBotCommands);
initLinkSets();
int32 len = text.size(), nextCmd = rich ? 0 : len;
@ -4088,6 +4106,7 @@ LinkRanges textParseLinks(const QString &text, int32 flags, bool rich) { // some
QRegularExpressionMatch mExplicitDomain = _reExplicitDomain.match(text, matchOffset);
QRegularExpressionMatch mHashtag = withHashtags ? _reHashtag.match(text, matchOffset) : QRegularExpressionMatch();
QRegularExpressionMatch mMention = withMentions ? _reMention.match(text, qMax(mentionSkip, matchOffset)) : QRegularExpressionMatch();
QRegularExpressionMatch mBotCommand = withBotCommands ? _reBotCommand.match(text, matchOffset) : QRegularExpressionMatch();
LinkRange link;
int32 domainOffset = mDomain.hasMatch() ? mDomain.capturedStart() : INT_MAX,
@ -4097,7 +4116,9 @@ LinkRanges textParseLinks(const QString &text, int32 flags, bool rich) { // some
hashtagOffset = mHashtag.hasMatch() ? mHashtag.capturedStart() : INT_MAX,
hashtagEnd = mHashtag.hasMatch() ? mHashtag.capturedEnd() : INT_MAX,
mentionOffset = mMention.hasMatch() ? mMention.capturedStart() : INT_MAX,
mentionEnd = mMention.hasMatch() ? mMention.capturedEnd() : INT_MAX;
mentionEnd = mMention.hasMatch() ? mMention.capturedEnd() : INT_MAX,
botCommandOffset = mBotCommand.hasMatch() ? mBotCommand.capturedStart() : INT_MAX,
botCommandEnd = mBotCommand.hasMatch() ? mBotCommand.capturedEnd() : INT_MAX;
if (mHashtag.hasMatch()) {
if (!mHashtag.capturedRef(1).isEmpty()) {
++hashtagOffset;
@ -4127,14 +4148,24 @@ LinkRanges textParseLinks(const QString &text, int32 flags, bool rich) { // some
break;
}
}
if (!mMention.hasMatch() && !mDomain.hasMatch() && !mExplicitDomain.hasMatch() && !mHashtag.hasMatch()) break;
if (mBotCommand.hasMatch()) {
if (!mBotCommand.capturedRef(1).isEmpty()) {
++botCommandOffset;
}
if (!mBotCommand.capturedRef(2).isEmpty()) {
--botCommandEnd;
}
}
if (!mDomain.hasMatch() && !mExplicitDomain.hasMatch() && !mHashtag.hasMatch() && !mMention.hasMatch() && !mBotCommand.hasMatch()) {
break;
}
if (explicitDomainOffset < domainOffset) {
domainOffset = explicitDomainOffset;
domainEnd = explicitDomainEnd;
mDomain = mExplicitDomain;
}
if (mentionOffset < hashtagOffset && mentionOffset < domainOffset) {
if (mentionOffset < hashtagOffset && mentionOffset < domainOffset && mentionOffset < botCommandOffset) {
if (mentionOffset > nextCmd) {
const QChar *after = textSkipCommand(start + nextCmd, start + len);
if (after > start + nextCmd && mentionOffset < (after - start)) {
@ -4145,7 +4176,7 @@ LinkRanges textParseLinks(const QString &text, int32 flags, bool rich) { // some
link.from = start + mentionOffset;
link.len = start + mentionEnd - link.from;
} else if (hashtagOffset < domainOffset) {
} else if (hashtagOffset < domainOffset && hashtagOffset < botCommandOffset) {
if (hashtagOffset > nextCmd) {
const QChar *after = textSkipCommand(start + nextCmd, start + len);
if (after > start + nextCmd && hashtagOffset < (after - start)) {
@ -4156,6 +4187,17 @@ LinkRanges textParseLinks(const QString &text, int32 flags, bool rich) { // some
link.from = start + hashtagOffset;
link.len = start + hashtagEnd - link.from;
} else if (botCommandOffset < domainOffset) {
if (botCommandOffset > nextCmd) {
const QChar *after = textSkipCommand(start + nextCmd, start + len);
if (after > start + nextCmd && botCommandOffset < (after - start)) {
nextCmd = offset = matchOffset = after - start;
continue;
}
}
link.from = start + botCommandOffset;
link.len = start + botCommandEnd - link.from;
} else {
if (domainOffset > nextCmd) {
const QChar *after = textSkipCommand(start + nextCmd, start + len);

View File

@ -31,11 +31,12 @@ enum {
TextParseRichText = 0x004,
TextParseMentions = 0x008,
TextParseHashtags = 0x010,
TextParseBotCommands = 0x020,
TextTwitterMentions = 0x020,
TextTwitterHashtags = 0x040,
TextInstagramMentions = 0x080,
TextInstagramHashtags = 0x100,
TextTwitterMentions = 0x040,
TextTwitterHashtags = 0x080,
TextInstagramMentions = 0x100,
TextInstagramHashtags = 0x200,
};
struct LinkRange {
@ -385,6 +386,32 @@ private:
};
class BotCommandLink : public ITextLink {
public:
BotCommandLink(const QString &cmd) : _cmd(cmd) {
}
const QString &text() const {
return _cmd;
}
void onClick(Qt::MouseButton button) const;
const QString &readable() const {
return _cmd;
}
QString encoded() const {
return _cmd;
}
private:
QString _cmd;
};
static const QChar TextCommand(0x0010);
enum TextCommands {
TextCommandBold = 0x01,
@ -512,6 +539,7 @@ const QSet<int32> &validTopDomains();
const QRegularExpression &reDomain();
const QRegularExpression &reMailName();
const QRegularExpression &reHashtag();
const QRegularExpression &reBotCommand();
// text style
const style::textStyle *textstyleCurrent();

View File

@ -41,14 +41,20 @@ TextParseOptions _textDlgOptions = {
1, // maxh
Qt::LayoutDirectionAuto, // lang-dependent
};
TextParseOptions _historyTextOptions = {
TextParseLinks | TextParseMentions | TextParseHashtags | TextParseMultiline | TextParseRichText, // flags
0, // maxw
0, // maxh
Qt::LayoutDirectionAuto, // dir
};
TextParseOptions _historyBotOptions = {
TextParseLinks | TextParseMentions | TextParseHashtags | TextParseBotCommands | TextParseMultiline | TextParseRichText, // flags
0, // maxw
0, // maxh
Qt::LayoutDirectionAuto, // dir
};
namespace {
TextParseOptions _historyTextOptions = {
TextParseLinks | TextParseMentions | TextParseHashtags | TextParseMultiline | TextParseRichText, // flags
0, // maxw
0, // maxh
Qt::LayoutDirectionAuto, // dir
};
TextParseOptions _historySrvOptions = {
TextParseLinks | TextParseMentions | TextParseHashtags | TextParseMultiline | TextParseRichText, // flags
0, // maxw
@ -1548,7 +1554,8 @@ HistoryPhoto::HistoryPhoto(const MTPDphoto &photo, const QString &caption, Histo
, _caption(st::minPhotoSize)
, openl(new PhotoLink(data)) {
if (!caption.isEmpty()) {
_caption.setText(st::msgFont, caption + textcmdSkipBlock(parent->timeWidth(true), st::msgDateFont->height - st::msgDateDelta.y()), _historyTextOptions);
bool bot = (!parent->history()->peer->chat && parent->history()->peer->asUser()->botInfo) || (!parent->from()->chat && parent->from()->asUser()->botInfo);
_caption.setText(st::msgFont, caption + textcmdSkipBlock(parent->timeWidth(true), st::msgDateFont->height - st::msgDateDelta.y()), bot ? _historyBotOptions : _historyTextOptions);
}
init();
}
@ -1948,7 +1955,8 @@ HistoryVideo::HistoryVideo(const MTPDvideo &video, const QString &caption, Histo
, _uplDone(0)
{
if (!caption.isEmpty()) {
_caption.setText(st::msgFont, caption + textcmdSkipBlock(parent->timeWidth(true), st::msgDateFont->height - st::msgDateDelta.y()), _historyTextOptions);
bool bot = (!parent->history()->peer->chat && parent->history()->peer->asUser()->botInfo) || (!parent->from()->chat && parent->from()->asUser()->botInfo);
_caption.setText(st::msgFont, caption + textcmdSkipBlock(parent->timeWidth(true), st::msgDateFont->height - st::msgDateDelta.y()), bot ? _historyBotOptions : _historyTextOptions);
}
_size = formatDurationAndSizeText(data->duration, data->size);
@ -4656,10 +4664,11 @@ void HistoryMessage::initMediaFromDocument(DocumentData *doc) {
void HistoryMessage::initDimensions(const QString &text) {
if (!_media || !text.isEmpty()) { // !justMedia()
bool bot = (!history()->peer->chat && history()->peer->asUser()->botInfo) || (!from()->chat && from()->asUser()->botInfo);
if (_media && _media->isDisplayed()) {
_text.setText(st::msgFont, text, _historyTextOptions);
_text.setText(st::msgFont, text, bot ? _historyBotOptions : _historyTextOptions);
} else {
_text.setText(st::msgFont, text + textcmdSkipBlock(timeWidth(true), st::msgDateFont->height - st::msgDateDelta.y()), _historyTextOptions);
_text.setText(st::msgFont, text + textcmdSkipBlock(timeWidth(true), st::msgDateFont->height - st::msgDateDelta.y()), bot ? _historyBotOptions : _historyTextOptions);
}
}
}
@ -4675,17 +4684,18 @@ void HistoryMessage::initDimensions(const HistoryItem *parent) {
_maxw += st::msgPadding.left() + st::msgPadding.right();
if (_media) {
_media->initDimensions(this);
bool bot = (!history()->peer->chat && history()->peer->asUser()->botInfo) || (!from()->chat && from()->asUser()->botInfo);
if (_media->isDisplayed() && _text.hasSkipBlock()) {
QString was = HistoryMessage::selectedText(FullItemSel);
if (!was.isEmpty()) {
_text.setText(st::msgFont, was, _historyTextOptions); // without date skip
_text.setText(st::msgFont, was, bot ? _historyBotOptions : _historyTextOptions); // without date skip
_textWidth = 0;
_textHeight = 0;
}
} else if (!_media->isDisplayed() && !_text.hasSkipBlock()) {
QString was = HistoryMessage::selectedText(FullItemSel);
if (!was.isEmpty()) {
_text.setText(st::msgFont, was + textcmdSkipBlock(timeWidth(true), st::msgDateFont->height - st::msgDateDelta.y()), _historyTextOptions); // without date skip
_text.setText(st::msgFont, was + textcmdSkipBlock(timeWidth(true), st::msgDateFont->height - st::msgDateDelta.y()), bot ? _historyBotOptions : _historyTextOptions); // without date skip
_textWidth = 0;
_textHeight = 0;
}
@ -4739,17 +4749,18 @@ void HistoryMessage::setMedia(const MTPmessageMedia &media) {
}
QString t;
initMedia(media, t);
bool bot = (!history()->peer->chat && history()->peer->asUser()->botInfo) || (!from()->chat && from()->asUser()->botInfo);
if (_media && _media->isDisplayed() && !mediaWasDisplayed) {
QString was = HistoryMessage::selectedText(FullItemSel);
if (!was.isEmpty()) {
_text.setText(st::msgFont, was, _historyTextOptions); // without date skip
_text.setText(st::msgFont, was, bot ? _historyBotOptions : _historyTextOptions); // without date skip
_textWidth = 0;
_textHeight = 0;
}
} else if (mediaWasDisplayed && (!_media || !_media->isDisplayed())) {
QString was = HistoryMessage::selectedText(FullItemSel);
if (!was.isEmpty()) {
_text.setText(st::msgFont, was + textcmdSkipBlock(timeWidth(true), st::msgDateFont->height - st::msgDateDelta.y()), _historyTextOptions); // without date skip
_text.setText(st::msgFont, was + textcmdSkipBlock(timeWidth(true), st::msgDateFont->height - st::msgDateDelta.y()), bot ? _historyBotOptions : _historyTextOptions); // without date skip
_textWidth = 0;
_textHeight = 0;
}

View File

@ -30,7 +30,7 @@ static const uint32 FullItemSel = 0xFFFFFFFF;
typedef QMap<int32, HistoryItem*> SelectedItemSet;
extern TextParseOptions _textNameOptions, _textDlgOptions;
extern TextParseOptions _textNameOptions, _textDlgOptions, _historyTextOptions, _historyBotOptions;
#include "structs.h"

View File

@ -36,6 +36,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
HistoryList::HistoryList(HistoryWidget *historyWidget, ScrollArea *scroll, History *history) : QWidget(0)
, hist(history)
, ySkip(0)
, botInfo(history->peer->chat ? 0 : history->peer->asUser()->botInfo)
, botDescWidth(0), botDescHeight(0)
, historyWidget(historyWidget)
, scrollArea(scroll)
, currentBlock(0)
@ -69,6 +72,8 @@ HistoryList::HistoryList(HistoryWidget *historyWidget, ScrollArea *scroll, Histo
_trippleClickTimer.setSingleShot(true);
if (botInfo && !botInfo->inited) App::api()->requestFullPeer(hist->peer);
setMouseTracking(true);
}
@ -82,7 +87,7 @@ void HistoryList::messagesReceivedDown(const QVector<MTPMessage> &messages) {
void HistoryList::updateMsg(const HistoryItem *msg) {
if (!msg || msg->detached() || !hist || hist != msg->history()) return;
update(0, height() - hist->height - st::historyPadding + msg->block()->y + msg->y, width(), msg->height());
update(0, ySkip + msg->block()->y + msg->y, width(), msg->height());
}
void HistoryList::paintEvent(QPaintEvent *e) {
@ -94,10 +99,24 @@ void HistoryList::paintEvent(QPaintEvent *e) {
p.setClipRect(r);
}
if (hist->isEmpty()) {
if (botInfo && !botInfo->text.isEmpty() && botDescHeight > 0) {
if (r.top() < botDescRect.y() + botDescRect.height() && r.bottom() > botDescRect.y()) {
textstyleSet(&st::inTextStyle);
App::roundRect(p, botDescRect, st::msgInBg, MessageInCorners, &st::msgInShadow);
p.setFont(st::msgNameFont->f);
p.setPen(st::black->p);
p.drawText(botDescRect.left() + st::msgPadding.left(), botDescRect.top() + st::msgPadding.top() + st::msgNameFont->ascent, lang(lng_bot_description));
botInfo->text.draw(p, botDescRect.left() + st::msgPadding.left(), botDescRect.top() + st::msgPadding.top() + st::msgNameFont->height + st::botDescSkip, botDescWidth);
textstyleRestore();
}
} else if (hist->isEmpty()) {
QPoint dogPos((width() - st::msgDogImg.pxWidth()) / 2, ((height() - st::msgDogImg.pxHeight()) * 4) / 9);
p.drawPixmap(dogPos, *cChatDogImage());
} else {
}
if (!hist->isEmpty()) {
adjustCurrent(r.top());
HistoryBlock *block = (*hist)[currentBlock];
HistoryItem *item = (*block)[currentItem];
@ -105,7 +124,7 @@ void HistoryList::paintEvent(QPaintEvent *e) {
SelectedItems::const_iterator selEnd = _selected.cend();
bool hasSel = !_selected.isEmpty();
int32 firstItemY = height() - hist->height - st::historyPadding, drawToY = r.bottom() - firstItemY;
int32 drawToY = r.bottom() - ySkip;
int32 selfromy = 0, seltoy = 0;
if (_dragSelFrom && _dragSelTo) {
@ -114,7 +133,7 @@ void HistoryList::paintEvent(QPaintEvent *e) {
}
int32 iBlock = currentBlock, iItem = currentItem, y = block->y + item->y;
p.translate(0, firstItemY + y);
p.translate(0, ySkip + y);
while (y < drawToY) {
int32 h = item->height();
uint32 sel = 0;
@ -922,13 +941,85 @@ void HistoryList::keyPressEvent(QKeyEvent *e) {
int32 HistoryList::recountHeight(bool dontRecountText) {
int32 st = hist->lastScrollTop;
hist->geomResize(scrollArea->width(), &st, dontRecountText);
updateBotInfo(false);
if (botInfo && !botInfo->text.isEmpty()) {
int32 tw = scrollArea->width() - st::msgMargin.left() - st::msgMargin.right();
if (tw > st::msgMaxWidth) tw = st::msgMaxWidth;
tw -= st::msgPadding.left() + st::msgPadding.right();
int32 mw = qMax(botInfo->text.maxWidth(), st::msgNameFont->m.width(lang(lng_bot_description)));
if (tw > mw) tw = mw;
botDescWidth = tw;
botDescHeight = botInfo->text.countHeight(botDescWidth);
int32 descH = st::msgMargin.top() + st::msgPadding.top() + st::msgNameFont->height + st::botDescSkip + botDescHeight + st::msgPadding.bottom() + st::msgMargin.bottom();
int32 descAtX = (scrollArea->width() - botDescWidth) / 2 - st::msgPadding.left();
int32 descAtY = qMin(ySkip - descH, (scrollArea->height() - descH) / 2) + st::msgMargin.top();
botDescRect = QRect(descAtX, descAtY, botDescWidth + st::msgPadding.left() + st::msgPadding.right(), descH - st::msgMargin.top() - st::msgMargin.bottom());
} else {
botDescWidth = botDescHeight = 0;
botDescRect = QRect();
}
return st;
}
void HistoryList::updateBotInfo(bool recount) {
int32 newh = 0;
if (botInfo && !botInfo->description.isEmpty()) {
if (botInfo->text.isEmpty()) {
botInfo->text.setText(st::msgFont, botInfo->description, _historyBotOptions);
if (recount) {
int32 tw = scrollArea->width() - st::msgMargin.left() - st::msgMargin.right();
if (tw > st::msgMaxWidth) tw = st::msgMaxWidth;
tw -= st::msgPadding.left() + st::msgPadding.right();
int32 mw = qMax(botInfo->text.maxWidth(), st::msgNameFont->m.width(lang(lng_bot_description)));
if (tw > mw) tw = mw;
botDescWidth = tw;
newh = botInfo->text.countHeight(botDescWidth);
}
}
}
if (recount) {
if (botDescHeight != newh) {
botDescHeight = newh;
updateSize();
}
if (botDescHeight > 0) {
int32 descH = st::msgMargin.top() + st::msgPadding.top() + st::msgNameFont->height + st::botDescSkip + botDescHeight + st::msgPadding.bottom() + st::msgMargin.bottom();
int32 descAtX = (scrollArea->width() - botDescWidth) / 2 - st::msgPadding.left();
int32 descAtY = qMin(ySkip - descH, (scrollArea->height() - descH) / 2) + st::msgMargin.top();
botDescRect = QRect(descAtX, descAtY, botDescWidth + st::msgPadding.left() + st::msgPadding.right(), descH - st::msgMargin.top() - st::msgMargin.bottom());
} else {
botDescWidth = 0;
botDescRect = QRect();
}
}
}
void HistoryList::updateSize() {
int32 ph = scrollArea->height(), nh = (hist->height + st::historyPadding) > ph ? (hist->height + st::historyPadding) : ph;
int32 ph = scrollArea->height(), minadd = 0;
ySkip = ph - (hist->height + st::historyPadding);
if (botInfo && !botInfo->text.isEmpty()) {
minadd = st::msgMargin.top() + st::msgMargin.bottom() + st::msgPadding.top() + st::msgPadding.bottom() + st::msgNameFont->height + st::botDescSkip + botDescHeight;
}
if (ySkip < minadd) ySkip = minadd;
if (botDescHeight > 0) {
int32 descH = st::msgMargin.top() + st::msgPadding.top() + st::msgNameFont->height + st::botDescSkip + botDescHeight + st::msgPadding.bottom() + st::msgMargin.bottom();
int32 descAtX = (scrollArea->width() - botDescWidth) / 2 - st::msgPadding.left();
int32 descAtY = qMin(ySkip - descH, (scrollArea->height() - descH) / 2) + st::msgMargin.top();
botDescRect = QRect(descAtX, descAtY, botDescWidth + st::msgPadding.left() + st::msgPadding.right(), descH - st::msgMargin.top() - st::msgMargin.bottom());
}
int32 nh = hist->height + st::historyPadding + ySkip;
if (width() != scrollArea->width() || height() != nh) {
resize(scrollArea->width(), nh);
dragActionUpdate(QCursor::pos());
} else {
update();
}
@ -964,12 +1055,11 @@ void HistoryList::adjustCurrent(int32 y) {
currentItem = 0;
}
int32 dh = height() - hist->height - st::historyPadding;
while ((*hist)[currentBlock]->y + dh > y && currentBlock > 0) {
while ((*hist)[currentBlock]->y + ySkip > y && currentBlock > 0) {
--currentBlock;
currentItem = 0;
}
while ((*hist)[currentBlock]->y + (*hist)[currentBlock]->height + dh <= y && currentBlock + 1 < hist->size()) {
while ((*hist)[currentBlock]->y + (*hist)[currentBlock]->height + ySkip <= y && currentBlock + 1 < hist->size()) {
++currentBlock;
currentItem = 0;
}
@ -978,10 +1068,10 @@ void HistoryList::adjustCurrent(int32 y) {
currentItem = block->size() - 1;
}
int32 by = block->y;
while ((*block)[currentItem]->y + by + dh > y && currentItem > 0) {
while ((*block)[currentItem]->y + by + ySkip > y && currentItem > 0) {
--currentItem;
}
while ((*block)[currentItem]->y + (*block)[currentItem]->height() + by + dh <= y && currentItem + 1 < block->size()) {
while ((*block)[currentItem]->y + (*block)[currentItem]->height() + by + ySkip <= y && currentItem + 1 < block->size()) {
++currentItem;
}
}
@ -1076,13 +1166,14 @@ void HistoryList::onUpdateSelected() {
if (!hist || hist->isEmpty()) return;
QPoint mousePos(mapFromGlobal(_dragPos));
QPoint m(historyWidget->clampMousePosition(mousePos));
adjustCurrent(m.y());
QPoint point(historyWidget->clampMousePosition(mousePos));
adjustCurrent(point.y());
HistoryBlock *block = (*hist)[currentBlock];
HistoryItem *item = (*block)[currentItem];
App::mousedItem(item);
m = mapMouseToItem(m, item);
QPoint m = mapMouseToItem(point, item);
if (item->hasPoint(m.x(), m.y())) {
updateMsg(App::hoveredItem());
App::hoveredItem(item);
@ -1094,17 +1185,36 @@ void HistoryList::onUpdateSelected() {
linkTipTimer.start(1000);
Qt::CursorShape cur = style::cur_default;
bool inText, lnkChanged = false;
bool inText = false, lnkChanged = false, lnkInDesc = false;
TextLinkPtr lnk;
item->getState(lnk, inText, m.x(), m.y());
if (point.y() < ySkip) {
if (botInfo && !botInfo->text.isEmpty() && botDescHeight > 0) {
botInfo->text.getState(lnk, inText, point.x() - botDescRect.left() - st::msgPadding.left(), point.y() - botDescRect.top() - st::msgPadding.top() - st::botDescSkip - st::msgNameFont->height, botDescWidth);
lnkInDesc = true;
}
} else {
item->getState(lnk, inText, m.x(), m.y());
}
if (lnk != textlnkOver()) {
lnkChanged = true;
updateMsg(App::hoveredLinkItem());
if (textlnkOver()) {
if (App::hoveredLinkItem()) {
updateMsg(App::hoveredLinkItem());
} else {
update(botDescRect);
}
}
textlnkOver(lnk);
QToolTip::showText(_dragPos, QString(), App::wnd());
App::hoveredLinkItem(lnk ? item : 0);
updateMsg(App::hoveredLinkItem());
App::hoveredLinkItem((lnk && !lnkInDesc) ? item : 0);
if (textlnkOver()) {
if (App::hoveredLinkItem()) {
updateMsg(App::hoveredLinkItem());
} else {
update(botDescRect);
}
}
}
if (_dragAction == NoDrag) {
@ -4110,8 +4220,9 @@ void HistoryWidget::onCancel() {
void HistoryWidget::onPeerLoaded(PeerData *data) {
peerUpdated(data);
if (data == histPeer) {
if (_list && data == histPeer) {
checkMentionDropdown();
_list->updateBotInfo();
}
}

View File

@ -79,6 +79,8 @@ public:
void itemRemoved(HistoryItem *item);
void itemReplaced(HistoryItem *oldItem, HistoryItem *newItem);
void updateBotInfo(bool recount = true);
~HistoryList();
public slots:
@ -116,6 +118,12 @@ private:
void applyDragSelection();
History *hist;
int32 ySkip;
BotInfo *botInfo;
int32 botDescWidth, botDescHeight;
QRect botDescRect;
HistoryWidget *historyWidget;
ScrollArea *scrollArea;
int32 currentBlock, currentItem;

View File

@ -1016,6 +1016,12 @@ void MainWidget::stopAnimActive() {
history.stopAnimActive();
}
void MainWidget::sendBotCommand(const QString &cmd) {
if (history.peer()) {
sendMessage(App::history(history.peer()->id), cmd, 0);
}
}
void MainWidget::searchMessages(const QString &query) {
App::wnd()->hideMediaview();
dialogs.searchMessages(query);

View File

@ -295,6 +295,8 @@ public:
uint64 animActiveTime() const;
void stopAnimActive();
void sendBotCommand(const QString &cmd);
void searchMessages(const QString &query);
void preloadOverviews(PeerData *peer);
void mediaOverviewUpdated(PeerData *peer);

View File

@ -210,6 +210,7 @@ void UserData::setBotInfoVersion(int32 version) {
botInfo->description.clear();
botInfo->shareText.clear();
botInfo->version = version;
botInfo->inited = false;
}
}
void UserData::setBotInfo(const MTPBotInfo &info) {
@ -221,9 +222,18 @@ void UserData::setBotInfo(const MTPBotInfo &info) {
case mtpc_botInfo: {
const MTPDbotInfo &d(info.c_botInfo());
if (d.vuser_id.v != id) return;
setBotInfoVersion(d.vversion.v);
if (botInfo->version > d.vversion.v) return;
botInfo->description = qs(d.vdescription);
if (botInfo) {
botInfo->version = d.vversion.v;
} else {
setBotInfoVersion(d.vversion.v);
}
QString desc = qs(d.vdescription) + "\n\nhttps://telegram.org test #test test /help test";
if (botInfo->description != desc) {
botInfo->description = desc;
botInfo->text = Text();
}
botInfo->shareText = qs(d.vshare_text);
const QVector<MTPBotCommand> &v(d.vcommands.c_vector().v);
@ -234,6 +244,8 @@ void UserData::setBotInfo(const MTPBotInfo &info) {
botInfo->commands.push_back(BotCommand(qs(v.at(i).c_botCommand().vcommand), qs(v.at(i).c_botCommand().vparams), qs(v.at(i).c_botCommand().vdescription)));
}
}
botInfo->inited = true;
} break;
}
}

View File

@ -124,9 +124,13 @@ struct BotCommand {
QString command, params, description;
};
struct BotInfo {
BotInfo() : inited(false), version(0), text(st::msgMinWidth) {
}
bool inited;
int32 version;
QString shareText, description;
QList<BotCommand> commands;
Text text; // description
};
struct PhotoData;