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