diff --git a/Telegram/SourceFiles/history/history_admin_log_inner.cpp b/Telegram/SourceFiles/history/history_admin_log_inner.cpp index 6b05428865..e1f8f9ef55 100644 --- a/Telegram/SourceFiles/history/history_admin_log_inner.cpp +++ b/Telegram/SourceFiles/history/history_admin_log_inner.cpp @@ -22,6 +22,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "styles/style_history.h" #include "history/history_media_types.h" +#include "history/history_service_layout.h" #include "history/history_admin_log_section.h" #include "mainwindow.h" #include "window/window_controller.h" @@ -32,10 +33,166 @@ namespace AdminLog { namespace { constexpr auto kScrollDateHideTimeout = 1000; -constexpr auto kEventsPerPage = 10; +constexpr auto kEventsPerPage = 1; } // namespace +template +void InnerWidget::enumerateItems(Method method) { + constexpr auto TopToBottom = (direction == EnumItemsDirection::TopToBottom); + + // No displayed messages in this history. + if (_items.empty()) { + return; + } + if (_visibleBottom <= _itemsTop || _itemsTop + _itemsHeight <= _visibleTop) { + return; + } + + auto begin = std::rbegin(_items), end = std::rend(_items); + auto from = TopToBottom ? std::lower_bound(begin, end, _visibleTop, [this](auto &elem, int top) { + return itemTop(elem) + elem->height() <= top; + }) : std::upper_bound(begin, end, _visibleBottom, [this](int bottom, auto &elem) { + return itemTop(elem) + elem->height() >= bottom; + }); + auto wasEnd = (from == end); + if (wasEnd) { + --from; + } + if (TopToBottom) { + t_assert(itemTop(from->get()) + from->get()->height() > _visibleTop); + } else { + t_assert(itemTop(from->get()) < _visibleBottom); + } + + while (true) { + auto item = from->get(); + auto itemtop = itemTop(item); + auto itembottom = itemtop + item->height(); + + // Binary search should've skipped all the items that are above / below the visible area. + if (TopToBottom) { + t_assert(itembottom > _visibleTop); + } else { + t_assert(itemtop < _visibleBottom); + } + + if (!method(item, itemtop, itembottom)) { + return; + } + + // Skip all the items that are below / above the visible area. + if (TopToBottom) { + if (itembottom >= _visibleBottom) { + return; + } + } else { + if (itemtop <= _visibleTop) { + return; + } + } + + if (TopToBottom) { + if (++from == end) { + break; + } + } else { + if (from == begin) { + break; + } + --from; + } + } +} + +template +void InnerWidget::enumerateUserpics(Method method) { + // 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, &lowestAttachedItemTop, &method](HistoryItem *item, int itemtop, int itembottom) { + // Skip all service messages. + auto message = item->toHistoryMessage(); + if (!message) return true; + + 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 next message if they are bottom-most visible. + if (message->displayFromPhoto() || (message->hasFromPhoto() && itembottom >= _visibleBottom)) { + if (lowestAttachedItemTop < 0) { + lowestAttachedItemTop = itemtop + message->marginTop(); + } + // 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(), _visibleBottom - userpicMinBottomSkip); + + // 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, userpicBottom - st::msgPhotoSize)) { + return false; + } + } + + // Forget the found top of the pack, search for the next one from scratch. + if (!message->isAttachedToNext()) { + lowestAttachedItemTop = -1; + } + + return true; + }; + + enumerateItems(userpicCallback); +} + +template +void InnerWidget::enumerateDates(Method method) { + // Find and remember the bottom of an single-day messages pack + // -1 means we didn't find a same-day with previous message yet. + auto lowestInOneDayItemBottom = -1; + + auto dateCallback = [this, &lowestInOneDayItemBottom, &method](HistoryItem *item, int itemtop, int itembottom) { + if (lowestInOneDayItemBottom < 0 && item->isInOneDayWithPrevious()) { + lowestInOneDayItemBottom = itembottom - item->marginBottom(); + } + + // Call method on a date for all messages that have it and for those who are not showing it + // because they are in a one day together with the previous message if they are top-most visible. + if (item->displayDate() || (!item->isEmpty() && itemtop <= _visibleTop)) { + if (lowestInOneDayItemBottom < 0) { + lowestInOneDayItemBottom = itembottom - item->marginBottom(); + } + // Attach date to the top of the visible area with the same margin as it has in service message. + auto dateTop = qMax(itemtop, _visibleTop) + st::msgServiceMargin.top(); + + // Do not let the date go below the single-day messages pack bottom line. + auto dateHeight = st::msgServicePadding.bottom() + st::msgServiceFont->height + st::msgServicePadding.top(); + dateTop = qMin(dateTop, lowestInOneDayItemBottom - dateHeight); + + // Call the template callback function that was passed + // and return if it finished everything it needed. + if (!method(item, itemtop, dateTop)) { + return false; + } + } + + // Forget the found bottom of the pack, search for the next one from scratch. + if (!item->isInOneDayWithPrevious()) { + lowestInOneDayItemBottom = -1; + } + + return true; + }; + + enumerateItems(dateCallback); +} + InnerWidget::InnerWidget(QWidget *parent, gsl::not_null controller, gsl::not_null channel, base::lambda scrollTo) : TWidget(parent) , _controller(controller) , _channel(channel) @@ -62,17 +219,23 @@ InnerWidget::InnerWidget(QWidget *parent, gsl::not_null con } void InnerWidget::setVisibleTopBottom(int visibleTop, int visibleBottom) { + auto scrolledUp = (visibleTop < _visibleTop); _visibleTop = visibleTop; _visibleBottom = visibleBottom; updateVisibleTopItem(); checkPreloadMore(); + if (scrolledUp) { + _scrollDateCheck.call(); + } else { + scrollDateHideByTimer(); + } } void InnerWidget::updateVisibleTopItem() { - auto start = std::rbegin(_items), end = std::rend(_items); - auto from = std::upper_bound(start, end, _visibleTop, [](int top, auto &elem) { - return top <= elem->y() + elem->height(); + auto begin = std::rbegin(_items), end = std::rend(_items); + auto from = std::lower_bound(begin, end, _visibleTop, [this](auto &elem, int top) { + return itemTop(elem) + elem->height() <= top; }); if (from != end) { _visibleTopItem = *from; @@ -105,9 +268,7 @@ void InnerWidget::scrollDateCheck() { void InnerWidget::scrollDateHideByTimer() { _scrollDateHideTimer.cancel(); - if (!_scrollDateLink || ClickHandler::getPressed() != _scrollDateLink) { - scrollDateHide(); - } + scrollDateHide(); } void InnerWidget::scrollDateHide() { @@ -116,13 +277,6 @@ void InnerWidget::scrollDateHide() { } } -void InnerWidget::keepScrollDateForNow() { - if (!_scrollDateShown && _scrollDateLastItem && _scrollDateOpacity.animating()) { - toggleScrollDateShown(); - } - _scrollDateHideTimer.callOnce(kScrollDateHideTimeout); -} - void InnerWidget::toggleScrollDateShown() { _scrollDateShown = !_scrollDateShown; auto from = _scrollDateShown ? 0. : 1.; @@ -233,7 +387,8 @@ void InnerWidget::preloadMore(Direction direction) { App::feedChats(results.vchats); auto &events = results.vevents.v; if (!events.empty()) { - _items.reserve(_items.size() + events.size()); + auto oldItemsCount = _items.size(); + _items.reserve(oldItemsCount + events.size() * 2); for_const (auto &event, events) { t_assert(event.type() == mtpc_channelAdminLogEvent); auto &data = event.c_channelAdminLogEvent(); @@ -253,14 +408,29 @@ void InnerWidget::preloadMore(Direction direction) { } } } - if (!_items.empty()) { + auto newItemsCount = _items.size(); + if (newItemsCount != oldItemsCount) { + for (auto i = oldItemsCount; i != newItemsCount + 1; ++i) { + if (i > 0) { + auto item = _items[i - 1].get(); + if (i == newItemsCount) { + item->setLogEntryDisplayDate(true); + } else { + auto previous = _items[i].get(); + item->setLogEntryDisplayDate(item->date.date() != previous->date.date()); + auto attachToPrevious = item->computeIsAttachToPrevious(previous); + item->setLogEntryAttachToPrevious(attachToPrevious); + previous->setLogEntryAttachToNext(attachToPrevious); + } + } + } _maxId = (--_itemsByIds.end())->first; _minId = _itemsByIds.begin()->first; if (_minId == 1) { _upLoaded = true; } + itemsAdded(direction); } - itemsAdded(direction); } else { loadedFlag = true; } @@ -308,12 +478,12 @@ void InnerWidget::paintEvent(QPaintEvent *e) { if (_items.empty() && _upLoaded && _downLoaded) { paintEmpty(p); } else { - auto start = std::rbegin(_items), end = std::rend(_items); - auto from = std::upper_bound(start, end, clip.top(), [this](int top, auto &elem) { - return top <= itemTop(elem.get()) + elem->height(); + auto begin = std::rbegin(_items), end = std::rend(_items); + auto from = std::lower_bound(begin, end, clip.top(), [this](auto &elem, int top) { + return itemTop(elem) + elem->height() <= top; }); - auto to = std::lower_bound(start, end, clip.top() + clip.height(), [this](auto &elem, int bottom) { - return itemTop(elem.get()) < bottom; + auto to = std::lower_bound(begin, end, clip.top() + clip.height(), [this](auto &elem, int bottom) { + return itemTop(elem) < bottom; }); if (from != end) { auto top = itemTop(from->get()); @@ -324,6 +494,58 @@ void InnerWidget::paintEvent(QPaintEvent *e) { top += height; p.translate(0, height); } + p.translate(0, -top); + + enumerateUserpics([&p, &clip](HistoryMessage *message, int userpicTop) { + // stop the enumeration if the userpic is below the painted rect + if (userpicTop >= clip.top() + clip.height()) { + return false; + } + + // paint the userpic if it intersects the painted rect + if (userpicTop + st::msgPhotoSize > clip.top()) { + message->from()->paintUserpicLeft(p, st::historyPhotoLeft, userpicTop, message->width(), st::msgPhotoSize); + } + return true; + }); + + auto dateHeight = st::msgServicePadding.bottom() + st::msgServiceFont->height + st::msgServicePadding.top(); + auto scrollDateOpacity = _scrollDateOpacity.current(ms, _scrollDateShown ? 1. : 0.); + enumerateDates([&p, &clip, scrollDateOpacity, dateHeight/*, lastDate, showFloatingBefore*/](HistoryItem *item, int itemtop, int dateTop) { + // stop the enumeration if the date is above the painted rect + if (dateTop + dateHeight <= clip.top()) { + return false; + } + + bool displayDate = item->displayDate(); + bool dateInPlace = displayDate; + if (dateInPlace) { + int correctDateTop = itemtop + st::msgServiceMargin.top(); + dateInPlace = (dateTop < correctDateTop + dateHeight); + } + //bool noFloatingDate = (item->date.date() == lastDate && displayDate); + //if (noFloatingDate) { + // if (itemtop < showFloatingBefore) { + // noFloatingDate = false; + // } + //} + + // paint the date if it intersects the painted rect + if (dateTop < clip.top() + clip.height()) { + auto opacity = (dateInPlace/* || noFloatingDate*/) ? 1. : scrollDateOpacity; + if (opacity > 0.) { + p.setOpacity(opacity); + int dateY = /*noFloatingDate ? itemtop :*/ (dateTop - st::msgServiceMargin.top()); + int width = item->width(); + if (auto date = item->Get()) { + date->paint(p, dateY, width); + } else { + HistoryLayout::ServiceMessagePainter::paintDate(p, item->date, dateY, width); + } + } + } + return true; + }); } } } @@ -354,18 +576,10 @@ void InnerWidget::mousePressEvent(QMouseEvent *e) { } void InnerWidget::mouseMoveEvent(QMouseEvent *e) { - static auto lastGlobalPosition = e->globalPos(); - auto reallyMoved = (lastGlobalPosition != e->globalPos()); auto buttonsPressed = (e->buttons() & (Qt::LeftButton | Qt::MiddleButton)); if (!buttonsPressed && _mouseAction != MouseAction::None) { mouseReleaseEvent(e); } - if (reallyMoved) { - lastGlobalPosition = e->globalPos(); - if (!buttonsPressed || (_scrollDateLink && ClickHandler::getPressed() == _scrollDateLink)) { - keepScrollDateForNow(); - } - } mouseActionUpdate(e->globalPos()); } @@ -531,9 +745,9 @@ void InnerWidget::updateSelected() { auto point = QPoint(snap(mousePosition.x(), 0, width()), snap(mousePosition.y(), _visibleTop, _visibleBottom)); auto itemPoint = QPoint(); - auto start = std::rbegin(_items), end = std::rend(_items); - auto from = (point.y() >= _itemsTop && point.y() < _itemsTop + _itemsHeight) ? std::upper_bound(start, end, point.y(), [this](int top, auto &elem) { - return top <= itemTop(elem.get()) + elem->height(); + auto begin = std::rbegin(_items), end = std::rend(_items); + auto from = (point.y() >= _itemsTop && point.y() < _itemsTop + _itemsHeight) ? std::lower_bound(begin, end, point.y(), [this](auto &elem, int top) { + return itemTop(elem) + elem->height() <= top; }) : end; auto item = (from != end) ? from->get() : nullptr; if (item) { @@ -561,83 +775,31 @@ void InnerWidget::updateSelected() { InvokeQueued(this, [this] { performDrag(); }); } } + HistoryStateRequest request; + if (_mouseAction == MouseAction::Selecting) { + request.flags |= Text::StateRequest::Flag::LookupSymbol; + } else { + selectingText = false; + } + dragState = item->getState(itemPoint, request); + lnkhost = item; + if (!dragState.link && itemPoint.x() >= st::historyPhotoLeft && itemPoint.x() < st::historyPhotoLeft + st::msgPhotoSize) { + if (auto msg = item->toHistoryMessage()) { + if (msg->hasFromPhoto()) { + enumerateUserpics([&dragState, &lnkhost, &point](HistoryMessage *message, int userpicTop) -> bool { + // stop enumeration if the userpic is below our point + if (userpicTop > point.y()) { + return false; + } - auto dateHeight = st::msgServicePadding.bottom() + st::msgServiceFont->height + st::msgServicePadding.top(); - auto scrollDateOpacity = _scrollDateOpacity.current(_scrollDateShown ? 1. : 0.); - //enumerateDates([this, &dragState, &lnkhost, &point, scrollDateOpacity, dateHeight/*, lastDate, showFloatingBefore*/](HistoryItem *item, int itemtop, int dateTop) { - // // stop enumeration if the date is above our point - // if (dateTop + dateHeight <= point.y()) { - // return false; - // } - - // bool displayDate = item->displayDate(); - // bool dateInPlace = displayDate; - // if (dateInPlace) { - // int correctDateTop = itemtop + st::msgServiceMargin.top(); - // dateInPlace = (dateTop < correctDateTop + dateHeight); - // } - - // // stop enumeration if we've found a date under the cursor - // if (dateTop <= point.y()) { - // auto opacity = (dateInPlace/* || noFloatingDate*/) ? 1. : scrollDateOpacity; - // if (opacity > 0.) { - // auto dateWidth = 0; - // if (auto date = item->Get()) { - // dateWidth = date->_width; - // } else { - // dateWidth = st::msgServiceFont->width(langDayOfMonthFull(item->date.date())); - // } - // dateWidth += st::msgServicePadding.left() + st::msgServicePadding.right(); - // auto dateLeft = st::msgServiceMargin.left(); - // auto maxwidth = item->history()->width; - // if (Adaptive::ChatWide()) { - // maxwidth = qMin(maxwidth, int32(st::msgMaxWidth + 2 * st::msgPhotoSkip + 2 * st::msgMargin.left())); - // } - // auto widthForDate = maxwidth - st::msgServiceMargin.left() - st::msgServiceMargin.left(); - - // dateLeft += (widthForDate - dateWidth) / 2; - - // if (point.x() >= dateLeft && point.x() < dateLeft + dateWidth) { - // if (!_scrollDateLink) { - // _scrollDateLink = MakeShared(item->history()->peer, item->date.date()); - // } else { - // static_cast(_scrollDateLink.data())->setDate(item->date.date()); - // } - // dragState.link = _scrollDateLink; - // lnkhost = item; - // } - // } - // return false; - // } - // return true; - //}); // TODO - if (!dragState.link) { - HistoryStateRequest request; - if (_mouseAction == MouseAction::Selecting) { - request.flags |= Text::StateRequest::Flag::LookupSymbol; - } else { - selectingText = false; - } - dragState = item->getState(itemPoint, request); - lnkhost = item; - if (!dragState.link && itemPoint.x() >= st::historyPhotoLeft && itemPoint.x() < st::historyPhotoLeft + st::msgPhotoSize) { - if (auto msg = item->toHistoryMessage()) { - if (msg->hasFromPhoto()) { - //enumerateUserpics([&dragState, &lnkhost, &point](HistoryMessage *message, int userpicTop) -> bool { - // // stop enumeration if the userpic is below our point - // if (userpicTop > point.y()) { - // return false; - // } - - // // stop enumeration if we've found a userpic under the cursor - // if (point.y() >= userpicTop && point.y() < userpicTop + st::msgPhotoSize) { - // dragState.link = message->from()->openLink(); - // lnkhost = message; - // return false; - // } - // return true; - //}); // TODO - } + // stop enumeration if we've found a userpic under the cursor + if (point.y() >= userpicTop && point.y() < userpicTop + st::msgPhotoSize) { + dragState.link = message->from()->openLink(); + lnkhost = message; + return false; + } + return true; + }); } } } diff --git a/Telegram/SourceFiles/history/history_admin_log_inner.h b/Telegram/SourceFiles/history/history_admin_log_inner.h index eb4fe4ed05..dc074ff296 100644 --- a/Telegram/SourceFiles/history/history_admin_log_inner.h +++ b/Telegram/SourceFiles/history/history_admin_log_inner.h @@ -111,6 +111,10 @@ private: Dragging, Selecting, }; + enum class EnumItemsDirection { + TopToBottom, + BottomToTop, + }; void mouseActionStart(const QPoint &screenPos, Qt::MouseButton button); void mouseActionUpdate(const QPoint &screenPos); @@ -134,10 +138,33 @@ private: void repaintScrollDateCallback(); bool displayScrollDate() const; void scrollDateHide(); - void keepScrollDateForNow(); void scrollDateCheck(); void scrollDateHideByTimer(); + // This function finds all history items that are displayed and calls template method + // 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 + void enumerateItems(Method method); + + // This function finds all userpics on the left that are displayed and calls template 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. + template + void enumerateUserpics(Method method); + + // This function finds all date elements that are displayed and calls template method + // for each found date element (from the bottom to the top) using enumerateItems() method. + // + // Method has "bool (*Method)(HistoryItem *item, int itemtop, int dateTop)" signature + // if it returns false the enumeration stops immidiately. + template + void enumerateDates(Method method); + gsl::not_null _controller; gsl::not_null _channel; gsl::not_null _history; @@ -161,7 +188,6 @@ private: base::Timer _scrollDateHideTimer; HistoryItem *_scrollDateLastItem = nullptr; int _scrollDateLastItemTop = 0; - ClickHandlerPtr _scrollDateLink; // Up - max, Down - min. uint64 _maxId = 0; diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 030bfd1996..2011cf25ea 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -57,7 +57,27 @@ private: }; -std::unique_ptr mimeDataFromTextWithEntities(const TextWithEntities &forClipboard) { +// 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) { + // static_cast to work around GCC bug #78693 + auto start = 0, end = static_cast(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; +} + +std::unique_ptr MimeDataFromTextWithEntities(const TextWithEntities &forClipboard) { if (forClipboard.text.isEmpty()) { return nullptr; } @@ -149,33 +169,9 @@ void HistoryInner::repaintItem(const HistoryItem *item) { } } -namespace { - -// 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) { - // static_cast to work around GCC bug #78693 - auto start = 0, end = static_cast(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; -} - -} // namespace - template void HistoryInner::enumerateItemsInHistory(History *history, int historytop, Method method) { - // no displayed messages in this history + // No displayed messages in this history. if (historytop < 0 || history->isEmpty()) { return; } @@ -185,14 +181,14 @@ void HistoryInner::enumerateItemsInHistory(History *history, int historytop, Met auto searchEdge = TopToBottom ? _visibleAreaTop : _visibleAreaBottom; - // binary search for blockIndex of the first block that is not completely below the visible area - auto blockIndex = binarySearchBlocksOrItems(history->blocks, searchEdge - historytop); + // Binary search for blockIndex of the first block that is not completely below the visible area. + auto blockIndex = BinarySearchBlocksOrItems(history->blocks, searchEdge - historytop); - // binary search for itemIndex of the first item that is not completely below the visible area + // Binary search for itemIndex of the first item that is not completely below the visible area. auto block = history->blocks.at(blockIndex); auto blocktop = historytop + block->y(); auto blockbottom = blocktop + block->height(); - auto itemIndex = binarySearchBlocksOrItems(block->items, searchEdge - blocktop); + auto itemIndex = BinarySearchBlocksOrItems(block->items, searchEdge - blocktop); while (true) { while (true) { @@ -200,7 +196,7 @@ void HistoryInner::enumerateItemsInHistory(History *history, int historytop, Met auto itemtop = blocktop + item->y(); auto itembottom = itemtop + item->height(); - // binary search should've skipped all the items that are above / below the visible area + // Binary search should've skipped all the items that are above / below the visible area. if (TopToBottom) { t_assert(itembottom > _visibleAreaTop); } else { @@ -211,7 +207,7 @@ void HistoryInner::enumerateItemsInHistory(History *history, int historytop, Met return; } - // skip all the items that are below / above the visible area + // Skip all the items that are below / above the visible area. if (TopToBottom) { if (itembottom >= _visibleAreaBottom) { return; @@ -233,7 +229,7 @@ void HistoryInner::enumerateItemsInHistory(History *history, int historytop, Met } } - // skip all the rest blocks that are below / above the visible area + // Skip all the rest blocks that are below / above the visible area. if (TopToBottom) { if (blockbottom >= _visibleAreaBottom) { return; @@ -270,12 +266,12 @@ void HistoryInner::enumerateUserpics(Method method) { return; } - // find and remember the top of an attached messages pack - // -1 means we didn't find an attached to next message yet + // 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, &lowestAttachedItemTop, &method](HistoryItem *item, int itemtop, int itembottom) { - // skip all service messages + // Skip all service messages. auto message = item->toHistoryMessage(); if (!message) return true; @@ -283,27 +279,27 @@ void HistoryInner::enumerateUserpics(Method method) { 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 next message if they are bottom-most visible + // 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 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 bottom of the visible area with the same margin as the last message + // 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 above the attached messages pack top line + // 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 + // Call the template callback function that was passed + // and return if it finished everything it needed. if (!method(message, userpicBottom - st::msgPhotoSize)) { return false; } } - // forget the found top of the pack, search for the next one from scratch + // Forget the found top of the pack, search for the next one from scratch. if (!message->isAttachedToNext()) { lowestAttachedItemTop = -1; } @@ -316,28 +312,28 @@ void HistoryInner::enumerateUserpics(Method method) { template void HistoryInner::enumerateDates(Method method) { - int drawtop = historyDrawTop(); + auto drawtop = historyDrawTop(); - // find and remember the bottom of an single-day messages pack - // -1 means we didn't find a same-day with previous message yet - int lowestInOneDayItemBottom = -1; + // Find and remember the bottom of an single-day messages pack + // -1 means we didn't find a same-day with previous message yet. + auto lowestInOneDayItemBottom = -1; auto dateCallback = [this, &lowestInOneDayItemBottom, &method, drawtop](HistoryItem *item, int itemtop, int itembottom) { if (lowestInOneDayItemBottom < 0 && item->isInOneDayWithPrevious()) { lowestInOneDayItemBottom = itembottom - item->marginBottom(); } - // call method on a date for all messages that have it and for those who are not showing it - // because they are in a one day together with the previous message if they are top-most visible + // Call method on a date for all messages that have it and for those who are not showing it + // because they are in a one day together with the previous message if they are top-most visible. if (item->displayDate() || (!item->isEmpty() && itemtop <= _visibleAreaTop)) { // skip the date of history migrate item if it will be in migrated if (itemtop < drawtop && item->history() == _history) { if (itemtop > _visibleAreaTop) { - // previous item (from the _migrated history) is drawing date now + // Previous item (from the _migrated history) is drawing date now. return false; } else if (item == _history->blocks.front()->items.front() && item->isGroupMigrate() && _migrated->blocks.back()->items.back()->isGroupMigrate()) { - // this item is completely invisible and should be completely ignored + // This item is completely invisible and should be completely ignored. return false; } } @@ -345,21 +341,21 @@ void HistoryInner::enumerateDates(Method method) { if (lowestInOneDayItemBottom < 0) { lowestInOneDayItemBottom = itembottom - item->marginBottom(); } - // attach date to the top of the visible area with the same margin as it has in service message + // Attach date to the top of the visible area with the same margin as it has in service message. int dateTop = qMax(itemtop, _visibleAreaTop) + st::msgServiceMargin.top(); - // do not let the date go below the single-day messages pack bottom line + // Do not let the date go below the single-day messages pack bottom line. int dateHeight = st::msgServicePadding.bottom() + st::msgServiceFont->height + st::msgServicePadding.top(); dateTop = qMin(dateTop, lowestInOneDayItemBottom - dateHeight); - // call the template callback function that was passed - // and return if it finished everything it needed + // Call the template callback function that was passed + // and return if it finished everything it needed. if (!method(item, itemtop, dateTop)) { return false; } } - // forget the found bottom of the pack, search for the next one from scratch + // Forget the found bottom of the pack, search for the next one from scratch. if (!item->isInOneDayWithPrevious()) { lowestInOneDayItemBottom = -1; } @@ -953,7 +949,7 @@ void HistoryInner::performDrag() { // urls.push_back(QUrl::fromEncoded(sel.toUtf8())); // Google Chrome crashes in Mac OS X O_o //} } - if (auto mimeData = mimeDataFromTextWithEntities(sel)) { + if (auto mimeData = MimeDataFromTextWithEntities(sel)) { updateDragSelection(0, 0, false); _widget->noSelectingScroll(); @@ -1491,7 +1487,7 @@ void HistoryInner::copyContextText() { } void HistoryInner::setToClipboard(const TextWithEntities &forClipboard, QClipboard::Mode mode) { - if (auto data = mimeDataFromTextWithEntities(forClipboard)) { + if (auto data = MimeDataFromTextWithEntities(forClipboard)) { QApplication::clipboard()->setMimeData(data.release(), mode); } } diff --git a/Telegram/SourceFiles/history/history_inner_widget.h b/Telegram/SourceFiles/history/history_inner_widget.h index de11669df8..882f78f56b 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.h +++ b/Telegram/SourceFiles/history/history_inner_widget.h @@ -269,11 +269,11 @@ private: TopToBottom, BottomToTop, }; - // this function finds all history items that are displayed and calls template method - // for each found message (in given direction) in the passed history with passed top offset + // This function finds all history items that are displayed and calls template method + // 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 + // Method has "bool (*Method)(HistoryItem *item, int itemtop, int itembottom)" signature + // if it returns false the enumeration stops immidiately. template void enumerateItemsInHistory(History *history, int historytop, Method method); @@ -289,19 +289,19 @@ private: } } - // this function finds all userpics on the left that are displayed and calls template method - // for each found userpic (from the top to the bottom) using enumerateItems() method + // This function finds all userpics on the left that are displayed and calls template 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 + // Method has "bool (*Method)(HistoryMessage *message, int userpicTop)" signature + // if it returns false the enumeration stops immidiately. template void enumerateUserpics(Method method); - // this function finds all date elements that are displayed and calls template method - // for each found date element (from the bottom to the top) using enumerateItems() method + // This function finds all date elements that are displayed and calls template method + // for each found date element (from the bottom to the top) using enumerateItems() method. // - // method has "bool (*Method)(HistoryItem *item, int itemtop, int dateTop)" signature - // if it returns false the enumeration stops immidiately + // Method has "bool (*Method)(HistoryItem *item, int itemtop, int dateTop)" signature + // if it returns false the enumeration stops immidiately. template void enumerateDates(Method method); diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index d33a4338ca..4d74486dc6 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -735,17 +735,22 @@ void HistoryItem::nextItemChanged() { setAttachToNext(false); } +bool HistoryItem::computeIsAttachToPrevious(gsl::not_null previous) { + if (!Has() && !Has()) { + return !isPost() && !previous->isPost() + && !serviceMsg() && !previous->serviceMsg() + && !isEmpty() && !previous->isEmpty() + && previous->from() == from() + && (qAbs(previous->date.secsTo(date)) < kAttachMessageToPreviousSecondsDelta); + } + return false; +} + void HistoryItem::recountAttachToPrevious() { Expects(!isLogEntry()); auto attachToPrevious = false; if (auto previous = previousItem()) { - if (!Has() && !Has()) { - attachToPrevious = !isPost() && !previous->isPost() - && !serviceMsg() && !previous->serviceMsg() - && !isEmpty() && !previous->isEmpty() - && previous->from() == from() - && (qAbs(previous->date.secsTo(date)) < kAttachMessageToPreviousSecondsDelta); - } + attachToPrevious = computeIsAttachToPrevious(previous); previous->setAttachToNext(attachToPrevious); } setAttachToPrevious(attachToPrevious); diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index aff016c1f2..cf5bca7614 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -880,13 +880,14 @@ public: void clipCallback(Media::Clip::Notification notification); void audioTrackUpdated(); + bool computeIsAttachToPrevious(gsl::not_null previous); void setLogEntryDisplayDate(bool displayDate) { Expects(isLogEntry()); setDisplayDate(displayDate); } void setLogEntryAttachToPrevious(bool attachToPrevious) { Expects(isLogEntry()); - setAttachToNext(attachToPrevious); + setAttachToPrevious(attachToPrevious); } void setLogEntryAttachToNext(bool attachToNext) { Expects(isLogEntry());