diff --git a/Telegram/Resources/basic.style b/Telegram/Resources/basic.style index f4bf337d3..235043880 100644 --- a/Telegram/Resources/basic.style +++ b/Telegram/Resources/basic.style @@ -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; diff --git a/Telegram/Resources/icons/bubble_tail.png b/Telegram/Resources/icons/bubble_tail.png new file mode 100644 index 000000000..451a3c7d4 Binary files /dev/null and b/Telegram/Resources/icons/bubble_tail.png differ diff --git a/Telegram/Resources/icons/bubble_tail@2x.png b/Telegram/Resources/icons/bubble_tail@2x.png new file mode 100644 index 000000000..a1cdf916e Binary files /dev/null and b/Telegram/Resources/icons/bubble_tail@2x.png differ diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index 6f364b393..15f81695f 100644 --- a/Telegram/Resources/winrc/Telegram.rc +++ b/Telegram/Resources/winrc/Telegram.rc @@ -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" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index 79ff23db8..bff3862db 100644 --- a/Telegram/Resources/winrc/Updater.rc +++ b/Telegram/Resources/winrc/Updater.rc @@ -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" diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 312bc8eb3..2c2513c03 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -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; })(); diff --git a/Telegram/SourceFiles/core/version.h b/Telegram/SourceFiles/core/version.h index 99f826d4a..c35b27dad 100644 --- a/Telegram/SourceFiles/core/version.h +++ b/Telegram/SourceFiles/core/version.h @@ -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"; diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index 038a91514..82e3a326b 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -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()) { diff --git a/Telegram/SourceFiles/history/history.style b/Telegram/SourceFiles/history/history.style index a05310bc4..dd8fe42fe 100644 --- a/Telegram/SourceFiles/history/history.style +++ b/Telegram/SourceFiles/history/history.style @@ -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 }}; diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 342958c57..2111f7b86 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -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() && !Has()) { - 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() && !Has()) { + 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; diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index fc12f2673..a09115f2f 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -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(); } @@ -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() 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(this)->inlineReplyMarkup(); } diff --git a/Telegram/SourceFiles/history/history_media.h b/Telegram/SourceFiles/history/history_media.h index fac47c497..eea88a510 100644 --- a/Telegram/SourceFiles/history/history_media.h +++ b/Telegram/SourceFiles/history/history_media.h @@ -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) diff --git a/Telegram/SourceFiles/history/history_media_types.h b/Telegram/SourceFiles/history/history_media_types.h index c2871553a..3de506cfd 100644 --- a/Telegram/SourceFiles/history/history_media_types.h +++ b/Telegram/SourceFiles/history/history_media_types.h @@ -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); diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index 77444931b..fcf94c7b2 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -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 { diff --git a/Telegram/SourceFiles/history/history_service_layout.cpp b/Telegram/SourceFiles/history/history_service_layout.cpp index 121800cab..beb585ba0 100644 --- a/Telegram/SourceFiles/history/history_service_layout.cpp +++ b/Telegram/SourceFiles/history/history_service_layout.cpp @@ -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 diff --git a/Telegram/SourceFiles/history/history_service_layout.h b/Telegram/SourceFiles/history/history_service_layout.h index faae5d450..d38482829 100644 --- a/Telegram/SourceFiles/history/history_service_layout.h +++ b/Telegram/SourceFiles/history/history_service_layout.h @@ -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 diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 73a1fc60c..bb1149ae7 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -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 - 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 +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 +} // namespace + +template 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(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(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(userpicCallback); } template @@ -346,7 +391,7 @@ void HistoryInner::enumerateDates(Method method) { return true; }; - enumerateItems(dateCallback); + enumerateItems(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 { diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h index 2ee78b572..e4ba2c91a 100644 --- a/Telegram/SourceFiles/historywidget.h +++ b/Telegram/SourceFiles/historywidget.h @@ -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 + template void enumerateItemsInHistory(History *history, int historytop, Method method); - template + template void enumerateItems(Method method) { - enumerateItemsInHistory(_history, historyTop(), method); - if (_migrated) { - enumerateItemsInHistory(_migrated, migratedTop(), method); + constexpr auto TopToBottom = (direction == EnumItemsDirection::TopToBottom); + if (TopToBottom && _migrated) { + enumerateItemsInHistory(_migrated, migratedTop(), method); + } + enumerateItemsInHistory(_history, historyTop(), method); + if (!TopToBottom && _migrated) { + enumerateItemsInHistory(_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 diff --git a/Telegram/SourceFiles/mtproto/core_types.h b/Telegram/SourceFiles/mtproto/core_types.h index f716ed230..edf3dd9c3 100644 --- a/Telegram/SourceFiles/mtproto/core_types.h +++ b/Telegram/SourceFiles/mtproto/core_types.h @@ -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) diff --git a/Telegram/build/version b/Telegram/build/version index 8a3949576..0b19c091b 100644 --- a/Telegram/build/version +++ b/Telegram/build/version @@ -3,4 +3,4 @@ AppVersionStrMajor 0.10 AppVersionStrSmall 0.10.20 AppVersionStr 0.10.20 AlphaChannel 0 -BetaVersion 10019008 +BetaVersion 10019009