Closed version 10019009: bubbles with tails.

This commit is contained in:
John Preston 2016-11-18 19:27:47 +03:00
parent 31a66d66e2
commit d607f0768a
20 changed files with 272 additions and 117 deletions

View File

@ -79,7 +79,7 @@ linkCropLimit: 360px;
linkFont: normalFont;
linkOverFont: font(fsize underline);
dateRadius: 10px;
dateRadius: 6px;
buttonRadius: 3px;
lnkText: #0f7dc7;
@ -104,8 +104,8 @@ msgMinWidth: 190px;
msgPhotoSize: 33px;
msgPhotoSkip: 40px;
msgPadding: margins(13px, 7px, 13px, 8px);
msgMargin: margins(13px, 10px, 53px, 2px);
msgMarginTopAttached: 3px;
msgMargin: margins(16px, 6px, 56px, 2px);
msgMarginTopAttached: 1px;
msgLnkPadding: 2px; // for media open / save links
msgBorder: #f0f0f0;
msgInBg: #ffffff;

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 B

View File

@ -34,8 +34,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 0,10,19,8
PRODUCTVERSION 0,10,19,8
FILEVERSION 0,10,19,9
PRODUCTVERSION 0,10,19,9
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@ -51,10 +51,10 @@ BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "Telegram Messenger LLP"
VALUE "FileVersion", "0.10.19.8"
VALUE "FileVersion", "0.10.19.9"
VALUE "LegalCopyright", "Copyright (C) 2014-2016"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "0.10.19.8"
VALUE "ProductVersion", "0.10.19.9"
END
END
BLOCK "VarFileInfo"

View File

@ -25,8 +25,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 0,10,19,8
PRODUCTVERSION 0,10,19,8
FILEVERSION 0,10,19,9
PRODUCTVERSION 0,10,19,9
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@ -43,10 +43,10 @@ BEGIN
BEGIN
VALUE "CompanyName", "Telegram Messenger LLP"
VALUE "FileDescription", "Telegram Updater"
VALUE "FileVersion", "0.10.19.8"
VALUE "FileVersion", "0.10.19.9"
VALUE "LegalCopyright", "Copyright (C) 2014-2016"
VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "0.10.19.8"
VALUE "ProductVersion", "0.10.19.9"
END
END
BLOCK "VarFileInfo"

View File

@ -2198,6 +2198,7 @@ namespace {
int msgRadius() {
static int MsgRadius = ([]() {
return st::historyMessageRadius;
auto minMsgHeight = (st::msgPadding.top() + st::msgFont->height + st::msgPadding.bottom());
return minMsgHeight / 2;
})();

View File

@ -22,7 +22,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "core/utils.h"
#define BETA_VERSION_MACRO (10019008ULL)
#define BETA_VERSION_MACRO (10019009ULL)
constexpr int AppVersion = 10020;
constexpr str_const AppVersionStr = "0.10.20";

View File

@ -1637,18 +1637,22 @@ HistoryItem *History::addNewInTheMiddle(HistoryItem *newItem, int32 blockIndex,
t_assert(blockIndex >= 0);
t_assert(blockIndex < blocks.size());
t_assert(itemIndex >= 0);
t_assert(itemIndex <= blocks.at(blockIndex)->items.size());
t_assert(itemIndex <= blocks[blockIndex]->items.size());
HistoryBlock *block = blocks.at(blockIndex);
auto block = blocks.at(blockIndex);
newItem->attachToBlock(block, itemIndex);
block->items.insert(itemIndex, newItem);
newItem->previousItemChanged();
for (int i = itemIndex + 1, l = block->items.size(); i < l; ++i) {
block->items.at(i)->setIndexInBlock(i);
}
if (itemIndex + 1 < block->items.size()) {
block->items.at(itemIndex + 1)->previousItemChanged();
for (int i = itemIndex + 1, l = block->items.size(); i < l; ++i) {
block->items[i]->setIndexInBlock(i);
}
block->items[itemIndex + 1]->previousItemChanged();
} else if (blockIndex + 1 < blocks.size() && !blocks[blockIndex + 1]->items.empty()) {
blocks[blockIndex + 1]->items.front()->previousItemChanged();
} else {
newItem->nextItemChanged();
}
return newItem;
@ -1666,14 +1670,18 @@ HistoryBlock *History::finishBuildingFrontBlock() {
t_assert(isBuildingFrontBlock());
// Some checks if there was some message history already
HistoryBlock *block = _buildingFrontBlock->block;
if (block && blocks.size() > 1) {
HistoryItem *last = block->items.back(); // ... item, item, item, last ], [ first, item, item ...
HistoryItem *first = blocks.at(1)->items.front();
auto block = _buildingFrontBlock->block;
if (block) {
if (blocks.size() > 1) {
auto last = block->items.back(); // ... item, item, item, last ], [ first, item, item ...
auto first = blocks.at(1)->items.front();
// we've added a new front block, so previous item for
// the old first item of a first block was changed
first->previousItemChanged();
// we've added a new front block, so previous item for
// the old first item of a first block was changed
first->previousItemChanged();
} else {
block->items.back()->nextItemChanged();
}
}
_buildingFrontBlock = nullptr;
@ -2106,11 +2114,13 @@ void History::removeBlock(HistoryBlock *block) {
int index = block->indexInHistory();
blocks.removeAt(index);
for (int i = index, l = blocks.size(); i < l; ++i) {
blocks.at(i)->setIndexInHistory(i);
}
if (index < blocks.size()) {
for (int i = index, l = blocks.size(); i < l; ++i) {
blocks.at(i)->setIndexInHistory(i);
}
blocks.at(index)->items.front()->previousItemChanged();
} else if (!blocks.empty() && !blocks.back()->items.empty()) {
blocks.back()->items.back()->nextItemChanged();
}
}
@ -2176,6 +2186,8 @@ void HistoryBlock::removeItem(HistoryItem *item) {
items.at(itemIndex)->previousItemChanged();
} else if (blockIndex + 1 < history->blocks.size()) {
history->blocks.at(blockIndex + 1)->items.front()->previousItemChanged();
} else if (!history->blocks.empty() && !history->blocks.back()->items.empty()) {
history->blocks.back()->items.back()->nextItemChanged();
}
if (items.isEmpty()) {

View File

@ -412,3 +412,16 @@ mentionFg: #777777;
mentionFgOver: #707070;
mentionFgActive: #0080c0;
mentionFgOverActive: #0077b3;
historyDateFadeDuration: 200;
historyPhotoLeft: 14px;
historyMessageRadius: 6px;
historyBubbleTailInLeft: icon {{ "bubble_tail", msgInBg }};
historyBubbleTailInLeftSelected: icon {{ "bubble_tail", msgInBgSelected }};
historyBubbleTailOutLeft: icon {{ "bubble_tail", msgOutBg }};
historyBubbleTailOutLeftSelected: icon {{ "bubble_tail", msgOutBgSelected }};
historyBubbleTailInRight: icon {{ "bubble_tail-flip_horizontal", msgInBg }};
historyBubbleTailInRightSelected: icon {{ "bubble_tail-flip_horizontal", msgInBgSelected }};
historyBubbleTailOutRight: icon {{ "bubble_tail-flip_horizontal", msgOutBg }};
historyBubbleTailOutRightSelected: icon {{ "bubble_tail-flip_horizontal", msgOutBgSelected }};

View File

@ -621,6 +621,9 @@ void HistoryItem::finishEditionToEmpty() {
if (auto next = nextItem()) {
next->previousItemChanged();
}
if (auto previous = previousItem()) {
previous->nextItemChanged();
}
}
void HistoryItem::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) {
@ -690,16 +693,22 @@ void HistoryItem::previousItemChanged() {
recountAttachToPrevious();
}
// Called only if there is no more next item! Not always when it changes!
void HistoryItem::nextItemChanged() {
setAttachToNext(false);
}
void HistoryItem::recountAttachToPrevious() {
bool attach = false;
if (!isPost() && !Has<HistoryMessageDate>() && !Has<HistoryMessageUnreadBar>()) {
if (auto previos = previousItem()) {
attach = !previos->isPost()
&& !previos->serviceMsg()
&& !previos->isEmpty()
&& previos->from() == from()
&& (qAbs(previos->date.secsTo(date)) < kAttachMessageToPreviousSecondsDelta);
if (auto previous = previousItem()) {
if (!isPost() && !Has<HistoryMessageDate>() && !Has<HistoryMessageUnreadBar>()) {
attach = !previous->isPost()
&& !previous->serviceMsg()
&& !previous->isEmpty()
&& previous->from() == from()
&& (qAbs(previous->date.secsTo(date)) < kAttachMessageToPreviousSecondsDelta);
}
previous->setAttachToNext(attach);
}
if (attach && !(_flags & MTPDmessage_ClientFlag::f_attach_to_previous)) {
_flags |= MTPDmessage_ClientFlag::f_attach_to_previous;
@ -710,6 +719,16 @@ void HistoryItem::recountAttachToPrevious() {
}
}
void HistoryItem::setAttachToNext(bool attachToNext) {
if (attachToNext && !(_flags & MTPDmessage_ClientFlag::f_attach_to_next)) {
_flags |= MTPDmessage_ClientFlag::f_attach_to_next;
Global::RefPendingRepaintItems().insert(this);
} else if (!attachToNext && (_flags & MTPDmessage_ClientFlag::f_attach_to_next)) {
_flags &= ~MTPDmessage_ClientFlag::f_attach_to_next;
Global::RefPendingRepaintItems().insert(this);
}
}
void HistoryItem::setId(MsgId newId) {
history()->changeMsgId(id, newId);
id = newId;

View File

@ -652,7 +652,9 @@ public:
virtual bool hasBubble() const {
return false;
}
virtual void previousItemChanged();
void previousItemChanged();
void nextItemChanged();
virtual TextWithEntities selectedText(TextSelection selection) const {
return { qsl("[-]"), EntitiesInText() };
@ -845,6 +847,9 @@ public:
bool isAttachedToPrevious() const {
return _flags & MTPDmessage_ClientFlag::f_attach_to_previous;
}
bool isAttachedToNext() const {
return _flags & MTPDmessage_ClientFlag::f_attach_to_next;
}
bool displayDate() const {
return Has<HistoryMessageDate>();
}
@ -909,16 +914,20 @@ protected:
return nullptr;
}
// this should be used only in previousItemChanged()
// this should be called only from previousItemChanged()
// to add required bits to the Composer mask
// after that always use Has<HistoryMessageDate>()
void recountDisplayDate();
// this should be used only in previousItemChanged() or when
// this should be called only from previousItemChanged() or when
// HistoryMessageDate or HistoryMessageUnreadBar bit is changed in the Composer mask
// then the result should be cached in a client side flag MTPDmessage_ClientFlag::f_attach_to_previous
void recountAttachToPrevious();
// this should be called only recountAttachToPrevious() of the next item
// or when the next item is removed through nextItemChanged() call
void setAttachToNext(bool attachToNext);
const HistoryMessageReplyMarkup *inlineReplyMarkup() const {
return const_cast<HistoryItem*>(this)->inlineReplyMarkup();
}

View File

@ -168,6 +168,9 @@ public:
bool isBubbleBottom() const {
return (_inBubbleState == MediaInBubbleState::Bottom) || (_inBubbleState == MediaInBubbleState::None);
}
virtual bool skipBubbleTail() const {
return false;
}
// Sometimes click on media in message is overloaded by the messsage:
// (for example it can open a link or a game instead of opening media)

View File

@ -172,6 +172,9 @@ public:
bool hideFromName() const override {
return true;
}
bool skipBubbleTail() const override {
return isBubbleBottom();
}
bool isReadyForOpen() const override {
return _data->loaded();
}
@ -259,6 +262,9 @@ public:
bool hideFromName() const override {
return true;
}
bool skipBubbleTail() const override {
return isBubbleBottom();
}
protected:
float64 dataProgress() const override {
@ -484,6 +490,9 @@ public:
bool hideFromName() const override {
return true;
}
bool skipBubbleTail() const override {
return isBubbleBottom();
}
bool isReadyForOpen() const override {
return _data->loaded();
}
@ -882,6 +891,10 @@ public:
return true;
}
bool skipBubbleTail() const override {
return isBubbleBottom();
}
private:
TextSelection toDescriptionSelection(TextSelection selection) const {
return internal::unshiftSelection(selection, _title);

View File

@ -1290,12 +1290,11 @@ void HistoryMessage::draw(Painter &p, const QRect &r, TextSelection selection, u
auto mediaDisplayed = _media && _media->isDisplayed();
auto top = marginTop();
QRect r(left, top, width, height - top - marginBottom());
auto r = QRect(left, top, width, height - top - marginBottom());
auto &bg = selected ? (outbg ? st::msgOutBgSelected : st::msgInBgSelected) : (outbg ? st::msgOutBg : st::msgInBg);
auto &sh = selected ? (outbg ? st::msgOutShadowSelected : st::msgInShadowSelected) : (outbg ? st::msgOutShadow : st::msgInShadow);
RoundCorners cors(selected ? (outbg ? MessageOutSelectedCorners : MessageInSelectedCorners) : (outbg ? MessageOutCorners : MessageInCorners));
App::roundRect(p, r, bg, cors, &sh);
auto skipTail = isAttachedToNext() || (_media && _media->skipBubbleTail());
auto displayTail = skipTail ? HistoryLayout::BubbleTail::None : (outbg && !Adaptive::Wide()) ? HistoryLayout::BubbleTail::Right : HistoryLayout::BubbleTail::Left;
HistoryLayout::paintBubble(p, r, _history->width, selected, outbg, displayTail);
QRect trect(r.marginsAdded(-st::msgPadding));
if (mediaDisplayed && _media->isBubbleTop()) {
@ -1335,7 +1334,7 @@ void HistoryMessage::draw(Painter &p, const QRect &r, TextSelection selection, u
HistoryMessage::drawInfo(p, r.x() + r.width(), r.y() + r.height(), 2 * r.x() + r.width(), selected, InfoDisplayDefault);
}
} else if (_media) {
int32 top = marginTop();
auto top = marginTop();
p.translate(left, top);
_media->draw(p, r.translated(-left, -top), toMediaSelection(selection), ms);
p.translate(-left, -top);
@ -1744,7 +1743,7 @@ QString HistoryMessage::notificationHeader() const {
}
bool HistoryMessage::displayFromPhoto() const {
return hasFromPhoto() && !isAttachedToPrevious();
return hasFromPhoto() && !isAttachedToNext();
}
bool HistoryMessage::hasFromPhoto() const {

View File

@ -352,4 +352,27 @@ void serviceColorsUpdated() {
}
}
void paintBubble(Painter &p, QRect rect, int outerWidth, bool selected, bool outbg, BubbleTail tail) {
auto &bg = selected ? (outbg ? st::msgOutBgSelected : st::msgInBgSelected) : (outbg ? st::msgOutBg : st::msgInBg);
auto &sh = selected ? (outbg ? st::msgOutShadowSelected : st::msgInShadowSelected) : (outbg ? st::msgOutShadow : st::msgInShadow);
auto cors = selected ? (outbg ? MessageOutSelectedCorners : MessageInSelectedCorners) : (outbg ? MessageOutCorners : MessageInCorners);
auto parts = App::RectPart::TopFull | App::RectPart::NoTopBottom | App::RectPart::Bottom;
if (tail == BubbleTail::Right) {
parts |= App::RectPart::BottomLeft;
p.fillRect(rect.x() + rect.width() - st::historyMessageRadius, rect.y() + rect.height() - st::historyMessageRadius, st::historyMessageRadius, st::historyMessageRadius, bg);
auto &tail = selected ? st::historyBubbleTailOutRightSelected : st::historyBubbleTailOutRight;
tail.paint(p, rect.x() + rect.width(), rect.y() + rect.height() - tail.height(), outerWidth);
p.fillRect(rect.x() + rect.width() - st::historyMessageRadius, rect.y() + rect.height(), st::historyMessageRadius + tail.width(), st::msgShadow, sh);
} else if (tail == BubbleTail::Left) {
parts |= App::RectPart::BottomRight;
p.fillRect(rect.x(), rect.y() + rect.height() - st::historyMessageRadius, st::historyMessageRadius, st::historyMessageRadius, bg);
auto &tail = selected ? (outbg ? st::historyBubbleTailOutLeftSelected : st::historyBubbleTailInLeftSelected) : (outbg ? st::historyBubbleTailOutLeft : st::historyBubbleTailInLeft);
tail.paint(p, rect.x() - tail.width(), rect.y() + rect.height() - tail.height(), outerWidth);
p.fillRect(rect.x() - tail.width(), rect.y() + rect.height(), st::historyMessageRadius + tail.width(), st::msgShadow, sh);
} else {
parts |= App::RectPart::BottomFull;
}
App::roundRect(p, rect, bg, cors, &sh, parts);
}
} // namespace HistoryLayout

View File

@ -52,4 +52,11 @@ void paintEmpty(Painter &p, int width, int height);
void serviceColorsUpdated();
enum class BubbleTail {
None,
Left,
Right,
};
void paintBubble(Painter &p, QRect rect, int outerWidth, bool selected, bool outbg, BubbleTail tail);
} // namespace HistoryLayout

View File

@ -175,24 +175,29 @@ void HistoryInner::repaintItem(const HistoryItem *item) {
}
namespace {
// helper binary search for an item in a list that is not completely below the given bottom of the visible area
// is applied once for blocks list in a history and once for items list in the found block
template <typename T>
int binarySearchBlocksOrItems(const T &list, int bottom) {
int start = 0, end = list.size();
while (end - start > 1) {
int middle = (start + end) / 2;
if (list.at(middle)->y >= bottom) {
end = middle;
} else {
start = middle;
}
// helper binary search for an item in a list that is not completely
// above the given top of the visible area or below the given bottom of the visible area
// is applied once for blocks list in a history and once for items list in the found block
template <bool TopToBottom, typename T>
int binarySearchBlocksOrItems(const T &list, int edge) {
auto start = 0, end = list.size();
while (end - start > 1) {
auto middle = (start + end) / 2;
auto top = list[middle]->y;
auto chooseLeft = (TopToBottom ? (top <= edge) : (top < edge));
if (chooseLeft) {
start = middle;
} else {
end = middle;
}
return start;
}
return start;
}
template <typename Method>
} // namespace
template <bool TopToBottom, typename Method>
void HistoryInner::enumerateItemsInHistory(History *history, int historytop, Method method) {
// no displayed messages in this history
if (historytop < 0 || history->isEmpty()) {
@ -202,43 +207,82 @@ void HistoryInner::enumerateItemsInHistory(History *history, int historytop, Met
return;
}
auto searchEdge = TopToBottom ? _visibleAreaTop : _visibleAreaBottom;
// binary search for blockIndex of the first block that is not completely below the visible area
int blockIndex = binarySearchBlocksOrItems(history->blocks, _visibleAreaBottom - historytop);
auto blockIndex = binarySearchBlocksOrItems<TopToBottom>(history->blocks, searchEdge - historytop);
// binary search for itemIndex of the first item that is not completely below the visible area
HistoryBlock *block = history->blocks.at(blockIndex);
int blocktop = historytop + block->y;
int itemIndex = binarySearchBlocksOrItems(block->items, _visibleAreaBottom - blocktop);
auto block = history->blocks.at(blockIndex);
auto blocktop = historytop + block->y;
auto blockbottom = blocktop + block->height;
auto itemIndex = binarySearchBlocksOrItems<TopToBottom>(block->items, searchEdge - blocktop);
while (true) {
while (itemIndex >= 0) {
HistoryItem *item = block->items.at(itemIndex--);
int itemtop = blocktop + item->y;
int itembottom = itemtop + item->height();
while (true) {
auto item = block->items.at(itemIndex);
auto itemtop = blocktop + item->y;
auto itembottom = itemtop + item->height();
// binary search should've skipped all the items that are below the visible area
t_assert(itemtop < _visibleAreaBottom);
// binary search should've skipped all the items that are above / below the visible area
if (TopToBottom) {
t_assert(itembottom > _visibleAreaTop);
} else {
t_assert(itemtop < _visibleAreaBottom);
}
if (!method(item, itemtop, itembottom)) {
return;
}
// skip all the items that are above the visible area
if (itemtop <= _visibleAreaTop) {
// skip all the items that are below / above the visible area
if (TopToBottom) {
if (itembottom >= _visibleAreaBottom) {
return;
}
} else {
if (itemtop <= _visibleAreaTop) {
return;
}
}
if (TopToBottom) {
if (++itemIndex >= block->items.size()) {
break;
}
} else {
if (--itemIndex < 0) {
break;
}
}
}
// skip all the rest blocks that are below / above the visible area
if (TopToBottom) {
if (blockbottom >= _visibleAreaBottom) {
return;
}
} else {
if (blocktop <= _visibleAreaTop) {
return;
}
}
// skip all the rest blocks that are above the visible area
if (blocktop <= _visibleAreaTop) {
return;
}
if (--blockIndex < 0) {
return;
if (TopToBottom) {
if (++blockIndex >= history->blocks.size()) {
return;
}
} else {
if (--blockIndex < 0) {
return;
}
}
block = history->blocks.at(blockIndex);
blocktop = historytop + block->y;
blockbottom = blocktop + block->height;
if (TopToBottom) {
itemIndex = 0;
} else {
block = history->blocks.at(blockIndex);
blocktop = historytop + block->y;
itemIndex = block->items.size() - 1;
}
}
@ -250,47 +294,48 @@ void HistoryInner::enumerateUserpics(Method method) {
return;
}
// find and remember the bottom of an attached messages pack
// -1 means we didn't find an attached to previous message yet
int lowestAttachedItemBottom = -1;
// find and remember the top of an attached messages pack
// -1 means we didn't find an attached to next message yet
int lowestAttachedItemTop = -1;
auto userpicCallback = [this, &lowestAttachedItemBottom, &method](HistoryItem *item, int itemtop, int itembottom) {
auto userpicCallback = [this, &lowestAttachedItemTop, &method](HistoryItem *item, int itemtop, int itembottom) {
// skip all service messages
auto message = item->toHistoryMessage();
if (!message) return true;
if (lowestAttachedItemBottom < 0 && message->isAttachedToPrevious()) {
lowestAttachedItemBottom = itembottom - message->marginBottom();
if (lowestAttachedItemTop < 0 && message->isAttachedToNext()) {
lowestAttachedItemTop = itemtop + message->marginTop();
}
// call method on a userpic for all messages that have it and for those who are not showing it
// because of their attachment to the previous message if they are top-most visible
if (message->displayFromPhoto() || (message->hasFromPhoto() && itemtop <= _visibleAreaTop)) {
if (lowestAttachedItemBottom < 0) {
lowestAttachedItemBottom = itembottom - message->marginBottom();
// because of their attachment to the next message if they are bottom-most visible
if (message->displayFromPhoto() || (message->hasFromPhoto() && itembottom >= _visibleAreaBottom)) {
if (lowestAttachedItemTop < 0) {
lowestAttachedItemTop = itemtop + message->marginTop();
}
// attach userpic to the top of the visible area with the same margin as it is from the left side
int userpicTop = qMax(itemtop + message->marginTop(), _visibleAreaTop + st::msgMargin.left());
// attach userpic to the bottom of the visible area with the same margin as the last message
auto userpicMinBottomSkip = st::historyPaddingBottom + st::msgMargin.bottom();
auto userpicBottom = qMin(itembottom - message->marginBottom(), _visibleAreaBottom - userpicMinBottomSkip);
// do not let the userpic go below the attached messages pack bottom line
userpicTop = qMin(userpicTop, lowestAttachedItemBottom - st::msgPhotoSize);
// do not let the userpic go above the attached messages pack top line
userpicBottom = qMax(userpicBottom, lowestAttachedItemTop + st::msgPhotoSize);
// call the template callback function that was passed
// and return if it finished everything it needed
if (!method(message, userpicTop)) {
if (!method(message, userpicBottom - st::msgPhotoSize)) {
return false;
}
}
// forget the found bottom of the pack, search for the next one from scratch
if (!message->isAttachedToPrevious()) {
lowestAttachedItemBottom = -1;
// forget the found top of the pack, search for the next one from scratch
if (!message->isAttachedToNext()) {
lowestAttachedItemTop = -1;
}
return true;
};
enumerateItems(userpicCallback);
enumerateItems<EnumItemsDirection::TopToBottom>(userpicCallback);
}
template <typename Method>
@ -346,7 +391,7 @@ void HistoryInner::enumerateDates(Method method) {
return true;
};
enumerateItems(dateCallback);
enumerateItems<EnumItemsDirection::BottomToTop>(dateCallback);
}
void HistoryInner::paintEvent(QPaintEvent *e) {
@ -494,13 +539,13 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
if (mtop >= 0 || htop >= 0) {
enumerateUserpics([&p, &r](HistoryMessage *message, int userpicTop) {
// stop the enumeration if the userpic is above the painted rect
if (userpicTop + st::msgPhotoSize <= r.top()) {
if (userpicTop >= r.top() + r.height()) {
return false;
}
// paint the userpic if it intersects the painted rect
if (userpicTop < r.top() + r.height()) {
message->from()->paintUserpicLeft(p, st::msgPhotoSize, st::msgMargin.left(), userpicTop, message->history()->width);
if (userpicTop + st::msgPhotoSize > r.top()) {
message->from()->paintUserpicLeft(p, st::msgPhotoSize, st::historyPhotoLeft, userpicTop, message->history()->width);
}
return true;
});
@ -1704,7 +1749,7 @@ void HistoryInner::toggleScrollDateShown() {
_scrollDateShown = !_scrollDateShown;
auto from = _scrollDateShown ? 0. : 1.;
auto to = _scrollDateShown ? 1. : 0.;
_scrollDateOpacity.start([this] { repaintScrollDateCallback(); }, from, to, st::historyAttach.duration);
_scrollDateOpacity.start([this] { repaintScrollDateCallback(); }, from, to, st::historyDateFadeDuration);
}
void HistoryInner::repaintScrollDateCallback() {
@ -1975,7 +2020,7 @@ void HistoryInner::onUpdateSelected() {
}
dragState = item->getState(m.x(), m.y(), request);
lnkhost = item;
if (!dragState.link && m.x() >= st::msgMargin.left() && m.x() < st::msgMargin.left() + st::msgPhotoSize) {
if (!dragState.link && m.x() >= st::historyPhotoLeft && m.x() < st::historyPhotoLeft + st::msgPhotoSize) {
if (auto msg = item->toHistoryMessage()) {
if (msg->hasFromPhoto()) {
enumerateUserpics([&dragState, &lnkhost, &point](HistoryMessage *message, int userpicTop) -> bool {

View File

@ -278,24 +278,32 @@ private:
HistoryItem *_scrollDateLastItem = nullptr;
int _scrollDateLastItemTop = 0;
enum class EnumItemsDirection {
TopToBottom,
BottomToTop,
};
// this function finds all history items that are displayed and calls template method
// for each found message (from the bottom to the top) in the passed history with passed top offset
// for each found message (in given direction) in the passed history with passed top offset
//
// method has "bool (*Method)(HistoryItem *item, int itemtop, int itembottom)" signature
// if it returns false the enumeration stops immidiately
template <typename Method>
template <bool TopToBottom, typename Method>
void enumerateItemsInHistory(History *history, int historytop, Method method);
template <typename Method>
template <EnumItemsDirection direction, typename Method>
void enumerateItems(Method method) {
enumerateItemsInHistory(_history, historyTop(), method);
if (_migrated) {
enumerateItemsInHistory(_migrated, migratedTop(), method);
constexpr auto TopToBottom = (direction == EnumItemsDirection::TopToBottom);
if (TopToBottom && _migrated) {
enumerateItemsInHistory<TopToBottom>(_migrated, migratedTop(), method);
}
enumerateItemsInHistory<TopToBottom>(_history, historyTop(), method);
if (!TopToBottom && _migrated) {
enumerateItemsInHistory<TopToBottom>(_migrated, migratedTop(), method);
}
}
// this function finds all userpics on the left that are displayed and calls template method
// for each found userpic (from the bottom to the top) using enumerateItems() method
// for each found userpic (from the top to the bottom) using enumerateItems() method
//
// method has "bool (*Method)(HistoryMessage *message, int userpicTop)" signature
// if it returns false the enumeration stops immidiately

View File

@ -999,17 +999,20 @@ enum class MTPDmessage_ClientFlag : int32 {
// message is attached to previous one when displaying the history
f_attach_to_previous = (1 << 25),
// message is attached to next one when displaying the history
f_attach_to_next = (1 << 24),
// message was sent from inline bot, need to re-set media when sent
f_from_inline_bot = (1 << 24),
f_from_inline_bot = (1 << 23),
// message has a switch inline keyboard button, need to return to inline
f_has_switch_inline_button = (1 << 23),
f_has_switch_inline_button = (1 << 22),
// message is generated on the client side and should be unread
f_clientside_unread = (1 << 22),
f_clientside_unread = (1 << 21),
// update this when adding new client side flags
MIN_FIELD = (1 << 22),
MIN_FIELD = (1 << 21),
};
DEFINE_MTP_CLIENT_FLAGS(MTPDmessage)

View File

@ -3,4 +3,4 @@ AppVersionStrMajor 0.10
AppVersionStrSmall 0.10.20
AppVersionStr 0.10.20
AlphaChannel 0
BetaVersion 10019008
BetaVersion 10019009