diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp index bd1eb1f40..cd0b40735 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp @@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/admin_log/history_admin_log_section.h" #include "history/admin_log/history_admin_log_filter.h" #include "history/view/history_view_service_message.h" +#include "history/view/history_view_message.h" #include "chat_helpers/message_field.h" #include "mainwindow.h" #include "mainwidget.h" @@ -54,16 +55,16 @@ void InnerWidget::enumerateItems(Method method) { auto begin = std::rbegin(_items), end = std::rend(_items); auto from = TopToBottom ? std::lower_bound(begin, end, _visibleTop, [this](auto &elem, int top) { - return this->itemTop(elem) + elem->height() <= top; + return this->itemTop(elem) + elem->data()->height() <= top; }) : std::upper_bound(begin, end, _visibleBottom, [this](int bottom, auto &elem) { - return this->itemTop(elem) + elem->height() >= bottom; + return this->itemTop(elem) + elem->data()->height() >= bottom; }); auto wasEnd = (from == end); if (wasEnd) { --from; } if (TopToBottom) { - Assert(itemTop(from->get()) + from->get()->height() > _visibleTop); + Assert(itemTop(from->get()) + from->get()->data()->height() > _visibleTop); } else { Assert(itemTop(from->get()) < _visibleBottom); } @@ -71,7 +72,7 @@ void InnerWidget::enumerateItems(Method method) { while (true) { auto item = from->get(); auto itemtop = itemTop(item); - auto itembottom = itemtop + item->height(); + auto itembottom = itemtop + item->data()->height(); // Binary search should've skipped all the items that are above / below the visible area. if (TopToBottom) { @@ -114,8 +115,9 @@ void InnerWidget::enumerateUserpics(Method method) { // -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) { + auto userpicCallback = [&](Message *view, int itemtop, int itembottom) { // Skip all service messages. + const auto item = view->data(); auto message = item->toHistoryMessage(); if (!message) return true; @@ -160,7 +162,8 @@ void InnerWidget::enumerateDates(Method method) { // -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) { + auto dateCallback = [&](Message *view, int itemtop, int itembottom) { + const auto item = view->data(); if (lowestInOneDayItemBottom < 0 && item->isInOneDayWithPrevious()) { lowestInOneDayItemBottom = itembottom - item->marginBottom(); } @@ -211,7 +214,7 @@ InnerWidget::InnerWidget( Auth().data().itemRepaintRequest( ) | rpl::start_with_next([this](auto item) { if (item->isLogEntry() && _history == item->history()) { - repaintItem(item); + repaintItem(viewForItem(item)); } }, lifetime()); subscribe(Auth().data().pendingHistoryResize(), [this] { handlePendingHistoryResize(); }); @@ -219,9 +222,11 @@ InnerWidget::InnerWidget( if (_history != query.item->history() || !query.item->isLogEntry() || !isVisible()) { return; } - auto top = itemTop(query.item); - if (top >= 0 && top + query.item->height() > _visibleTop && top < _visibleBottom) { - *query.isVisible = true; + if (const auto view = viewForItem(query.item)) { + auto top = itemTop(view); + if (top >= 0 && top + query.item->height() > _visibleTop && top < _visibleBottom) { + *query.isVisible = true; + } } }); updateEmptyText(); @@ -252,7 +257,7 @@ void InnerWidget::updateVisibleTopItem() { } else { auto begin = std::rbegin(_items), end = std::rend(_items); auto from = std::lower_bound(begin, end, _visibleTop, [this](auto &&elem, int top) { - return this->itemTop(elem) + elem->height() <= top; + return this->itemTop(elem) + elem->data()->height() <= top; }); if (from != end) { _visibleTopItem = *from; @@ -516,7 +521,7 @@ void InnerWidget::addEvents(Direction direction, const QVector(); + auto newItemsForDownDirection = std::vector(); auto oldItemsCount = _items.size(); auto &addToItems = (direction == Direction::Up) ? _items : newItemsForDownDirection; addToItems.reserve(oldItemsCount + events.size() * 2); @@ -528,8 +533,9 @@ void InnerWidget::addEvents(Direction direction, const QVectordata(), item.get()); addToItems.push_back(std::move(item)); ++count; }); @@ -575,9 +581,9 @@ void InnerWidget::itemsAdded(Direction direction, int addedCount) { auto checkTo = (direction == Direction::Up) ? (_items.size() + 1) : (addedCount + 1); for (auto i = checkFrom; i != checkTo; ++i) { if (i > 0) { - auto item = _items[i - 1].get(); + const auto item = _items[i - 1]->data(); if (i < _items.size()) { - auto previous = _items[i].get(); + const auto previous = _items[i]->data(); item->setLogEntryDisplayDate(item->date.date() != previous->date.date()); auto attachToPrevious = item->computeIsAttachToPrevious(previous); item->setLogEntryAttachToPrevious(attachToPrevious); @@ -603,7 +609,7 @@ int InnerWidget::resizeGetHeight(int newWidth) { auto newHeight = 0; for (auto &item : base::reversed(_items)) { item->setY(newHeight); - newHeight += item->resizeGetHeight(newWidth); + newHeight += item->data()->resizeGetHeight(newWidth); } _itemsHeight = newHeight; _itemsTop = (_minHeight > _itemsHeight + st::historyPaddingBottom) ? (_minHeight - _itemsHeight - st::historyPaddingBottom) : 0; @@ -611,7 +617,9 @@ int InnerWidget::resizeGetHeight(int newWidth) { } void InnerWidget::restoreScrollPosition() { - auto newVisibleTop = _visibleTopItem ? (itemTop(_visibleTopItem) + _visibleTopFromItem) : ScrollMax; + auto newVisibleTop = _visibleTopItem + ? (itemTop(_visibleTopItem) + _visibleTopFromItem) + : ScrollMax; scrollToSignal.notify(newVisibleTop, true); } @@ -630,7 +638,7 @@ void InnerWidget::paintEvent(QPaintEvent *e) { } else { auto begin = std::rbegin(_items), end = std::rend(_items); auto from = std::lower_bound(begin, end, clip.top(), [this](auto &elem, int top) { - return this->itemTop(elem) + elem->height() <= top; + return this->itemTop(elem) + elem->data()->height() <= top; }); auto to = std::lower_bound(begin, end, clip.top() + clip.height(), [this](auto &elem, int bottom) { return this->itemTop(elem) < bottom; @@ -639,15 +647,19 @@ void InnerWidget::paintEvent(QPaintEvent *e) { auto top = itemTop(from->get()); p.translate(0, top); for (auto i = from; i != to; ++i) { - auto selection = (*i == _selectedItem) ? _selectedText : TextSelection(); - (*i)->draw(p, clip.translated(0, -top), selection, ms); - auto height = (*i)->height(); + const auto view = i->get(); + const auto selection = (view == _selectedItem) + ? _selectedText + : TextSelection(); + const auto item = view->data(); + item->draw(p, clip.translated(0, -top), selection, ms); + auto height = item->height(); top += height; p.translate(0, height); } p.translate(0, -top); - enumerateUserpics([&p, &clip](not_null message, int userpicTop) { + enumerateUserpics([&](not_null message, int userpicTop) { // stop the enumeration if the userpic is below the painted rect if (userpicTop >= clip.top() + clip.height()) { return false; @@ -662,7 +674,7 @@ void InnerWidget::paintEvent(QPaintEvent *e) { 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*/](not_null item, int itemtop, int dateTop) { + enumerateDates([&](not_null item, int itemtop, int dateTop) { // stop the enumeration if the date is above the painted rect if (dateTop + dateHeight <= clip.top()) { return false; @@ -721,6 +733,16 @@ void InnerWidget::clearAfterFilterChange() { updateSize(); } +auto InnerWidget::viewForItem(const HistoryItem *item) -> Message* { + if (item) { + const auto i = _itemsByData.find(item); + if (i != _itemsByData.end()) { + return i->second; + } + } + return nullptr; +} + void InnerWidget::paintEmpty(Painter &p) { style::font font(st::msgServiceFont); auto rectWidth = st::historyAdminLogEmptyWidth; @@ -739,7 +761,9 @@ void InnerWidget::paintEmpty(Painter &p) { } TextWithEntities InnerWidget::getSelectedText() const { - return _selectedItem ? _selectedItem->selectedText(_selectedText) : TextWithEntities(); + return _selectedItem + ? _selectedItem->data()->selectedText(_selectedText) + : TextWithEntities(); } void InnerWidget::keyPressEvent(QKeyEvent *e) { @@ -761,7 +785,7 @@ void InnerWidget::mouseDoubleClickEvent(QMouseEvent *e) { if (((_mouseAction == MouseAction::Selecting && _selectedItem != nullptr) || (_mouseAction == MouseAction::None)) && _mouseSelectType == TextSelectType::Letters && _mouseActionItem) { HistoryStateRequest request; request.flags |= Text::StateRequest::Flag::LookupSymbol; - auto dragState = _mouseActionItem->getState(_dragStartPosition, request); + auto dragState = _mouseActionItem->data()->getState(_dragStartPosition, request); if (dragState.cursor == HistoryInTextCursorState) { _mouseTextSymbol = dragState.symbol; _mouseSelectType = TextSelectType::Words; @@ -802,7 +826,9 @@ void InnerWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { auto selTo = _selectedText.to; hasSelected = (selTo > selFrom) ? 1 : 0; if (App::mousedItem() && App::mousedItem() == App::hoveredItem()) { - auto mousePos = mapPointToItem(mapFromGlobal(_mousePosition), App::mousedItem()); + auto mousePos = mapPointToItem( + mapFromGlobal(_mousePosition), + viewForItem(App::mousedItem())); HistoryStateRequest request; request.flags |= Text::StateRequest::Flag::LookupSymbol; auto dragState = App::mousedItem()->getState(mousePos, request); @@ -1137,7 +1163,7 @@ void InnerWidget::enterEventHook(QEvent *e) { void InnerWidget::leaveEventHook(QEvent *e) { if (auto item = App::hoveredItem()) { - repaintItem(item); + repaintItem(viewForItem(item)); App::hoveredItem(nullptr); } ClickHandler::clearActive(); @@ -1155,14 +1181,16 @@ void InnerWidget::mouseActionStart(const QPoint &screenPos, Qt::MouseButton butt ClickHandler::pressed(); if (App::pressedItem() != App::hoveredItem()) { - repaintItem(App::pressedItem()); + repaintItem(viewForItem(App::pressedItem())); App::pressedItem(App::hoveredItem()); - repaintItem(App::pressedItem()); + repaintItem(viewForItem(App::pressedItem())); } _mouseAction = MouseAction::None; - _mouseActionItem = App::mousedItem(); - _dragStartPosition = mapPointToItem(mapFromGlobal(screenPos), _mouseActionItem); + _mouseActionItem = viewForItem(App::mousedItem()); + _dragStartPosition = mapPointToItem( + mapFromGlobal(screenPos), + _mouseActionItem); _pressWasInactive = _controller->window()->wasInactivePress(); if (_pressWasInactive) _controller->window()->setInactivePress(false); @@ -1174,7 +1202,7 @@ void InnerWidget::mouseActionStart(const QPoint &screenPos, Qt::MouseButton butt if (_trippleClickTimer.isActive() && (screenPos - _trippleClickPoint).manhattanLength() < QApplication::startDragDistance()) { HistoryStateRequest request; request.flags = Text::StateRequest::Flag::LookupSymbol; - dragState = _mouseActionItem->getState(_dragStartPosition, request); + dragState = _mouseActionItem->data()->getState(_dragStartPosition, request); if (dragState.cursor == HistoryInTextCursorState) { auto selection = TextSelection { dragState.symbol, dragState.symbol }; repaintItem(std::exchange(_selectedItem, _mouseActionItem)); @@ -1188,7 +1216,7 @@ void InnerWidget::mouseActionStart(const QPoint &screenPos, Qt::MouseButton butt } else if (App::pressedItem()) { HistoryStateRequest request; request.flags = Text::StateRequest::Flag::LookupSymbol; - dragState = _mouseActionItem->getState(_dragStartPosition, request); + dragState = _mouseActionItem->data()->getState(_dragStartPosition, request); } if (_mouseSelectType != TextSelectType::Paragraphs) { if (App::pressedItem()) { @@ -1243,7 +1271,7 @@ void InnerWidget::mouseActionFinish(const QPoint &screenPos, Qt::MouseButton but activated = nullptr; } if (App::pressedItem()) { - repaintItem(App::pressedItem()); + repaintItem(viewForItem(App::pressedItem())); App::pressedItem(nullptr); } @@ -1284,30 +1312,31 @@ void InnerWidget::updateSelected() { 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 this->itemTop(elem) + elem->height() <= top; + return this->itemTop(elem) + elem->data()->height() <= top; }) : end; - auto item = (from != end) ? from->get() : nullptr; + const auto view = (from != end) ? from->get() : nullptr; + const auto item = view ? view->data().get() : nullptr; if (item) { App::mousedItem(item); - itemPoint = mapPointToItem(point, item); + itemPoint = mapPointToItem(point, view); if (item->hasPoint(itemPoint)) { if (App::hoveredItem() != item) { - repaintItem(App::hoveredItem()); + repaintItem(viewForItem(App::hoveredItem())); App::hoveredItem(item); - repaintItem(App::hoveredItem()); + repaintItem(view); } } else if (App::hoveredItem()) { - repaintItem(App::hoveredItem()); + repaintItem(viewForItem(App::hoveredItem())); App::hoveredItem(nullptr); } } HistoryTextState dragState; ClickHandlerHost *lnkhost = nullptr; - auto selectingText = (item == _mouseActionItem && item == App::hoveredItem() && _selectedItem); - if (item) { - if (item != _mouseActionItem || (itemPoint - _dragStartPosition).manhattanLength() >= QApplication::startDragDistance()) { + auto selectingText = (view == _mouseActionItem && item == App::hoveredItem() && _selectedItem); + if (view) { + if (view != _mouseActionItem || (itemPoint - _dragStartPosition).manhattanLength() >= QApplication::startDragDistance()) { if (_mouseAction == MouseAction::PrepareDrag) { _mouseAction = MouseAction::Dragging; InvokeQueued(this, [this] { performDrag(); }); @@ -1369,7 +1398,9 @@ void InnerWidget::updateSelected() { } auto selection = TextSelection { qMin(second, _mouseTextSymbol), qMax(second, _mouseTextSymbol) }; if (_mouseSelectType != TextSelectType::Letters) { - selection = _mouseActionItem->adjustSelection(selection, _mouseSelectType); + selection = _mouseActionItem->data()->adjustSelection( + selection, + _mouseSelectType); } if (_selectedText != selection) { _selectedText = selection; @@ -1394,7 +1425,7 @@ void InnerWidget::updateSelected() { if (auto pressedItem = App::pressedLinkItem()) { if (!pressedItem->detached()) { if (pressedItem->history() == _history) { - auto adjustedPoint = mapPointToItem(point, pressedItem); + auto adjustedPoint = mapPointToItem(point, viewForItem(pressedItem)); pressedItem->updatePressed(adjustedPoint); } } @@ -1502,22 +1533,22 @@ void InnerWidget::performDrag() { //} // TODO } -int InnerWidget::itemTop(not_null item) const { +int InnerWidget::itemTop(not_null item) const { return _itemsTop + item->y(); } -void InnerWidget::repaintItem(const HistoryItem *item) { +void InnerWidget::repaintItem(const Message *item) { if (!item) { return; } - update(0, itemTop(item), width(), item->height()); + update(0, itemTop(item), width(), item->data()->height()); } -QPoint InnerWidget::mapPointToItem(QPoint point, const HistoryItem *item) const { - if (!item) { +QPoint InnerWidget::mapPointToItem(QPoint point, const Message *view) const { + if (!view) { return QPoint(); } - return point - QPoint(0, itemTop(item)); + return point - QPoint(0, itemTop(view)); } void InnerWidget::handlePendingHistoryResize() { diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h index a58ca95bf..58cbe930b 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h @@ -22,6 +22,10 @@ namespace Window { class Controller; } // namespace Window +namespace HistoryView { +class Message; +} // namespace HistoryView + namespace AdminLog { class SectionMemento; @@ -86,6 +90,7 @@ protected: int resizeGetHeight(int newWidth) override; private: + using Message = HistoryView::Message; enum class Direction { Up, Down, @@ -107,9 +112,9 @@ private: void mouseActionCancel(); void updateSelected(); void performDrag(); - int itemTop(not_null item) const; - void repaintItem(const HistoryItem *item); - QPoint mapPointToItem(QPoint point, const HistoryItem *item) const; + int itemTop(not_null item) const; + void repaintItem(const Message *item); + QPoint mapPointToItem(QPoint point, const Message *item) const; void handlePendingHistoryResize(); void showContextMenu(QContextMenuEvent *e, bool showFromTouch = false); @@ -141,6 +146,7 @@ private: void clearAfterFilterChange(); void clearAndRequestLog(); void addEvents(Direction direction, const QVector &events); + Message *viewForItem(const HistoryItem *item); void toggleScrollDateShown(); void repaintScrollDateCallback(); @@ -152,7 +158,7 @@ private: // 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 + // Method has "bool (*Method)(Message *item, int itemtop, int itembottom)" signature // if it returns false the enumeration stops immidiately. template void enumerateItems(Method method); @@ -176,8 +182,9 @@ private: not_null _controller; not_null _channel; not_null _history; - std::vector _items; - std::map _itemsByIds; + std::vector _items; + std::map> _itemsByIds; + std::map, not_null, std::less<>> _itemsByData; int _itemsTop = 0; int _itemsHeight = 0; @@ -185,14 +192,14 @@ private: int _minHeight = 0; int _visibleTop = 0; int _visibleBottom = 0; - HistoryItem *_visibleTopItem = nullptr; + Message *_visibleTopItem = nullptr; int _visibleTopFromItem = 0; bool _scrollDateShown = false; Animation _scrollDateOpacity; SingleQueuedInvokation _scrollDateCheck; base::Timer _scrollDateHideTimer; - HistoryItem *_scrollDateLastItem = nullptr; + Message *_scrollDateLastItem = nullptr; int _scrollDateLastItemTop = 0; // Up - max, Down - min. @@ -211,12 +218,12 @@ private: TextSelectType _mouseSelectType = TextSelectType::Letters; QPoint _dragStartPosition; QPoint _mousePosition; - HistoryItem *_mouseActionItem = nullptr; + Message *_mouseActionItem = nullptr; HistoryCursorState _mouseCursorState = HistoryDefaultCursorState; uint16 _mouseTextSymbol = 0; bool _pressWasInactive = false; - HistoryItem *_selectedItem = nullptr; + Message *_selectedItem = nullptr; TextSelection _selectedText; bool _wasSelectedText = false; // was some text selected in current drag action Qt::CursorShape _cursor = style::cur_default; diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp index 0b3c3efb5..f3701f0da 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.cpp @@ -7,9 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "history/admin_log/history_admin_log_item.h" +#include "history/admin_log/history_admin_log_inner.h" +#include "history/view/history_view_message.h" #include "history/history_service.h" #include "history/history_message.h" -#include "history/admin_log/history_admin_log_inner.h" #include "lang/lang_keys.h" #include "boxes/sticker_set_box.h" #include "core/tl_help.h" @@ -281,7 +282,32 @@ TextWithEntities GenerateParticipantChangeText(not_null channel, c } // namespace -void GenerateItems(not_null history, LocalIdManager &idManager, const MTPDchannelAdminLogEvent &event, base::lambda callback) { +OwnedItem::OwnedItem(not_null data) +: _data(data) +, _view(std::make_unique( + data, + HistoryView::Context::AdminLog)) { +} + +OwnedItem::OwnedItem(OwnedItem &&other) +: _data(base::take(other._data)) +, _view(base::take(other._view)) { +} + +OwnedItem &OwnedItem::operator=(OwnedItem &&other) { + _data = base::take(other._data); + _view = base::take(other._view); + return *this; +} + +OwnedItem::~OwnedItem() { + _view = nullptr; + if (_data) { + _data->destroy(); + } +} + +void GenerateItems(not_null history, LocalIdManager &idManager, const MTPDchannelAdminLogEvent &event, base::lambda callback) { Expects(history->peer->isChannel()); auto id = event.vid.v; @@ -290,7 +316,7 @@ void GenerateItems(not_null history, LocalIdManager &idManager, const auto &action = event.vaction; auto date = event.vdate; auto addPart = [&callback](not_null item) { - return callback(HistoryItemOwned(item)); + return callback(OwnedItem(item)); }; using Flag = MTPDmessage::Flag; diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.h b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.h index 5bd44dac5..6531250c4 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_item.h +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_item.h @@ -9,42 +9,34 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace AdminLog { -class HistoryItemOwned; +class OwnedItem; class LocalIdManager; -void GenerateItems(not_null history, LocalIdManager &idManager, const MTPDchannelAdminLogEvent &event, base::lambda callback); +void GenerateItems(not_null history, LocalIdManager &idManager, const MTPDchannelAdminLogEvent &event, base::lambda callback); // Smart pointer wrapper for HistoryItem* that destroys the owned item. -class HistoryItemOwned { +class OwnedItem { public: - explicit HistoryItemOwned(not_null data) : _data(data) { - } - HistoryItemOwned(const HistoryItemOwned &other) = delete; - HistoryItemOwned &operator=(const HistoryItemOwned &other) = delete; - HistoryItemOwned(HistoryItemOwned &&other) : _data(base::take(other._data)) { - } - HistoryItemOwned &operator=(HistoryItemOwned &&other) { - _data = base::take(other._data); - return *this; - } - ~HistoryItemOwned() { - if (_data) { - _data->destroy(); - } - } + explicit OwnedItem(not_null data); + OwnedItem(const OwnedItem &other) = delete; + OwnedItem &operator=(const OwnedItem &other) = delete; + OwnedItem(OwnedItem &&other); + OwnedItem &operator=(OwnedItem &&other); + ~OwnedItem(); - HistoryItem *get() const { - return _data; + HistoryView::Message *get() const { + return _view.get(); } - HistoryItem *operator->() const { + HistoryView::Message *operator->() const { return get(); } - operator HistoryItem*() const { + operator HistoryView::Message*() const { return get(); } private: HistoryItem *_data = nullptr; + std::unique_ptr _view; }; diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_section.h b/Telegram/SourceFiles/history/admin_log/history_admin_log_section.h index 902f4c92e..31c493f19 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_section.h +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_section.h @@ -123,6 +123,8 @@ private: class SectionMemento : public Window::SectionMemento { public: + using Message = HistoryView::Message; + SectionMemento(not_null channel) : _channel(channel) { } @@ -155,7 +157,11 @@ public: return std::move(_adminsCanEdit); } - void setItems(std::vector &&items, std::map &&itemsByIds, bool upLoaded, bool downLoaded) { + void setItems( + std::vector &&items, + std::map> &&itemsByIds, + bool upLoaded, + bool downLoaded) { _items = std::move(items); _itemsByIds = std::move(itemsByIds); _upLoaded = upLoaded; @@ -170,10 +176,10 @@ public: void setIdManager(LocalIdManager &&manager) { _idManager = std::move(manager); } - std::vector takeItems() { + std::vector takeItems() { return std::move(_items); } - std::map takeItemsByIds() { + std::map> takeItemsByIds() { return std::move(_itemsByIds); } LocalIdManager takeIdManager() { @@ -197,8 +203,8 @@ private: int _scrollTop = 0; std::vector> _admins; std::vector> _adminsCanEdit; - std::vector _items; - std::map _itemsByIds; + std::vector _items; + std::map> _itemsByIds; bool _upLoaded = false; bool _downLoaded = true; LocalIdManager _idManager; diff --git a/Telegram/SourceFiles/history/feed/history_feed_section.cpp b/Telegram/SourceFiles/history/feed/history_feed_section.cpp index 533f1b892..c1989bb23 100644 --- a/Telegram/SourceFiles/history/feed/history_feed_section.cpp +++ b/Telegram/SourceFiles/history/feed/history_feed_section.cpp @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_top_bar_widget.h" #include "history/view/history_view_list_widget.h" +#include "history/view/history_view_message.h" #include "lang/lang_keys.h" #include "ui/widgets/buttons.h" #include "ui/widgets/shadow.h" @@ -135,6 +136,10 @@ bool Widget::cmd_search() { return true; } +HistoryView::Context Widget::listContext() { + return HistoryView::Context::Feed; +} + void Widget::listScrollTo(int top) { if (_scroll->scrollTop() != top) { _scroll->scrollToY(top); diff --git a/Telegram/SourceFiles/history/feed/history_feed_section.h b/Telegram/SourceFiles/history/feed/history_feed_section.h index dcbbea6d9..c4964b304 100644 --- a/Telegram/SourceFiles/history/feed/history_feed_section.h +++ b/Telegram/SourceFiles/history/feed/history_feed_section.h @@ -59,6 +59,7 @@ public: bool cmd_search() override; // HistoryView::ListDelegate interface. + HistoryView::Context listContext() override; void listScrollTo(int top) override; void listCloseRequest() override; rpl::producer listSource( diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index fa08c60a6..2a33b0b6a 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "history/history.h" +#include "history/view/history_view_message.h" #include "history/history_message.h" #include "history/history_media_types.h" #include "history/history_service.h" @@ -141,7 +142,11 @@ void History::setCloudDraft(std::unique_ptr &&draft) { Data::Draft *History::createCloudDraft(Data::Draft *fromDraft) { if (Data::draftIsNull(fromDraft)) { - setCloudDraft(std::make_unique(TextWithTags(), 0, MessageCursor(), false)); + setCloudDraft(std::make_unique( + TextWithTags(), + 0, + MessageCursor(), + false)); cloudDraft()->date = QDateTime(); } else { auto existing = cloudDraft(); @@ -369,11 +374,11 @@ void ChannelHistory::getRangeDifference() { auto fromId = MsgId(0); auto toId = MsgId(0); for (auto blockIndex = 0, blocksCount = int(blocks.size()); blockIndex < blocksCount; ++blockIndex) { - auto block = blocks[blockIndex]; - for (auto itemIndex = 0, itemsCount = int(block->items.size()); itemIndex < itemsCount; ++itemIndex) { - auto item = block->items[itemIndex]; - if (item->id > 0) { - fromId = item->id; + const auto &block = blocks[blockIndex]; + for (auto itemIndex = 0, itemsCount = int(block->messages.size()); itemIndex < itemsCount; ++itemIndex) { + const auto &message = block->messages[itemIndex]; + if (message->id() > 0) { + fromId = message->id(); break; } } @@ -382,11 +387,11 @@ void ChannelHistory::getRangeDifference() { if (!fromId) return; for (auto blockIndex = blocks.size(); blockIndex > 0;) { - auto block = blocks[--blockIndex]; - for (auto itemIndex = block->items.size(); itemIndex > 0;) { - auto item = block->items[--itemIndex]; - if (item->id > 0) { - toId = item->id; + const auto &block = blocks[--blockIndex]; + for (auto itemIndex = block->messages.size(); itemIndex > 0;) { + const auto &message = block->messages[--itemIndex]; + if (message->id() > 0) { + toId = message->id(); break; } } @@ -438,9 +443,9 @@ HistoryJoined *ChannelHistory::insertJoinedMessage(bool unread) { } for (auto blockIndex = blocks.size(); blockIndex > 0;) { - auto block = blocks[--blockIndex]; - for (auto itemIndex = block->items.size(); itemIndex > 0;) { - auto item = block->items[--itemIndex]; + const auto &block = blocks[--blockIndex]; + for (auto itemIndex = block->messages.size(); itemIndex > 0;) { + const auto item = block->messages[--itemIndex]->data(); // Due to a server bug sometimes inviteDate is less (before) than the // first message in the megagroup (message about migration), let us @@ -497,8 +502,8 @@ void ChannelHistory::checkJoinedMessage(bool createUnread) { QDateTime inviteDate = peer->asChannel()->inviteDate; QDateTime firstDate, lastDate; if (!blocks.empty()) { - firstDate = blocks.front()->items.front()->date; - lastDate = blocks.back()->items.back()->date; + firstDate = blocks.front()->messages.front()->data()->date; + lastDate = blocks.back()->messages.back()->data()->date; } if (!firstDate.isNull() && !lastDate.isNull() && (firstDate <= inviteDate || loadedAtTop()) && (lastDate > inviteDate || loadedAtBottom())) { bool willBeLastMsg = (inviteDate >= lastDate); @@ -514,9 +519,9 @@ void ChannelHistory::checkMaxReadMessageDate() { if (_maxReadMessageDate.isValid()) return; for (auto blockIndex = blocks.size(); blockIndex > 0;) { - auto block = blocks[--blockIndex]; - for (auto itemIndex = block->items.size(); itemIndex > 0;) { - auto item = block->items[--itemIndex]; + const auto &block = blocks[--blockIndex]; + for (auto itemIndex = block->messages.size(); itemIndex > 0;) { + const auto item = block->messages[--itemIndex]->data(); if (!item->unread()) { _maxReadMessageDate = item->date; if (item->isGroupMigrate() && isMegagroup() && peer->migrateFrom()) { @@ -1339,29 +1344,26 @@ HistoryBlock *History::prepareBlockForAddingItem() { return _buildingFrontBlock->block; } - auto result = _buildingFrontBlock->block = new HistoryBlock(this); - if (_buildingFrontBlock->expectedItemsCount > 0) { - result->items.reserve(_buildingFrontBlock->expectedItemsCount + 1); - } - result->setIndexInHistory(0); - blocks.push_front(result); - for (int i = 1, l = blocks.size(); i < l; ++i) { + blocks.push_front(std::make_unique(this)); + for (auto i = 0, l = int(blocks.size()); i != l; ++i) { blocks[i]->setIndexInHistory(i); } - return result; + _buildingFrontBlock->block = blocks.front().get(); + if (_buildingFrontBlock->expectedItemsCount > 0) { + _buildingFrontBlock->block->messages.reserve( + _buildingFrontBlock->expectedItemsCount + 1); + } + return _buildingFrontBlock->block; } - auto addNewBlock = blocks.empty() || (blocks.back()->items.size() >= kNewBlockEachMessage); - if (!addNewBlock) { - return blocks.back(); + const auto addNewBlock = blocks.empty() + || (blocks.back()->messages.size() >= kNewBlockEachMessage); + if (addNewBlock) { + blocks.push_back(std::make_unique(this)); + blocks.back()->setIndexInHistory(blocks.size() - 1); + blocks.back()->messages.reserve(kNewBlockEachMessage); } - - auto result = new HistoryBlock(this); - result->setIndexInHistory(blocks.size()); - blocks.push_back(result); - - result->items.reserve(kNewBlockEachMessage); - return result; + return blocks.back().get(); }; void History::addItemToBlock(not_null item) { @@ -1369,8 +1371,10 @@ void History::addItemToBlock(not_null item) { auto block = prepareBlockForAddingItem(); - item->attachToBlock(block, block->items.size()); - block->items.push_back(item); + block->messages.push_back(std::make_unique( + item, + HistoryView::Context::History)); + item->attachToBlock(block, block->messages.size() - 1); item->previousItemChanged(); if (isBuildingFrontBlock() && _buildingFrontBlock->expectedItemsCount > 0) { @@ -1379,62 +1383,11 @@ void History::addItemToBlock(not_null item) { } template -void History::addToSharedMedia(std::vector (&medias)[kSharedMediaTypeCount], bool force) { +void History::addToSharedMedia( + std::vector (&medias)[kSharedMediaTypeCount], + bool force) { auto from = loadedAtTop() ? 0 : minMsgId(); auto till = loadedAtBottom() ? ServerMaxMsgId : maxMsgId(); - if (from > till) { - // History is desync, nothing good can be added. - //// Logging - auto value = QStringList(); - for (auto block : blocks) { - auto indices = QStringList(); - auto &items = block->items; - auto count = int(items.size()); - auto logItem = [&](auto &&item) { - indices.push_back(QString::number(item->id)); - }; - if (count < 4) { - for (auto item : items) { - logItem(item); - } - } else { - auto last = 0; - auto logLast = [&] { - logItem(items[last]); - }; - auto logTill = [&](int till) { - if (last < till - 1) { - indices.push_back("...[" - + QString::number(till - 1 - last) - + "]..."); - } - last = till; - logLast(); - }; - auto badPair = [&](int index) { - auto prev = items[index - 1]->id; - auto next = items[index]->id; - return IsServerMsgId(prev) - && IsServerMsgId(next) - && (next < prev); - }; - - logLast(); - for (auto i = 1; i != count - 1; ++i) { - if (badPair(i) || badPair(i + 1)) { - logTill(i); - } - } - logTill(count - 1); - } - value.push_back(indices.join(",")); - } - CrashReports::SetAnnotation("full", value.join(";")); - Assert(!"History desync caught!"); - //// Logging - - return; - } for (auto i = 0; i != Storage::kSharedMediaTypeCount; ++i) { if (force || !medias[i].empty()) { auto type = static_cast(i); @@ -1507,8 +1460,8 @@ void History::addOlderSlice(const QVector &slice) { // lastParticipants are displayed in Profile as members list. markupSenders = &peer->asChannel()->mgInfo->markupSenders; } - for (auto i = block->items.size(); i > 0; --i) { - auto item = block->items[i - 1]; + for (auto i = block->messages.size(); i > 0; --i) { + const auto item = block->messages[i - 1]->data(); item->addToUnreadMentions(UnreadMentionType::Existing); if (item->from()->id) { if (lastAuthors) { // chats @@ -1678,8 +1631,9 @@ void History::checkAddAllToUnreadMentions() { return; } - for (const auto block : blocks) { - for (const auto item : block->items) { + for (const auto &block : blocks) { + for (const auto &message : block->messages) { + const auto item = message->data(); item->addToUnreadMentions(UnreadMentionType::Existing); } } @@ -1688,13 +1642,14 @@ void History::checkAddAllToUnreadMentions() { void History::addBlockToSharedMedia(HistoryBlock *block) { std::vector medias[Storage::kSharedMediaTypeCount]; if (block) { - for (auto item : block->items) { + for (const auto &message : block->messages) { + const auto item = message->data(); if (auto types = item->sharedMediaTypes()) { for (auto i = 0; i != Storage::kSharedMediaTypeCount; ++i) { auto type = static_cast(i); if (types.test(type)) { if (medias[i].empty()) { - medias[i].reserve(block->items.size()); + medias[i].reserve(block->messages.size()); } medias[i].push_back(item->id); } @@ -1709,11 +1664,13 @@ int History::countUnread(MsgId upTo) { int result = 0; for (auto i = blocks.cend(), e = blocks.cbegin(); i != e;) { --i; - for (auto j = (*i)->items.cend(), en = (*i)->items.cbegin(); j != en;) { + const auto &messages = (*i)->messages; + for (auto j = messages.cend(), en = messages.cbegin(); j != en;) { --j; - if ((*j)->id > 0 && (*j)->id <= upTo) { + const auto item = (*j)->data(); + if (item->id > 0 && item->id <= upTo) { break; - } else if (!(*j)->out() && (*j)->unread() && (*j)->id > upTo) { + } else if (!item->out() && item->unread() && item->id > upTo) { ++result; } } @@ -1726,11 +1683,13 @@ void History::updateShowFrom() { for (auto i = blocks.cend(); i != blocks.cbegin();) { --i; - for (auto j = (*i)->items.cend(); j != (*i)->items.cbegin();) { + const auto &messages = (*i)->messages; + for (auto j = messages.cend(); j != messages.cbegin();) { --j; - if ((*j)->id > 0 && (!(*j)->out() || !showFrom)) { - if ((*j)->id >= inboxReadBefore) { - showFrom = *j; + const auto item = (*j)->data(); + if (item->id > 0 && (!item->out() || !showFrom)) { + if (item->id >= inboxReadBefore) { + showFrom = item; } else { return; } @@ -1779,7 +1738,7 @@ MsgId History::outboxRead(HistoryItem *wasRead) { } HistoryItem *History::lastAvailableMessage() const { - return isEmpty() ? nullptr : blocks.back()->items.back(); + return isEmpty() ? nullptr : blocks.back()->messages.back()->data().get(); } void History::setUnreadCount(int newUnreadCount) { @@ -1839,21 +1798,28 @@ bool History::changeMute(bool newMute) { } void History::getNextShowFrom(HistoryBlock *block, int i) { + const auto setFromMessage = [&](const auto &message) { + const auto item = message->data(); + if (item->id > 0) { + showFrom = item; + return true; + } + return false; + }; if (i >= 0) { - auto l = block->items.size(); + auto l = block->messages.size(); for (++i; i < l; ++i) { - if (block->items[i]->id > 0) { - showFrom = block->items[i]; + const auto &message = block->messages[i]; + if (setFromMessage(block->messages[i])) { return; } } } for (auto j = block->indexInHistory() + 1, s = int(blocks.size()); j < s; ++j) { - block = blocks[j]; - for_const (auto item, block->items) { - if (item->id > 0) { - showFrom = item; + block = blocks[j].get(); + for (const auto &message : block->messages) { + if (setFromMessage(message)) { return; } } @@ -1874,7 +1840,7 @@ QDateTime History::adjustChatListDate() const { void History::countScrollState(int top) { countScrollTopItem(top); if (scrollTopItem) { - scrollTopOffset = (top - scrollTopItem->block()->y() - scrollTopItem->y()); + scrollTopOffset = (top - scrollTopItem->data()->block()->y() - scrollTopItem->y()); } } @@ -1884,64 +1850,65 @@ void History::countScrollTopItem(int top) { return; } - int itemIndex = 0, blockIndex = 0, itemTop = 0; - if (scrollTopItem && !scrollTopItem->detached()) { - itemIndex = scrollTopItem->indexInBlock(); - blockIndex = scrollTopItem->block()->indexInHistory(); + auto itemIndex = 0; + auto blockIndex = 0; + auto itemTop = 0; + if (scrollTopItem) { + itemIndex = scrollTopItem->data()->indexInBlock(); + blockIndex = scrollTopItem->data()->block()->indexInHistory(); itemTop = blocks[blockIndex]->y() + scrollTopItem->y(); } if (itemTop > top) { // go backward through history while we don't find an item that starts above do { - auto block = blocks[blockIndex]; + const auto &block = blocks[blockIndex]; for (--itemIndex; itemIndex >= 0; --itemIndex) { - auto item = block->items[itemIndex]; - itemTop = block->y() + item->y(); + const auto view = block->messages[itemIndex].get(); + itemTop = block->y() + view->y(); if (itemTop <= top) { - scrollTopItem = item; + scrollTopItem = view; return; } } if (--blockIndex >= 0) { - itemIndex = blocks[blockIndex]->items.size(); + itemIndex = blocks[blockIndex]->messages.size(); } else { break; } } while (true); - scrollTopItem = blocks.front()->items.front(); + scrollTopItem = blocks.front()->messages.front().get(); } else { // go forward through history while we don't find the last item that starts above - for (int blocksCount = blocks.size(); blockIndex < blocksCount; ++blockIndex) { - auto block = blocks[blockIndex]; - for (int itemsCount = block->items.size(); itemIndex < itemsCount; ++itemIndex) { - auto item = block->items[itemIndex]; - itemTop = block->y() + item->y(); + for (auto blocksCount = int(blocks.size()); blockIndex < blocksCount; ++blockIndex) { + const auto &block = blocks[blockIndex]; + for (auto itemsCount = int(block->messages.size()); itemIndex < itemsCount; ++itemIndex) { + itemTop = block->y() + block->messages[itemIndex]->y(); if (itemTop > top) { Assert(itemIndex > 0 || blockIndex > 0); if (itemIndex > 0) { - scrollTopItem = block->items[itemIndex - 1]; + scrollTopItem = block->messages[itemIndex - 1].get(); } else { - scrollTopItem = blocks[blockIndex - 1]->items.back(); + scrollTopItem = blocks[blockIndex - 1]->messages.back().get(); } return; } } itemIndex = 0; } - scrollTopItem = blocks.back()->items.back(); + scrollTopItem = blocks.back()->messages.back().get(); } } void History::getNextScrollTopItem(HistoryBlock *block, int32 i) { ++i; - if (i > 0 && i < block->items.size()) { - scrollTopItem = block->items[i]; + if (i > 0 && i < block->messages.size()) { + scrollTopItem = block->messages[i].get(); return; } int j = block->indexInHistory() + 1; if (j > 0 && j < blocks.size()) { - scrollTopItem = blocks[j]->items.front(); + scrollTopItem = blocks[j]->messages.front().get(); return; } scrollTopItem = nullptr; @@ -1966,24 +1933,29 @@ void History::destroyUnreadBar() { } } -not_null History::addNewInTheMiddle(not_null newItem, int32 blockIndex, int32 itemIndex) { +not_null History::addNewInTheMiddle( + not_null newItem, + int blockIndex, + int itemIndex) { Expects(blockIndex >= 0); Expects(blockIndex < blocks.size()); Expects(itemIndex >= 0); - Expects(itemIndex <= blocks[blockIndex]->items.size()); + Expects(itemIndex <= blocks[blockIndex]->messages.size()); - auto block = blocks[blockIndex]; + const auto &block = blocks[blockIndex]; - newItem->attachToBlock(block, itemIndex); - block->items.insert(block->items.begin() + itemIndex, newItem); + block->messages.insert( + block->messages.begin() + itemIndex, + std::make_unique(newItem, HistoryView::Context::History)); + newItem->attachToBlock(block.get(), itemIndex); newItem->previousItemChanged(); - if (itemIndex + 1 < block->items.size()) { - for (int i = itemIndex + 1, l = block->items.size(); i < l; ++i) { - block->items[i]->setIndexInBlock(i); + if (itemIndex + 1 < block->messages.size()) { + for (auto i = itemIndex + 1, l = int(block->messages.size()); i != l; ++i) { + block->messages[i]->data()->setIndexInBlock(i); } - block->items[itemIndex + 1]->previousItemChanged(); - } else if (blockIndex + 1 < blocks.size() && !blocks[blockIndex + 1]->items.empty()) { - blocks[blockIndex + 1]->items.front()->previousItemChanged(); + block->messages[itemIndex + 1]->data()->previousItemChanged(); + } else if (blockIndex + 1 < blocks.size() && !blocks[blockIndex + 1]->messages.empty()) { + blocks[blockIndex + 1]->messages.front()->data()->previousItemChanged(); } else { newItem->nextItemChanged(); } @@ -2001,10 +1973,10 @@ HistoryItem *History::findNextItem(not_null item) const { const auto nextBlockIndex = item->block()->indexInHistory() + 1; const auto nextItemIndex = item->indexInBlock() + 1; - if (nextItemIndex < int(item->block()->items.size())) { - return item->block()->items[nextItemIndex]; + if (nextItemIndex < int(item->block()->messages.size())) { + return item->block()->messages[nextItemIndex]->data(); } else if (nextBlockIndex < int(blocks.size())) { - return blocks[nextBlockIndex]->items.front(); + return blocks[nextBlockIndex]->messages.front()->data(); } return nullptr; } @@ -2015,9 +1987,9 @@ HistoryItem *History::findPreviousItem(not_null item) const { const auto blockIndex = item->block()->indexInHistory(); const auto itemIndex = item->indexInBlock(); if (itemIndex > 0) { - return item->block()->items[itemIndex - 1]; + return item->block()->messages[itemIndex - 1]->data(); } else if (blockIndex > 0) { - return blocks[blockIndex - 1]->items.back(); + return blocks[blockIndex - 1]->messages.back()->data(); } return nullptr; } @@ -2162,14 +2134,14 @@ HistoryBlock *History::finishBuildingFrontBlock() { 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[1]->items.front(); + const auto last = block->messages.back()->data(); // ... item, item, item, last ], [ first, item, item ... + const auto first = blocks[1]->messages.front()->data(); // 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(); + block->messages.back()->data()->nextItemChanged(); } } @@ -2288,8 +2260,9 @@ void History::fixLastMessage(bool wasAtBottom) { } MsgId History::minMsgId() const { - for (auto block : std::as_const(blocks)) { - for (auto item : std::as_const(block->items)) { + for (const auto &block : blocks) { + for (const auto &message : block->messages) { + const auto item = message->data(); if (IsServerMsgId(item->id)) { return item->id; } @@ -2299,8 +2272,9 @@ MsgId History::minMsgId() const { } MsgId History::maxMsgId() const { - for (auto block : base::reversed(std::as_const(blocks))) { - for (auto item : base::reversed(std::as_const(block->items))) { + for (const auto &block : base::reversed(blocks)) { + for (const auto &message : base::reversed(block->messages)) { + const auto item = message->data(); if (IsServerMsgId(item->id)) { return item->id; } @@ -2325,7 +2299,7 @@ int History::resizeGetHeight(int newWidth) { width = newWidth; int y = 0; - for_const (auto block, blocks) { + for (const auto &block : blocks) { block->setY(y); y += block->resizeGetHeight(newWidth, resizeAllItems); } @@ -2357,7 +2331,9 @@ History *History::migrateFrom() const { } bool History::isDisplayedEmpty() const { - return isEmpty() || ((blocks.size() == 1) && blocks.front()->items.size() == 1 && blocks.front()->items.front()->isEmpty()); + return isEmpty() || ((blocks.size() == 1) + && blocks.front()->messages.size() == 1 + && blocks.front()->messages.front()->data()->isEmpty()); } bool History::hasOrphanMediaGroupPart() const { @@ -2365,10 +2341,10 @@ bool History::hasOrphanMediaGroupPart() const { return false; } else if (blocks.size() != 1) { return false; - } else if (blocks.front()->items.size() != 1) { + } else if (blocks.front()->messages.size() != 1) { return false; } - return blocks.front()->items.front()->groupId() != MessageGroupId(); + return blocks.front()->messages.front()->data()->groupId() != MessageGroupId(); } bool History::removeOrphanMediaGroupPart() { @@ -2444,8 +2420,8 @@ void History::clearUpTill(MsgId availableMinId) { return; } do { - auto item = blocks.front()->items.front(); - auto itemId = item->id; + const auto item = blocks.front()->messages.front()->data(); + const auto itemId = item->id; if (IsServerMsgId(itemId) && itemId >= availableMinId) { if (itemId == availableMinId) { auto fromId = 0; @@ -2472,21 +2448,19 @@ void History::clearUpTill(MsgId availableMinId) { void History::applyGroupAdminChanges( const base::flat_map &changes) { - for (auto block : blocks) { - for (auto item : block->items) { - item->applyGroupAdminChanges(changes); + for (const auto &block : blocks) { + for (const auto &message : block->messages) { + message->data()->applyGroupAdminChanges(changes); } } } void History::clearBlocks(bool leaveItems) { - Blocks lst; - std::swap(lst, blocks); - for_const (HistoryBlock *block, lst) { + const auto cleared = base::take(blocks); + for (const auto &block : cleared) { if (leaveItems) { block->clear(true); } - delete block; } } @@ -2518,7 +2492,7 @@ void History::changeMsgId(MsgId oldId, MsgId newId) { } void History::removeBlock(not_null block) { - Expects(block->items.empty()); + Expects(block->messages.empty()); if (_buildingFrontBlock && block == _buildingFrontBlock->block) { _buildingFrontBlock->block = nullptr; @@ -2530,9 +2504,9 @@ void History::removeBlock(not_null block) { for (int i = index, l = blocks.size(); i < l; ++i) { blocks[i]->setIndexInHistory(i); } - blocks[index]->items.front()->previousItemChanged(); - } else if (!blocks.empty() && !blocks.back()->items.empty()) { - blocks.back()->items.back()->nextItemChanged(); + blocks[index]->messages.front()->data()->previousItemChanged(); + } else if (!blocks.empty() && !blocks.back()->messages.empty()) { + blocks.back()->messages.back()->data()->nextItemChanged(); } } @@ -2540,10 +2514,16 @@ History::~History() { clearOnDestroy(); } +HistoryBlock::HistoryBlock(not_null history) +: _history(history) { +} + int HistoryBlock::resizeGetHeight(int newWidth, bool resizeAllItems) { auto y = 0; - for_const (auto item, items) { - item->setY(y); + for (const auto &message : messages) { + message->setY(y); + + const auto item = message->data(); if (resizeAllItems || item->pendingResize()) { y += item->resizeGetHeight(newWidth); } else { @@ -2555,17 +2535,14 @@ int HistoryBlock::resizeGetHeight(int newWidth, bool resizeAllItems) { } void HistoryBlock::clear(bool leaveItems) { - auto itemsList = base::take(items); + const auto list = base::take(messages); if (leaveItems) { - for_const (auto item, itemsList) { - item->detachFast(); - } - } else { - for_const (auto item, itemsList) { - delete item; + for (const auto &message : list) { + message->data()->detachFast(); } } + // #TODO feeds delete all items in history } void HistoryBlock::removeItem(not_null item) { @@ -2594,30 +2571,31 @@ void HistoryBlock::removeItem(not_null item) { if (_history->unreadBar == item) { _history->unreadBar = nullptr; } - if (_history->scrollTopItem == item) { + if (_history->scrollTopItem && _history->scrollTopItem->data() == item) { _history->getNextScrollTopItem(this, itemIndex); } item->detachFast(); - items.erase(items.begin() + itemIndex); - for (auto i = itemIndex, l = int(items.size()); i < l; ++i) { - items[i]->setIndexInBlock(i); + messages.erase(messages.begin() + itemIndex); + for (auto i = itemIndex, l = int(messages.size()); i < l; ++i) { + messages[i]->data()->setIndexInBlock(i); } - if (items.empty()) { + if (messages.empty()) { + // Deletes this. _history->removeBlock(this); - } else if (itemIndex < items.size()) { - items[itemIndex]->previousItemChanged(); + } else if (itemIndex < messages.size()) { + messages[itemIndex]->data()->previousItemChanged(); } else if (blockIndex + 1 < _history->blocks.size()) { - _history->blocks[blockIndex + 1]->items.front()->previousItemChanged(); - } else if (!_history->blocks.empty() && !_history->blocks.back()->items.empty()) { - _history->blocks.back()->items.back()->nextItemChanged(); - } - - if (items.empty()) { - delete this; + _history->blocks[blockIndex + 1]->messages.front()->data()->previousItemChanged(); + } else if (!_history->blocks.empty() && !_history->blocks.back()->messages.empty()) { + _history->blocks.back()->messages.back()->data()->nextItemChanged(); } if (needGroupRecount) { groupHistory->recountGrouping(groupFrom, groupTill); } } + +HistoryBlock::~HistoryBlock() { + clear(); +} diff --git a/Telegram/SourceFiles/history/history.h b/Telegram/SourceFiles/history/history.h index c6b03dd41..b231a74f3 100644 --- a/Telegram/SourceFiles/history/history.h +++ b/Telegram/SourceFiles/history/history.h @@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/flat_set.h" #include "base/flags.h" +class History; class HistoryItem; using HistoryItemsList = std::vector>; @@ -26,7 +27,10 @@ enum NewMessageType { NewMessageExisting, }; -class History; +namespace HistoryView { +class Message; +} // namespace HistoryView + class Histories { public: using Map = QHash; @@ -320,8 +324,7 @@ public: void eraseFromUnreadMentions(MsgId msgId); void addUnreadMentionsSlice(const MTPmessages_Messages &result); - using Blocks = std::deque; - Blocks blocks; + std::deque> blocks; int width = 0; int height = 0; @@ -394,7 +397,7 @@ public: // we save a pointer of the history item at the top of the displayed window // together with an offset from the window top to the top of this message // resulting scrollTop = top(scrollTopItem) + scrollTopOffset - HistoryItem *scrollTopItem = nullptr; + HistoryView::Message *scrollTopItem = nullptr; int scrollTopOffset = 0; void forgetScrollState() { scrollTopItem = nullptr; @@ -446,7 +449,10 @@ protected: not_null createItemGame(MsgId id, MTPDmessage::Flags flags, UserId viaBotId, MsgId replyTo, QDateTime date, UserId from, const QString &postAuthor, GameData *game, const MTPReplyMarkup &markup); not_null addNewItem(not_null adding, bool newMsg); - not_null addNewInTheMiddle(not_null newItem, int32 blockIndex, int32 itemIndex); + not_null addNewInTheMiddle( + not_null newItem, + int blockIndex, + int itemIndex); // All this methods add a new item to the first or last block // depending on if we are in isBuildingFronBlock() state. @@ -573,18 +579,14 @@ private: class HistoryBlock { public: - HistoryBlock(not_null history) : _history(history) { - } - + HistoryBlock(not_null history); HistoryBlock(const HistoryBlock &) = delete; HistoryBlock &operator=(const HistoryBlock &) = delete; + ~HistoryBlock(); - std::vector items; + std::vector> messages; void clear(bool leaveItems = false); - ~HistoryBlock() { - clear(); - } void removeItem(not_null item); int resizeGetHeight(int newWidth, bool resizeAllItems); @@ -604,12 +606,16 @@ public: HistoryBlock *previousBlock() const { Expects(_indexInHistory >= 0); - return (_indexInHistory > 0) ? _history->blocks.at(_indexInHistory - 1) : nullptr; + return (_indexInHistory > 0) + ? _history->blocks[_indexInHistory - 1].get() + : nullptr; } HistoryBlock *nextBlock() const { Expects(_indexInHistory >= 0); - return (_indexInHistory + 1 < _history->blocks.size()) ? _history->blocks.at(_indexInHistory + 1) : nullptr; + return (_indexInHistory + 1 < _history->blocks.size()) + ? _history->blocks[_indexInHistory + 1].get() + : nullptr; } void setIndexInHistory(int index) { _indexInHistory = index; @@ -617,7 +623,7 @@ public: int indexInHistory() const { Expects(_indexInHistory >= 0); Expects(_indexInHistory < _history->blocks.size()); - Expects(_history->blocks[_indexInHistory] == this); + Expects(_history->blocks[_indexInHistory].get() == this); return _indexInHistory; } diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index b2ca2e57e..dfbca504c 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_media_types.h" #include "history/history_item_components.h" #include "history/view/history_view_service_message.h" +#include "history/view/history_view_message.h" #include "ui/text_options.h" #include "ui/widgets/popup_menu.h" #include "window/window_controller.h" @@ -210,16 +211,16 @@ void HistoryInner::enumerateItemsInHistory(History *history, int historytop, Met auto blockIndex = BinarySearchBlocksOrItems(history->blocks, searchEdge - historytop); // Binary search for itemIndex of the first item that is not completely below the visible area. - auto block = history->blocks.at(blockIndex); + auto block = history->blocks[blockIndex].get(); auto blocktop = historytop + block->y(); auto blockbottom = blocktop + block->height(); - auto itemIndex = BinarySearchBlocksOrItems(block->items, searchEdge - blocktop); + auto itemIndex = BinarySearchBlocksOrItems(block->messages, searchEdge - blocktop); while (true) { while (true) { - auto item = block->items.at(itemIndex); - auto itemtop = blocktop + item->y(); - auto itembottom = itemtop + item->height(); + auto view = block->messages[itemIndex].get(); + auto itemtop = blocktop + view->y(); + auto itembottom = itemtop + view->data()->height(); // Binary search should've skipped all the items that are above / below the visible area. if (TopToBottom) { @@ -228,7 +229,7 @@ void HistoryInner::enumerateItemsInHistory(History *history, int historytop, Met Assert(itemtop < _visibleAreaBottom); } - if (!method(item, itemtop, itembottom)) { + if (!method(view, itemtop, itembottom)) { return; } @@ -244,7 +245,7 @@ void HistoryInner::enumerateItemsInHistory(History *history, int historytop, Met } if (TopToBottom) { - if (++itemIndex >= block->items.size()) { + if (++itemIndex >= block->messages.size()) { break; } } else { @@ -274,13 +275,13 @@ void HistoryInner::enumerateItemsInHistory(History *history, int historytop, Met return; } } - block = history->blocks[blockIndex]; + block = history->blocks[blockIndex].get(); blocktop = historytop + block->y(); blockbottom = blocktop + block->height(); if (TopToBottom) { itemIndex = 0; } else { - itemIndex = block->items.size() - 1; + itemIndex = block->messages.size() - 1; } } } @@ -295,9 +296,10 @@ void HistoryInner::enumerateUserpics(Method method) { // -1 means we didn't find an attached to next message yet. int lowestAttachedItemTop = -1; - auto userpicCallback = [this, &lowestAttachedItemTop, &method](not_null item, int itemtop, int itembottom) { + auto userpicCallback = [&](not_null view, int itemtop, int itembottom) { // Skip all service messages. - auto message = item->toHistoryMessage(); + const auto item = view->data(); + const auto message = item->toHistoryMessage(); if (!message) return true; if (lowestAttachedItemTop < 0 && message->isAttachedToNext()) { @@ -343,7 +345,8 @@ void HistoryInner::enumerateDates(Method method) { // -1 means we didn't find a same-day with previous message yet. auto lowestInOneDayItemBottom = -1; - auto dateCallback = [this, &lowestInOneDayItemBottom, &method, drawtop](not_null item, int itemtop, int itembottom) { + auto dateCallback = [&](not_null view, int itemtop, int itembottom) { + const auto item = view->data(); if (lowestInOneDayItemBottom < 0 && item->isInOneDayWithPrevious()) { lowestInOneDayItemBottom = itembottom - item->marginBottom(); } @@ -356,8 +359,8 @@ void HistoryInner::enumerateDates(Method method) { if (itemtop > _visibleAreaTop) { // 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()) { + } else if (item == _history->blocks.front()->messages.front()->data() && item->isGroupMigrate() + && _migrated->blocks.back()->messages.back()->data()->isGroupMigrate()) { // This item is completely invisible and should be completely ignored. return false; } @@ -430,12 +433,11 @@ TextSelection HistoryInner::computeRenderSelection( } TextSelection HistoryInner::itemRenderSelection( - not_null item, + not_null view, int selfromy, int seltoy) const { - Expects(!item->detached()); - - const auto y = item->block()->y() + item->y(); + const auto item = view->data(); + const auto y = item->block()->y() + view->y(); if (y >= selfromy && y < seltoy) { if (_dragSelecting && !item->serviceMsg() && item->id > 0) { return FullSelection; @@ -497,16 +499,17 @@ void HistoryInner::paintEvent(QPaintEvent *e) { auto hdrawtop = historyDrawTop(); if (mtop >= 0) { auto iBlock = (_curHistory == _migrated ? _curBlock : (_migrated->blocks.size() - 1)); - auto block = _migrated->blocks[iBlock]; - auto iItem = (_curHistory == _migrated ? _curItem : (block->items.size() - 1)); - auto item = block->items[iItem]; + auto block = _migrated->blocks[iBlock].get(); + auto iItem = (_curHistory == _migrated ? _curItem : (block->messages.size() - 1)); + auto view = block->messages[iItem].get(); + auto item = view->data(); - auto y = mtop + block->y() + item->y(); + auto y = mtop + block->y() + view->y(); p.save(); p.translate(0, y); if (clip.y() < y + item->height()) while (y < drawToY) { const auto selection = itemRenderSelection( - item, + view, selfromy - mtop, seltoy - mtop); item->draw(p, clip.translated(0, -y), selection, ms); @@ -524,33 +527,35 @@ void HistoryInner::paintEvent(QPaintEvent *e) { y += h; ++iItem; - if (iItem == block->items.size()) { + if (iItem == block->messages.size()) { iItem = 0; ++iBlock; if (iBlock == _migrated->blocks.size()) { break; } - block = _migrated->blocks[iBlock]; + block = _migrated->blocks[iBlock].get(); } - item = block->items[iItem]; + view = block->messages[iItem].get(); + item = view->data(); } p.restore(); } if (htop >= 0) { auto iBlock = (_curHistory == _history ? _curBlock : 0); - auto block = _history->blocks[iBlock]; + auto block = _history->blocks[iBlock].get(); auto iItem = (_curHistory == _history ? _curItem : 0); - auto item = block->items[iItem]; + auto view = block->messages[iItem].get(); + auto item = view->data(); auto hclip = clip.intersected(QRect(0, hdrawtop, width(), clip.top() + clip.height())); - auto y = htop + block->y() + item->y(); + auto y = htop + block->y() + view->y(); p.save(); p.translate(0, y); while (y < drawToY) { auto h = item->height(); if (hclip.y() < y + h && hdrawtop < y + h) { const auto selection = itemRenderSelection( - item, + view, selfromy - htop, seltoy - htop); item->draw(p, hclip.translated(0, -y), selection, ms); @@ -567,15 +572,16 @@ void HistoryInner::paintEvent(QPaintEvent *e) { y += h; ++iItem; - if (iItem == block->items.size()) { + if (iItem == block->messages.size()) { iItem = 0; ++iBlock; if (iBlock == _history->blocks.size()) { break; } - block = _history->blocks[iBlock]; + block = _history->blocks[iBlock].get(); } - item = block->items[iItem]; + view = block->messages[iItem].get(); + item = view->data(); } p.restore(); } @@ -601,7 +607,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) { int dateHeight = st::msgServicePadding.bottom() + st::msgServiceFont->height + st::msgServicePadding.top(); //QDate lastDate; //if (!_history->isEmpty()) { - // lastDate = _history->blocks.back()->items.back()->date.date(); + // lastDate = _history->blocks.back()->messages.back()->data()->date.date(); //} //// if item top is before this value always show date as a floating date @@ -1775,11 +1781,11 @@ void HistoryInner::recountHistoryGeometry() { _historySkipHeight = 0; if (_migrated) { if (!_migrated->isEmpty() && !_history->isEmpty() && _migrated->loadedAtBottom() && _history->loadedAtTop()) { - if (_migrated->blocks.back()->items.back()->date.date() == _history->blocks.front()->items.front()->date.date()) { - if (_migrated->blocks.back()->items.back()->isGroupMigrate() && _history->blocks.front()->items.front()->isGroupMigrate()) { - _historySkipHeight += _history->blocks.front()->items.front()->height(); + if (_migrated->blocks.back()->messages.back()->data()->date.date() == _history->blocks.front()->messages.front()->data()->date.date()) { + if (_migrated->blocks.back()->messages.back()->data()->isGroupMigrate() && _history->blocks.front()->messages.front()->data()->isGroupMigrate()) { + _historySkipHeight += _history->blocks.front()->messages.front()->data()->height(); } else { - _historySkipHeight += _history->blocks.front()->items.front()->displayedDateHeight(); + _historySkipHeight += _history->blocks.front()->messages.front()->data()->displayedDateHeight(); } } } @@ -1923,7 +1929,7 @@ void HistoryInner::onScrollDateCheck() { auto newScrollDateItem = _history->scrollTopItem ? _history->scrollTopItem : (_migrated ? _migrated->scrollTopItem : nullptr); auto newScrollDateItemTop = _history->scrollTopItem ? _history->scrollTopOffset : (_migrated ? _migrated->scrollTopOffset : 0); //if (newScrollDateItem && !displayScrollDate()) { - // if (!_history->isEmpty() && newScrollDateItem->date.date() == _history->blocks.back()->items.back()->date.date()) { + // if (!_history->isEmpty() && newScrollDateItem->date.date() == _history->blocks.back()->messages.back()->data()->date.date()) { // newScrollDateItem = nullptr; // } //} @@ -2065,32 +2071,34 @@ void HistoryInner::adjustCurrent(int32 y, History *history) const { ++_curBlock; _curItem = 0; } - auto block = history->blocks[_curBlock]; - if (_curItem >= block->items.size()) { - _curItem = block->items.size() - 1; + auto block = history->blocks[_curBlock].get(); + if (_curItem >= block->messages.size()) { + _curItem = block->messages.size() - 1; } auto by = block->y(); - while (block->items[_curItem]->y() + by > y && _curItem > 0) { + while (block->messages[_curItem]->y() + by > y && _curItem > 0) { --_curItem; } - while (block->items[_curItem]->y() + block->items[_curItem]->height() + by <= y && _curItem + 1 < block->items.size()) { + while (block->messages[_curItem]->y() + block->messages[_curItem]->data()->height() + by <= y && _curItem + 1 < block->messages.size()) { ++_curItem; } } HistoryItem *HistoryInner::prevItem(HistoryItem *item) { - if (!item || item->detached()) return nullptr; + if (!item || item->detached()) { + return nullptr; + } - HistoryBlock *block = item->block(); + const auto block = item->block(); int blockIndex = block->indexInHistory(), itemIndex = item->indexInBlock(); if (itemIndex > 0) { - return block->items.at(itemIndex - 1); + return block->messages[itemIndex - 1]->data(); } if (blockIndex > 0) { - return item->history()->blocks.at(blockIndex - 1)->items.back(); + return item->history()->blocks[blockIndex - 1]->messages.back()->data(); } if (item->history() == _history && _migrated && _history->loadedAtTop() && !_migrated->isEmpty() && _migrated->loadedAtBottom()) { - return _migrated->blocks.back()->items.back(); + return _migrated->blocks.back()->messages.back()->data(); } return nullptr; } @@ -2098,16 +2106,16 @@ HistoryItem *HistoryInner::prevItem(HistoryItem *item) { HistoryItem *HistoryInner::nextItem(HistoryItem *item) { if (!item || item->detached()) return nullptr; - HistoryBlock *block = item->block(); + const auto block = item->block(); int blockIndex = block->indexInHistory(), itemIndex = item->indexInBlock(); - if (itemIndex + 1 < block->items.size()) { - return block->items.at(itemIndex + 1); + if (itemIndex + 1 < block->messages.size()) { + return block->messages[itemIndex + 1]->data(); } if (blockIndex + 1 < item->history()->blocks.size()) { - return item->history()->blocks.at(blockIndex + 1)->items.front(); + return item->history()->blocks[blockIndex + 1]->messages.front()->data(); } if (item->history() == _migrated && _history && _migrated->loadedAtBottom() && _history->loadedAtTop() && !_history->isEmpty()) { - return _history->blocks.front()->items.front(); + return _history->blocks.front()->messages.front()->data(); } return nullptr; } @@ -2202,8 +2210,8 @@ void HistoryInner::onUpdateSelected() { adjustCurrent(point.y()); if (_curHistory && !_curHistory->isEmpty()) { - block = _curHistory->blocks[_curBlock]; - item = block->items[_curItem]; + block = _curHistory->blocks[_curBlock].get(); + item = block->messages[_curItem]->data(); App::mousedItem(item); m = mapPointToItem(point, item); @@ -2476,12 +2484,10 @@ int HistoryInner::historyScrollTop() const { auto htop = historyTop(); auto mtop = migratedTop(); if (htop >= 0 && _history->scrollTopItem) { - Assert(!_history->scrollTopItem->detached()); - return htop + _history->scrollTopItem->block()->y() + _history->scrollTopItem->y() + _history->scrollTopOffset; + return htop + _history->scrollTopItem->data()->block()->y() + _history->scrollTopItem->y() + _history->scrollTopOffset; } if (mtop >= 0 && _migrated->scrollTopItem) { - Assert(!_migrated->scrollTopItem->detached()); - return mtop + _migrated->scrollTopItem->block()->y() + _migrated->scrollTopItem->y() + _migrated->scrollTopOffset; + return mtop + _migrated->scrollTopItem->data()->block()->y() + _migrated->scrollTopItem->y() + _migrated->scrollTopOffset; } return ScrollMax; } @@ -2505,7 +2511,7 @@ int HistoryInner::itemTop(const HistoryItem *item) const { // -1 if should not b if (item->detached()) return -1; auto top = (item->history() == _history) ? historyTop() : (item->history() == _migrated ? migratedTop() : -2); - return (top < 0) ? top : (top + item->y() + item->block()->y()); + return (top < 0) ? top : (top + item->mainView()->y() + item->block()->y()); } void HistoryInner::notifyIsBotChanged() { @@ -2531,7 +2537,10 @@ void HistoryInner::notifyMigrateUpdated() { _migrated = _history->migrateFrom(); } -int HistoryInner::moveScrollFollowingInlineKeyboard(const HistoryItem *item, int oldKeyboardTop, int newKeyboardTop) { +int HistoryInner::moveScrollFollowingInlineKeyboard( + const HistoryItem *item, + int oldKeyboardTop, + int newKeyboardTop) { if (item == App::mousedItem()) { int top = itemTop(item); if (top >= oldKeyboardTop) { @@ -2706,9 +2715,9 @@ void HistoryInner::addSelectionRange( int toitem) const { if (fromblock >= 0 && fromitem >= 0 && toblock >= 0 && toitem >= 0) { for (; fromblock <= toblock; ++fromblock) { - auto block = history->blocks[fromblock]; - for (int cnt = (fromblock < toblock) ? block->items.size() : (toitem + 1); fromitem < cnt; ++fromitem) { - auto item = block->items[fromitem]; + auto block = history->blocks[fromblock].get(); + for (int cnt = (fromblock < toblock) ? block->messages.size() : (toitem + 1); fromitem < cnt; ++fromitem) { + auto item = block->messages[fromitem]->data(); changeSelectionAsGroup(toItems, item, SelectAction::Select); } if (toItems->size() >= MaxSelectedItems) break; @@ -2743,7 +2752,7 @@ void HistoryInner::applyDragSelection( toblock = -1; toitem = -1; } else { - addSelectionRange(toItems, _migrated, fromblock, fromitem, _migrated->blocks.size() - 1, _migrated->blocks.back()->items.size() - 1); + addSelectionRange(toItems, _migrated, fromblock, fromitem, _migrated->blocks.size() - 1, _migrated->blocks.back()->messages.size() - 1); } fromblock = 0; fromitem = 0; diff --git a/Telegram/SourceFiles/history/history_inner_widget.h b/Telegram/SourceFiles/history/history_inner_widget.h index d203f573d..d45b90e73 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.h +++ b/Telegram/SourceFiles/history/history_inner_widget.h @@ -122,7 +122,7 @@ private slots: private: class BotAbout; using SelectedItems = std::map>; - + using Message = HistoryView::Message; enum class MouseAction { None, PrepareDrag, @@ -161,7 +161,7 @@ private: HistoryItem *nextItem(HistoryItem *item); void updateDragSelection(HistoryItem *dragSelFrom, HistoryItem *dragSelTo, bool dragSelecting); TextSelection itemRenderSelection( - not_null item, + not_null view, int selfromy, int seltoy) const; TextSelection computeRenderSelection( @@ -291,7 +291,7 @@ private: Animation _scrollDateOpacity; SingleQueuedInvokation _scrollDateCheck; SingleTimer _scrollDateHideTimer; - HistoryItem *_scrollDateLastItem = nullptr; + Message *_scrollDateLastItem = nullptr; int _scrollDateLastItemTop = 0; ClickHandlerPtr _scrollDateLink; @@ -302,7 +302,7 @@ private: // 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)(not_null item, int itemtop, int itembottom)" signature + // Method has "bool (*Method)(not_null view, int itemtop, int itembottom)" signature // if it returns false the enumeration stops immidiately. template void enumerateItemsInHistory(History *history, int historytop, Method method); diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index d339c20dd..a4a5cd71b 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -9,11 +9,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "mainwidget.h" +#include "history/view/history_view_message.h" +#include "history/view/history_view_service_message.h" #include "history/history_item_components.h" #include "history/history_media_types.h" #include "history/history_media_grouped.h" #include "history/history_message.h" -#include "history/view/history_view_service_message.h" #include "media/media_clip_reader.h" #include "styles/style_dialogs.h" #include "styles/style_history.h" @@ -347,9 +348,22 @@ void HistoryItem::detach() { _history->setPendingResize(); } +void HistoryItem::attachToBlock(not_null block, int index) { + Expects(!isLogEntry()); + Expects(_block == nullptr); + Expects(_indexInBlock < 0); + Expects(index >= 0); + + _block = block; + _indexInBlock = index; + _mainView = block->messages[index].get(); + setPendingResize(); +} + void HistoryItem::detachFast() { _block = nullptr; _indexInBlock = -1; + _mainView = nullptr; validateGroupId(); if (groupId()) { @@ -970,6 +984,32 @@ void HistoryItem::audioTrackUpdated() { } } +HistoryItem *HistoryItem::previousItem() const { + if (_block && _indexInBlock >= 0) { + if (_indexInBlock > 0) { + return _block->messages[_indexInBlock - 1]->data(); + } + if (auto previous = _block->previousBlock()) { + Assert(!previous->messages.empty()); + return previous->messages.back()->data(); + } + } + return nullptr; +} + +HistoryItem *HistoryItem::nextItem() const { + if (_block && _indexInBlock >= 0) { + if (_indexInBlock + 1 < _block->messages.size()) { + return _block->messages[_indexInBlock + 1]->data(); + } + if (auto next = _block->nextBlock()) { + Assert(!next->messages.empty()); + return next->messages.front()->data(); + } + } + return nullptr; +} + void HistoryItem::recountDisplayDate() { Expects(!isLogEntry()); setDisplayDate([&] { diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index a5ebe0aaf..1f82b4850 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -232,23 +232,16 @@ public: const HistoryBlock *block() const { return _block; } + HistoryView::Message *mainView() const { + return _mainView; + } void destroy(); void detach(); void detachFast(); bool detached() const { return !_block; } - void attachToBlock(HistoryBlock *block, int index) { - Expects(!isLogEntry()); - Expects(_block == nullptr); - Expects(_indexInBlock < 0); - Expects(block != nullptr); - Expects(index >= 0); - - _block = block; - _indexInBlock = index; - setPendingResize(); - } + void attachToBlock(not_null block, int index); void setIndexInBlock(int index) { Expects(_block != nullptr); Expects(index >= 0); @@ -257,7 +250,7 @@ public: } int indexInBlock() const { Expects((_indexInBlock >= 0) == (_block != nullptr)); - Expects((_block == nullptr) || (_block->items[_indexInBlock] == this)); + //Expects((_block == nullptr) || (_block->messages[_indexInBlock]->data() == this)); return _indexInBlock; } @@ -423,12 +416,6 @@ public: bool hasDirectLink() const; QString directLink() const; - int y() const { - return _y; - } - void setY(int y) { - _y = y; - } MsgId id; QDateTime date; @@ -569,30 +556,8 @@ public: setAttachToNext(attachToNext); } - HistoryItem *previousItem() const { - if (_block && _indexInBlock >= 0) { - if (_indexInBlock > 0) { - return _block->items.at(_indexInBlock - 1); - } - if (auto previous = _block->previousBlock()) { - Assert(!previous->items.empty()); - return previous->items.back(); - } - } - return nullptr; - } - HistoryItem *nextItem() const { - if (_block && _indexInBlock >= 0) { - if (_indexInBlock + 1 < _block->items.size()) { - return _block->items.at(_indexInBlock + 1); - } - if (auto next = _block->nextBlock()) { - Assert(!next->items.empty()); - return next->items.front(); - } - } - return nullptr; - } + HistoryItem *previousItem() const; + HistoryItem *nextItem() const; ~HistoryItem(); @@ -676,7 +641,7 @@ protected: private: void resetGroupMedia(const std::vector> &others); - int _y = 0; + HistoryView::Message *_mainView = nullptr; int _width = 0; }; diff --git a/Telegram/SourceFiles/history/history_service.h b/Telegram/SourceFiles/history/history_service.h index cb8284e93..8919bdf28 100644 --- a/Telegram/SourceFiles/history/history_service.h +++ b/Telegram/SourceFiles/history/history_service.h @@ -13,18 +13,25 @@ struct HistoryServiceDependentData { ClickHandlerPtr lnk; }; -struct HistoryServicePinned : public RuntimeComponent, public HistoryServiceDependentData { +struct HistoryServicePinned + : public RuntimeComponent + , public HistoryServiceDependentData { }; -struct HistoryServiceGameScore : public RuntimeComponent, public HistoryServiceDependentData { +struct HistoryServiceGameScore + : public RuntimeComponent + , public HistoryServiceDependentData { int score = 0; }; -struct HistoryServicePayment : public RuntimeComponent, public HistoryServiceDependentData { +struct HistoryServicePayment + : public RuntimeComponent + , public HistoryServiceDependentData { QString amount; }; -struct HistoryServiceSelfDestruct : public RuntimeComponent { +struct HistoryServiceSelfDestruct + : public RuntimeComponent { enum class Type { Photo, Video, diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index d9ca01526..a48440102 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -29,6 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_inner_widget.h" #include "history/history_item_components.h" #include "history/view/history_view_service_message.h" +#include "history/view/history_view_message.h" #include "profile/profile_block_group_members.h" #include "info/info_memento.h" #include "core/click_handler_types.h" @@ -721,7 +722,7 @@ void HistoryWidget::animatedScrollToY(int scrollTo, HistoryItem *attachTo) { auto itemTop = _list->itemTop(attachTo); auto scrollTop = _scroll->scrollTop(); if (itemTop < 0 && !_history->isEmpty()) { - attachTo = _history->blocks.back()->items.back(); + attachTo = _history->blocks.back()->messages.back()->data(); itemTop = _list->itemTop(attachTo); } if (itemTop < 0 || (scrollTop == scrollTo)) { @@ -797,13 +798,13 @@ void HistoryWidget::adjustHighlightedMessageToMigrated() { && _migrated && !_migrated->isEmpty() && _migrated->loadedAtBottom() - && _migrated->blocks.back()->items.back()->isGroupMigrate() + && _migrated->blocks.back()->messages.back()->data()->isGroupMigrate() && _list->historyTop() != _list->historyDrawTop()) { auto highlighted = App::histItemById( _history->channelId(), _highlightedMessageId); if (highlighted && highlighted->isGroupMigrate()) { - _highlightedMessageId = -_migrated->blocks.back()->items.back()->id; + _highlightedMessageId = -_migrated->blocks.back()->messages.back()->id(); } } } @@ -2726,12 +2727,12 @@ void HistoryWidget::checkReplyReturns() { auto scrollTopMax = _scroll->scrollTopMax(); auto scrollHeight = _scroll->height(); while (_replyReturn) { - auto below = (_replyReturn->detached() && _replyReturn->history() == _history && !_history->isEmpty() && _replyReturn->id < _history->blocks.back()->items.back()->id); + auto below = (_replyReturn->detached() && _replyReturn->history() == _history && !_history->isEmpty() && _replyReturn->id < _history->blocks.back()->messages.back()->id()); if (!below) { below = (_replyReturn->detached() && _replyReturn->history() == _migrated && !_history->isEmpty()); } if (!below) { - below = (_replyReturn->detached() && _migrated && _replyReturn->history() == _migrated && !_migrated->isEmpty() && _replyReturn->id < _migrated->blocks.back()->items.back()->id); + below = (_replyReturn->detached() && _migrated && _replyReturn->history() == _migrated && !_migrated->isEmpty() && _replyReturn->id < _migrated->blocks.back()->messages.back()->id()); } if (!below && !_replyReturn->detached()) { below = (scrollTop >= scrollTopMax) || (_list->itemTop(_replyReturn) < scrollTop + scrollHeight / 2); @@ -5521,8 +5522,8 @@ void HistoryWidget::onReplyToMessage() { if (!to || to->id <= 0 || !_canSendMessages) return; if (to->history() == _migrated) { - if (to->isGroupMigrate() && !_history->isEmpty() && _history->blocks.front()->items.front()->isGroupMigrate() && _history != _migrated) { - App::contextItem(_history->blocks.front()->items.front()); + if (to->isGroupMigrate() && !_history->isEmpty() && _history->blocks.front()->messages.front()->data()->isGroupMigrate() && _history != _migrated) { + App::contextItem(_history->blocks.front()->messages.front()->data()); onReplyToMessage(); App::contextItem(to); } else { diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index 6f2879800..54121662d 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_media_types.h" #include "history/history_message.h" #include "history/history_item_components.h" +#include "history/view/history_view_message.h" #include "history/view/history_view_service_message.h" #include "chat_helpers/message_field.h" #include "mainwindow.h" @@ -59,29 +60,29 @@ void ListWidget::enumerateItems(Method method) { ending, _visibleTop, [this](auto &elem, int top) { - return this->itemTop(elem) + elem->height() <= top; + return this->itemTop(elem) + elem->data()->height() <= top; }) : std::upper_bound( beginning, ending, _visibleBottom, [this](int bottom, auto &elem) { - return this->itemTop(elem) + elem->height() >= bottom; + return this->itemTop(elem) + elem->data()->height() >= bottom; }); auto wasEnd = (from == ending); if (wasEnd) { --from; } if (TopToBottom) { - Assert(itemTop(from->get()) + from->get()->height() > _visibleTop); + Assert(itemTop(from->get()) + from->get()->data()->height() > _visibleTop); } else { Assert(itemTop(from->get()) < _visibleBottom); } while (true) { - auto item = from->get(); - auto itemtop = itemTop(item); - auto itembottom = itemtop + item->height(); + auto view = from->get(); + auto itemtop = itemTop(view); + auto itembottom = itemtop + view->data()->height(); // Binary search should've skipped all the items that are above / below the visible area. if (TopToBottom) { @@ -90,7 +91,7 @@ void ListWidget::enumerateItems(Method method) { Assert(itemtop < _visibleBottom); } - if (!method(item, itemtop, itembottom)) { + if (!method(view, itemtop, itembottom)) { return; } @@ -124,9 +125,9 @@ void ListWidget::enumerateUserpics(Method method) { // -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) { + auto userpicCallback = [this, &lowestAttachedItemTop, &method](Message *view, int itemtop, int itembottom) { // Skip all service messages. - auto message = item->toHistoryMessage(); + auto message = view->data()->toHistoryMessage(); if (!message) return true; if (lowestAttachedItemTop < 0 && message->isAttachedToNext()) { @@ -170,7 +171,8 @@ void ListWidget::enumerateDates(Method method) { // -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) { + auto dateCallback = [this, &lowestInOneDayItemBottom, &method](Message *view, int itemtop, int itembottom) { + const auto item = view->data(); if (lowestInOneDayItemBottom < 0 && item->isInOneDayWithPrevious()) { lowestInOneDayItemBottom = itembottom - item->marginBottom(); } @@ -213,20 +215,23 @@ ListWidget::ListWidget( : RpWidget(parent) , _delegate(delegate) , _controller(controller) +, _context(_delegate->listContext()) , _scrollDateCheck([this] { scrollDateCheck(); }) { setMouseTracking(true); _scrollDateHideTimer.setCallback([this] { scrollDateHideByTimer(); }); Auth().data().itemRepaintRequest( ) | rpl::start_with_next([this](auto item) { - if (ranges::find(_items, item) != _items.end()) { - repaintItem(item); + if (const auto view = viewForItem(item)) { + repaintItem(view); } }, lifetime()); subscribe(Auth().data().pendingHistoryResize(), [this] { handlePendingHistoryResize(); }); subscribe(Auth().data().queryItemVisibility(), [this](const Data::Session::ItemVisibilityQuery &query) { - if (ranges::find(_items, query.item) != _items.end()) { - auto top = itemTop(query.item); - if (top >= 0 && top + query.item->height() > _visibleTop && top < _visibleBottom) { + if (const auto view = viewForItem(query.item)) { + const auto top = itemTop(view); + if (top >= 0 + && top + query.item->height() > _visibleTop + && top < _visibleBottom) { *query.isVisible = true; } } @@ -252,7 +257,7 @@ void ListWidget::refreshRows() { _items.reserve(_slice.ids.size()); for (const auto &fullId : _slice.ids) { if (const auto item = App::histItemById(fullId)) { - _items.push_back(item); + _items.push_back(enforceViewForItem(item)); } } updateAroundPositionFromRows(); @@ -274,8 +279,8 @@ void ListWidget::restoreScrollState() { } const auto index = findNearestItem(_scrollTopState.item); if (index >= 0) { - const auto item = _items[index]; - auto newVisibleTop = itemTop(item) + _scrollTopState.shift; + const auto view = _items[index]; + auto newVisibleTop = itemTop(view) + _scrollTopState.shift; if (_visibleTop != newVisibleTop) { _delegate->listScrollTo(newVisibleTop); } @@ -283,10 +288,30 @@ void ListWidget::restoreScrollState() { _scrollTopState = ScrollTopState(); } +Message *ListWidget::viewForItem(const HistoryItem *item) const { + if (item) { + if (const auto i = _views.find(item); i != _views.end()) { + return i->second.get(); + } + } + return nullptr; +} + +not_null ListWidget::enforceViewForItem( + not_null item) { + if (const auto view = viewForItem(item)) { + return view; + } + const auto [i, ok] = _views.emplace( + item, + std::make_unique(item, _context)); + return i->second.get(); +} + void ListWidget::updateAroundPositionFromRows() { _aroundIndex = findNearestItem(_aroundPosition); if (_aroundIndex >= 0) { - _aroundPosition = _items[_aroundIndex]->position(); + _aroundPosition = _items[_aroundIndex]->data()->position(); } } @@ -296,8 +321,8 @@ int ListWidget::findNearestItem(Data::MessagePosition position) const { } const auto after = ranges::find_if( _items, - [&](not_null item) { - return (item->position() >= position); + [&](not_null view) { + return (view->data()->position() >= position); }); return (after == end(_items)) ? int(_items.size() - 1) @@ -410,10 +435,10 @@ void ListWidget::checkMoveToOtherViewer() { - kPreloadIfLessThanScreens; auto minUniversalIdDelta = (minScreenDelta * visibleHeight) / minItemHeight; - auto preloadAroundItem = [&](not_null item) { + auto preloadAroundMessage = [&](not_null view) { auto preloadRequired = false; - auto itemPosition = item->position(); - auto itemIndex = ranges::find(_items, item) - begin(_items); + auto itemPosition = view->data()->position(); + auto itemIndex = ranges::find(_items, view) - begin(_items); Assert(itemIndex < _items.size()); if (!preloadRequired) { @@ -433,9 +458,9 @@ void ListWidget::checkMoveToOtherViewer() { }; if (preloadTop && !topLoaded) { - preloadAroundItem(topItem); + preloadAroundMessage(topItem); } else if (preloadBottom && !bottomLoaded) { - preloadAroundItem(bottomItem); + preloadAroundMessage(bottomItem); } } @@ -491,9 +516,10 @@ void ListWidget::itemsAdded(Direction direction, int addedCount) { : (addedCount + 1); for (auto i = checkFrom; i != checkTo; ++i) { if (i > 0) { - auto item = _items[i - 1].get(); + const auto item = _items[i - 1]->data(); if (i < _items.size()) { - auto previous = _items[i].get(); + // #TODO feeds show + auto previous = _items[i]->data(); item->setLogEntryDisplayDate(item->date.date() != previous->date.date()); auto attachToPrevious = item->computeIsAttachToPrevious(previous); item->setLogEntryAttachToPrevious(attachToPrevious); @@ -518,7 +544,7 @@ int ListWidget::resizeGetHeight(int newWidth) { auto newHeight = 0; for (auto &item : _items) { item->setY(newHeight); - newHeight += item->resizeGetHeight(newWidth); + newHeight += item->data()->resizeGetHeight(newWidth); } _itemsHeight = newHeight; _itemsTop = (_minHeight > _itemsHeight + st::historyPaddingBottom) ? (_minHeight - _itemsHeight - st::historyPaddingBottom) : 0; @@ -543,7 +569,7 @@ void ListWidget::paintEvent(QPaintEvent *e) { auto clip = e->rect(); auto from = std::lower_bound(begin(_items), end(_items), clip.top(), [this](auto &elem, int top) { - return this->itemTop(elem) + elem->height() <= top; + return this->itemTop(elem) + elem->data()->height() <= top; }); auto to = std::lower_bound(begin(_items), end(_items), clip.top() + clip.height(), [this](auto &elem, int bottom) { return this->itemTop(elem) < bottom; @@ -553,8 +579,8 @@ void ListWidget::paintEvent(QPaintEvent *e) { p.translate(0, top); for (auto i = from; i != to; ++i) { auto selection = (*i == _selectedItem) ? _selectedText : TextSelection(); - (*i)->draw(p, clip.translated(0, -top), selection, ms); - auto height = (*i)->height(); + (*i)->data()->draw(p, clip.translated(0, -top), selection, ms); + auto height = (*i)->data()->height(); top += height; p.translate(0, height); } @@ -616,27 +642,27 @@ void ListWidget::paintEvent(QPaintEvent *e) { TextWithEntities ListWidget::getSelectedText() const { return _selectedItem - ? _selectedItem->selectedText(_selectedText) + ? _selectedItem->data()->selectedText(_selectedText) : TextWithEntities(); } -not_null ListWidget::findItemByY(int y) const { +not_null ListWidget::findItemByY(int y) const { Expects(!_items.empty()); if (y < _itemsTop) { - return _items.front().get(); + return _items.front(); } auto i = std::lower_bound( begin(_items), end(_items), y, [this](auto &elem, int top) { - return this->itemTop(elem) + elem->height() <= top; + return this->itemTop(elem) + elem->data()->height() <= top; }); return (i != end(_items)) ? i->get() : _items.back().get(); } -HistoryItem *ListWidget::strictFindItemByY(int y) const { +Message *ListWidget::strictFindItemByY(int y) const { if (_items.empty()) { return nullptr; } @@ -651,7 +677,7 @@ auto ListWidget::countScrollState() const -> ScrollTopState { } auto topItem = findItemByY(_visibleTop); return { - topItem->position(), + topItem->data()->position(), _visibleTop - itemTop(topItem) }; } @@ -675,7 +701,7 @@ void ListWidget::mouseDoubleClickEvent(QMouseEvent *e) { if (((_mouseAction == MouseAction::Selecting && _selectedItem != nullptr) || (_mouseAction == MouseAction::None)) && _mouseSelectType == TextSelectType::Letters && _mouseActionItem) { HistoryStateRequest request; request.flags |= Text::StateRequest::Flag::LookupSymbol; - auto dragState = _mouseActionItem->getState(_dragStartPosition, request); + auto dragState = _mouseActionItem->data()->getState(_dragStartPosition, request); if (dragState.cursor == HistoryInTextCursorState) { _mouseTextSymbol = dragState.symbol; _mouseSelectType = TextSelectType::Words; @@ -716,7 +742,7 @@ void ListWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { auto selTo = _selectedText.to; hasSelected = (selTo > selFrom) ? 1 : 0; if (App::mousedItem() && App::mousedItem() == App::hoveredItem()) { - auto mousePos = mapPointToItem(mapFromGlobal(_mousePosition), App::mousedItem()); + auto mousePos = mapPointToItem(mapFromGlobal(_mousePosition), viewForItem(App::mousedItem())); HistoryStateRequest request; request.flags |= Text::StateRequest::Flag::LookupSymbol; auto dragState = App::mousedItem()->getState(mousePos, request); @@ -971,7 +997,7 @@ void ListWidget::enterEventHook(QEvent *e) { void ListWidget::leaveEventHook(QEvent *e) { if (auto item = App::hoveredItem()) { - repaintItem(item); + repaintItem(viewForItem(item)); App::hoveredItem(nullptr); } ClickHandler::clearActive(); @@ -989,13 +1015,13 @@ void ListWidget::mouseActionStart(const QPoint &screenPos, Qt::MouseButton butto ClickHandler::pressed(); if (App::pressedItem() != App::hoveredItem()) { - repaintItem(App::pressedItem()); + repaintItem(viewForItem(App::pressedItem())); App::pressedItem(App::hoveredItem()); - repaintItem(App::pressedItem()); + repaintItem(viewForItem(App::pressedItem())); } _mouseAction = MouseAction::None; - _mouseActionItem = App::mousedItem(); + _mouseActionItem = viewForItem(App::mousedItem()); _dragStartPosition = mapPointToItem(mapFromGlobal(screenPos), _mouseActionItem); _pressWasInactive = _controller->window()->wasInactivePress(); if (_pressWasInactive) _controller->window()->setInactivePress(false); @@ -1008,7 +1034,7 @@ void ListWidget::mouseActionStart(const QPoint &screenPos, Qt::MouseButton butto if (_trippleClickTimer.isActive() && (screenPos - _trippleClickPoint).manhattanLength() < QApplication::startDragDistance()) { HistoryStateRequest request; request.flags = Text::StateRequest::Flag::LookupSymbol; - dragState = _mouseActionItem->getState(_dragStartPosition, request); + dragState = _mouseActionItem->data()->getState(_dragStartPosition, request); if (dragState.cursor == HistoryInTextCursorState) { auto selection = TextSelection { dragState.symbol, dragState.symbol }; repaintItem(std::exchange(_selectedItem, _mouseActionItem)); @@ -1022,7 +1048,7 @@ void ListWidget::mouseActionStart(const QPoint &screenPos, Qt::MouseButton butto } else if (App::pressedItem()) { HistoryStateRequest request; request.flags = Text::StateRequest::Flag::LookupSymbol; - dragState = _mouseActionItem->getState(_dragStartPosition, request); + dragState = _mouseActionItem->data()->getState(_dragStartPosition, request); } if (_mouseSelectType != TextSelectType::Paragraphs) { if (App::pressedItem()) { @@ -1077,7 +1103,7 @@ void ListWidget::mouseActionFinish(const QPoint &screenPos, Qt::MouseButton butt activated = nullptr; } if (App::pressedItem()) { - repaintItem(App::pressedItem()); + repaintItem(viewForItem(App::pressedItem())); App::pressedItem(nullptr); } @@ -1115,27 +1141,28 @@ void ListWidget::updateSelected() { auto point = QPoint(snap(mousePosition.x(), 0, width()), snap(mousePosition.y(), _visibleTop, _visibleBottom)); auto itemPoint = QPoint(); - auto item = strictFindItemByY(point.y()); - if (item) { + const auto view = strictFindItemByY(point.y()); + const auto item = view ? view->data().get() : nullptr; + if (view) { App::mousedItem(item); - itemPoint = mapPointToItem(point, item); + itemPoint = mapPointToItem(point, view); if (item->hasPoint(itemPoint)) { if (App::hoveredItem() != item) { - repaintItem(App::hoveredItem()); + repaintItem(viewForItem(App::hoveredItem())); App::hoveredItem(item); - repaintItem(App::hoveredItem()); + repaintItem(view); } } else if (App::hoveredItem()) { - repaintItem(App::hoveredItem()); + repaintItem(viewForItem(App::hoveredItem())); App::hoveredItem(nullptr); } } HistoryTextState dragState; ClickHandlerHost *lnkhost = nullptr; - auto selectingText = (item == _mouseActionItem && item == App::hoveredItem() && _selectedItem); - if (item) { - if (item != _mouseActionItem || (itemPoint - _dragStartPosition).manhattanLength() >= QApplication::startDragDistance()) { + auto selectingText = (view == _mouseActionItem && item == App::hoveredItem() && _selectedItem); + if (view) { + if (view != _mouseActionItem || (itemPoint - _dragStartPosition).manhattanLength() >= QApplication::startDragDistance()) { if (_mouseAction == MouseAction::PrepareDrag) { _mouseAction = MouseAction::Dragging; InvokeQueued(this, [this] { performDrag(); }); @@ -1188,7 +1215,7 @@ void ListWidget::updateSelected() { } else if (_mouseCursorState == HistoryInDateCursorState) { // cursor = style::cur_cross; } - } else if (item) { + } else if (view) { if (_mouseAction == MouseAction::Selecting) { if (selectingText) { auto second = dragState.symbol; @@ -1197,7 +1224,7 @@ void ListWidget::updateSelected() { } auto selection = TextSelection { qMin(second, _mouseTextSymbol), qMax(second, _mouseTextSymbol) }; if (_mouseSelectType != TextSelectType::Letters) { - selection = _mouseActionItem->adjustSelection(selection, _mouseSelectType); + selection = _mouseActionItem->data()->adjustSelection(selection, _mouseSelectType); } if (_selectedText != selection) { _selectedText = selection; @@ -1331,24 +1358,24 @@ void ListWidget::performDrag() { //} // #TODO drag } -int ListWidget::itemTop(not_null item) const { - return _itemsTop + item->y(); +int ListWidget::itemTop(not_null view) const { + return _itemsTop + view->y(); } -void ListWidget::repaintItem(const HistoryItem *item) { - if (!item) { +void ListWidget::repaintItem(const Message *view) { + if (!view) { return; } - update(0, itemTop(item), width(), item->height()); + update(0, itemTop(view), width(), view->data()->height()); } QPoint ListWidget::mapPointToItem( QPoint point, - const HistoryItem *item) const { - if (!item) { + const Message *view) const { + if (!view) { return QPoint(); } - return point - QPoint(0, itemTop(item)); + return point - QPoint(0, itemTop(view)); } void ListWidget::handlePendingHistoryResize() { diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.h b/Telegram/SourceFiles/history/view/history_view_list_widget.h index 401e57056..5f72a004d 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.h +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.h @@ -23,8 +23,12 @@ class Controller; namespace HistoryView { +enum class Context : char; +class Message; + class ListDelegate { public: + virtual Context listContext() = 0; virtual void listScrollTo(int top) = 0; virtual void listCloseRequest() = 0; virtual rpl::producer listSource( @@ -138,15 +142,18 @@ private: void saveScrollState(); void restoreScrollState(); + Message *viewForItem(const HistoryItem *item) const; + not_null enforceViewForItem(not_null item); + void mouseActionStart(const QPoint &screenPos, Qt::MouseButton button); void mouseActionUpdate(const QPoint &screenPos); void mouseActionFinish(const QPoint &screenPos, Qt::MouseButton button); void mouseActionCancel(); void updateSelected(); void performDrag(); - int itemTop(not_null item) const; - void repaintItem(const HistoryItem *item); - QPoint mapPointToItem(QPoint point, const HistoryItem *item) const; + int itemTop(not_null view) const; + void repaintItem(const Message *view); + QPoint mapPointToItem(QPoint point, const Message *view) const; void handlePendingHistoryResize(); void showContextMenu(QContextMenuEvent *e, bool showFromTouch = false); @@ -165,8 +172,8 @@ private: const TextWithEntities &forClipboard, QClipboard::Mode mode = QClipboard::Clipboard); - not_null findItemByY(int y) const; - HistoryItem *strictFindItemByY(int y) const; + not_null findItemByY(int y) const; + Message *strictFindItemByY(int y) const; int findNearestItem(Data::MessagePosition position) const; void checkMoveToOtherViewer(); @@ -184,7 +191,7 @@ private: // 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 + // Method has "bool (*Method)(Message *view, int itemtop, int itembottom)" signature // if it returns false the enumeration stops immediately. template void enumerateItems(Method method); @@ -210,18 +217,19 @@ private: not_null _delegate; not_null _controller; Data::MessagePosition _aroundPosition; + Context _context; int _aroundIndex = -1; int _idsLimit = kMinimalIdsLimit; Data::MessagesSlice _slice; - std::vector> _items; - std::map _itemsByIds; + std::vector> _items; + std::map, std::unique_ptr, std::less<>> _views; int _itemsTop = 0; int _itemsHeight = 0; int _minHeight = 0; int _visibleTop = 0; int _visibleBottom = 0; - HistoryItem *_visibleTopItem = nullptr; + Message *_visibleTopItem = nullptr; int _visibleTopFromItem = 0; ScrollTopState _scrollTopState; @@ -229,19 +237,19 @@ private: Animation _scrollDateOpacity; SingleQueuedInvokation _scrollDateCheck; base::Timer _scrollDateHideTimer; - HistoryItem *_scrollDateLastItem = nullptr; + Message *_scrollDateLastItem = nullptr; int _scrollDateLastItemTop = 0; MouseAction _mouseAction = MouseAction::None; TextSelectType _mouseSelectType = TextSelectType::Letters; QPoint _dragStartPosition; QPoint _mousePosition; - HistoryItem *_mouseActionItem = nullptr; + Message *_mouseActionItem = nullptr; HistoryCursorState _mouseCursorState = HistoryDefaultCursorState; uint16 _mouseTextSymbol = 0; bool _pressWasInactive = false; - HistoryItem *_selectedItem = nullptr; + Message *_selectedItem = nullptr; TextSelection _selectedText; bool _wasSelectedText = false; // was some text selected in current drag action Qt::CursorShape _cursor = style::cur_default; diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp new file mode 100644 index 000000000..64f8fd534 --- /dev/null +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -0,0 +1,25 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "history/view/history_view_message.h" + +namespace HistoryView { + +Message::Message(not_null data, Context context) +: _data(data) +, _context(context) { +} + +MsgId Message::id() const { + return _data->id; +} + +not_null Message::data() const { + return _data; +} + +} // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/history_view_message.h b/Telegram/SourceFiles/history/view/history_view_message.h new file mode 100644 index 000000000..fe9cd4a91 --- /dev/null +++ b/Telegram/SourceFiles/history/view/history_view_message.h @@ -0,0 +1,43 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +class HistoryItem; + +namespace HistoryView { + +enum class Context : char { + History, + Feed, + AdminLog +}; + +class Message + : public RuntimeComposer + , public ClickHandlerHost { +public: + Message(not_null data, Context context); + + MsgId id() const; + not_null data() const; + + int y() const { + return _y; + } + void setY(int y) { + _y = y; + } + +private: + const not_null _data; + int _y = 0; + Context _context; + +}; + +} // namespace HistoryView diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 8c9b2605c..264fa8c98 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -39,6 +39,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_message.h" #include "history/history_media.h" #include "history/view/history_view_service_message.h" +#include "history/view/history_view_message.h" #include "lang/lang_keys.h" #include "lang/lang_cloud_manager.h" #include "boxes/add_contact_box.h" @@ -1134,8 +1135,9 @@ void MainWidget::deleteAllFromUser(ChannelData *channel, UserData *from) { QVector toDestroy; if (auto history = App::historyLoaded(channel->id)) { - for_const (auto block, history->blocks) { - for_const (auto item, block->items) { + for (const auto &block : history->blocks) { + for (const auto &message : block->messages) { + const auto item = message->data(); if (item->from() == from && item->canDelete()) { toDestroy.push_back(item->id); } diff --git a/Telegram/SourceFiles/window/window_controller.cpp b/Telegram/SourceFiles/window/window_controller.cpp index 2a85d2fad..c524575a9 100644 --- a/Telegram/SourceFiles/window/window_controller.cpp +++ b/Telegram/SourceFiles/window/window_controller.cpp @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/main_window.h" #include "info/info_memento.h" +#include "history/view/history_view_message.h" #include "mainwidget.h" #include "mainwindow.h" #include "styles/style_window.h" @@ -252,13 +253,13 @@ void Controller::showJumpToDate(not_null peer, QDate requestedDate) { auto currentPeerDate = [peer] { if (auto history = App::historyLoaded(peer)) { if (history->scrollTopItem) { - return history->scrollTopItem->date.date(); + return history->scrollTopItem->data()->date.date(); } else if (history->loadedAtTop() && !history->isEmpty() && history->peer->migrateFrom()) { if (auto migrated = App::historyLoaded(history->peer->migrateFrom())) { if (migrated->scrollTopItem) { // We're up in the migrated history. // So current date is the date of first message here. - return history->blocks.front()->items.front()->date.date(); + return history->blocks.front()->messages.front()->data()->date.date(); } } } else if (!history->chatsListDate().isNull()) { @@ -287,7 +288,7 @@ void Controller::showJumpToDate(not_null peer, QDate requestedDate) { if (auto history = App::historyLoaded(chat)) { if (history->loadedAtTop()) { if (!history->isEmpty()) { - return history->blocks.front()->items.front()->date.date(); + return history->blocks.front()->messages.front()->data()->date.date(); } } else { return startDate(); @@ -297,7 +298,7 @@ void Controller::showJumpToDate(not_null peer, QDate requestedDate) { if (auto history = App::historyLoaded(peer)) { if (history->loadedAtTop()) { if (!history->isEmpty()) { - return history->blocks.front()->items.front()->date.date(); + return history->blocks.front()->messages.front()->data()->date.date(); } return QDate::currentDate(); } diff --git a/Telegram/gyp/telegram_sources.txt b/Telegram/gyp/telegram_sources.txt index 010010c04..9597e0635 100644 --- a/Telegram/gyp/telegram_sources.txt +++ b/Telegram/gyp/telegram_sources.txt @@ -227,6 +227,8 @@ <(src_loc)/history/feed/history_feed_section.h <(src_loc)/history/view/history_view_list_widget.cpp <(src_loc)/history/view/history_view_list_widget.h +<(src_loc)/history/view/history_view_message.cpp +<(src_loc)/history/view/history_view_message.h <(src_loc)/history/view/history_view_service_message.cpp <(src_loc)/history/view/history_view_service_message.h <(src_loc)/history/view/history_view_top_bar_widget.cpp