From 65cc4d3fbce0ac5fff898f8bf952854f29d83bb4 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 5 Oct 2017 16:35:52 +0100 Subject: [PATCH] Support item repaint in Info media overview. --- Telegram/SourceFiles/apiwrap.cpp | 2 +- Telegram/SourceFiles/app.cpp | 6 +- Telegram/SourceFiles/auth_session.h | 26 +- Telegram/SourceFiles/boxes/peer_list_box.h | 6 + .../calls/calls_box_controller.cpp | 49 +- .../SourceFiles/calls/calls_box_controller.h | 12 +- Telegram/SourceFiles/data/data_document.cpp | 13 +- Telegram/SourceFiles/data/data_photo.cpp | 18 +- .../dialogs/dialogs_inner_widget.cpp | 15 +- .../dialogs/dialogs_inner_widget.h | 2 +- Telegram/SourceFiles/facades.cpp | 14 +- Telegram/SourceFiles/facades.h | 3 - .../history/history_admin_log_inner.cpp | 17 +- .../history/history_admin_log_inner.h | 12 +- .../history/history_inner_widget.cpp | 187 ++++---- .../history/history_inner_widget.h | 18 +- Telegram/SourceFiles/history/history_item.cpp | 8 +- .../history/history_media_types.cpp | 14 +- .../SourceFiles/history/history_message.cpp | 6 +- .../history/history_shared_media.cpp | 5 +- .../history/history_shared_media.h | 14 +- .../SourceFiles/history/history_widget.cpp | 118 ++--- Telegram/SourceFiles/history/history_widget.h | 10 +- .../SourceFiles/info/info_wrap_widget.cpp | 3 +- .../info/media/info_media_inner_widget.cpp | 3 +- .../info/media/info_media_inner_widget.h | 3 - .../info/media/info_media_list_widget.cpp | 431 ++++++++++++++---- .../info/media/info_media_list_widget.h | 43 +- Telegram/SourceFiles/mainwidget.cpp | 33 +- Telegram/SourceFiles/mainwidget.h | 2 - .../media/player/media_player_float.cpp | 34 +- .../media/player/media_player_float.h | 9 +- .../media/player/media_player_list.cpp | 26 +- .../media/player/media_player_list.h | 8 +- .../media/player/media_player_panel.cpp | 6 - .../media/player/media_player_panel.h | 2 - Telegram/SourceFiles/mediaview.cpp | 2 +- .../SourceFiles/overview/overview_layout.cpp | 75 ++- .../SourceFiles/overview/overview_layout.h | 51 ++- Telegram/SourceFiles/overviewwidget.cpp | 25 +- Telegram/SourceFiles/overviewwidget.h | 4 - Telegram/SourceFiles/storage/localstorage.cpp | 2 +- .../SourceFiles/storage/storage_facade.cpp | 2 - Telegram/SourceFiles/ui/widgets/scroll_area.h | 4 +- 44 files changed, 825 insertions(+), 518 deletions(-) diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 70e58393ff..003e6ba51b 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -1661,7 +1661,7 @@ void ApiWrap::applyUpdateNoPtsCheck(const MTPUpdate &update) { if (auto item = App::histItemById(NoChannel, msgId.v)) { if (item->isMediaUnread()) { item->markMediaRead(); - Ui::repaintHistoryItem(item); + Auth().data().requestItemRepaint(item); if (item->out() && item->history()->peer->isUser()) { auto when = App::main()->requestingDifference() ? 0 : unixtime(); diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 0bbe315385..131816012b 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -1954,8 +1954,10 @@ namespace { } } Auth().notifications().clearFromItem(item); - if (Global::started() && !App::quitting()) { - Global::RefItemRemoved().notify(item, true); + if (Global::started() + && !App::quitting() + && AuthSession::Exists()) { + Auth().data().markItemRemoved(item); } } diff --git a/Telegram/SourceFiles/auth_session.h b/Telegram/SourceFiles/auth_session.h index 39236e8c61..3a806eca5d 100644 --- a/Telegram/SourceFiles/auth_session.h +++ b/Telegram/SourceFiles/auth_session.h @@ -66,9 +66,6 @@ public: base::Observable> &historyCleared() { return _historyCleared; } - base::Observable> &repaintLogEntry() { - return _repaintLogEntry; - } base::Observable &pendingHistoryResize() { return _pendingHistoryResize; } @@ -79,6 +76,24 @@ public: base::Observable &queryItemVisibility() { return _queryItemVisibility; } + void markItemLayoutChanged(not_null item) { + _itemLayoutChanged.fire(std::move(item)); + } + rpl::producer> itemLayoutChanged() const { + return _itemLayoutChanged.events(); + } + void requestItemRepaint(not_null item) { + _itemRepaintRequest.fire(std::move(item)); + } + rpl::producer> itemRepaintRequest() const { + return _itemRepaintRequest.events(); + } + void markItemRemoved(not_null item) { + _itemRemoved.fire(std::move(item)); + } + rpl::producer> itemRemoved() const { + return _itemRemoved.events(); + } void copyFrom(const AuthSessionData &other) { _variables = other._variables; @@ -187,9 +202,12 @@ private: base::Observable _stickersUpdated; base::Observable _savedGifsUpdated; base::Observable> _historyCleared; - base::Observable> _repaintLogEntry; base::Observable _pendingHistoryResize; base::Observable _queryItemVisibility; + rpl::event_stream> _itemLayoutChanged; + rpl::event_stream> _itemRepaintRequest; + rpl::event_stream> _itemRemoved; + rpl::event_stream _thirdSectionInfoEnabledValue; bool _tabbedReplacedWithInfo = false; rpl::event_stream _tabbedReplacedWithInfoValue; diff --git a/Telegram/SourceFiles/boxes/peer_list_box.h b/Telegram/SourceFiles/boxes/peer_list_box.h index 397749c52a..d98c7ded81 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.h +++ b/Telegram/SourceFiles/boxes/peer_list_box.h @@ -324,6 +324,10 @@ public: void peerListSearchAddRow(not_null peer) override; void peerListSearchRefreshRows() override; + rpl::lifetime &lifetime() { + return _lifetime; + } + virtual ~PeerListController() = default; protected: @@ -351,6 +355,8 @@ private: PeerListDelegate *_delegate = nullptr; std::unique_ptr _searchController = nullptr; + rpl::lifetime _lifetime; + }; class PeerListContent diff --git a/Telegram/SourceFiles/calls/calls_box_controller.cpp b/Telegram/SourceFiles/calls/calls_box_controller.cpp index 16a0294018..b7ba1b796e 100644 --- a/Telegram/SourceFiles/calls/calls_box_controller.cpp +++ b/Telegram/SourceFiles/calls/calls_box_controller.cpp @@ -28,6 +28,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "calls/calls_instance.h" #include "history/history_media_types.h" #include "mainwidget.h" +#include "auth_session.h" namespace Calls { namespace { @@ -39,7 +40,7 @@ constexpr auto kPerPageCount = 100; class BoxController::Row : public PeerListRow { public: - Row(HistoryItem *item); + Row(not_null item); enum class Type { Out, @@ -47,7 +48,7 @@ public: Missed, }; - bool canAddItem(HistoryItem *item) const { + bool canAddItem(not_null item) const { return (ComputeType(item) == _type && item->date.date() == _date); } void addItem(HistoryItem *item) { @@ -58,7 +59,7 @@ public: }); refreshStatus(); } - void itemRemoved(HistoryItem *item) { + void itemRemoved(not_null item) { if (hasItems() && item->id >= minItemId() && item->id <= maxItemId()) { _items.erase(std::remove(_items.begin(), _items.end(), item), _items.end()); refreshStatus(); @@ -102,7 +103,7 @@ public: private: void refreshStatus(); - static Type ComputeType(HistoryItem *item); + static Type ComputeType(not_null item); std::vector _items; QDate _date; @@ -112,7 +113,8 @@ private: }; -BoxController::Row::Row(HistoryItem *item) : PeerListRow(item->history()->peer, item->id) +BoxController::Row::Row(not_null item) +: PeerListRow(item->history()->peer, item->id) , _items(1, item) , _date(item->date.date()) , _type(ComputeType(item)) { @@ -164,7 +166,8 @@ void BoxController::Row::refreshStatus() { setCustomStatus((_items.size() > 1) ? lng_call_box_status_group(lt_count, QString::number(_items.size()), lt_status, text()) : text()); } -BoxController::Row::Type BoxController::Row::ComputeType(HistoryItem *item) { +BoxController::Row::Type BoxController::Row::ComputeType( + not_null item) { if (item->out()) { return Type::Out; } else if (auto media = item->getMedia()) { @@ -193,18 +196,19 @@ void BoxController::Row::stopLastActionRipple() { } void BoxController::prepare() { - subscribe(Global::RefItemRemoved(), [this](HistoryItem *item) { - if (auto row = rowForItem(item)) { - row->itemRemoved(item); - if (!row->hasItems()) { - delegate()->peerListRemoveRow(row); - if (!delegate()->peerListFullRowsCount()) { - refreshAbout(); + Auth().data().itemRemoved() + | rpl::start_with_next([this](auto item) { + if (auto row = rowForItem(item)) { + row->itemRemoved(item); + if (!row->hasItems()) { + delegate()->peerListRemoveRow(row); + if (!delegate()->peerListFullRowsCount()) { + refreshAbout(); + } } + delegate()->peerListRefreshRows(); } - delegate()->peerListRefreshRows(); - } - }); + }, lifetime()); subscribe(Current().newServiceMessage(), [this](const FullMsgId &msgId) { if (auto item = App::histItemById(msgId)) { insertRow(item, InsertWay::Prepend); @@ -287,21 +291,25 @@ void BoxController::receivedCalls(const QVector &result) { delegate()->peerListRefreshRows(); } -bool BoxController::insertRow(HistoryItem *item, InsertWay way) { +bool BoxController::insertRow( + not_null item, + InsertWay way) { if (auto row = rowForItem(item)) { if (row->canAddItem(item)) { row->addItem(item); return false; } } - (way == InsertWay::Append) ? delegate()->peerListAppendRow(createRow(item)) : delegate()->peerListPrependRow(createRow(item)); + (way == InsertWay::Append) + ? delegate()->peerListAppendRow(createRow(item)) + : delegate()->peerListPrependRow(createRow(item)); delegate()->peerListSortRows([](PeerListRow &a, PeerListRow &b) { return static_cast(a).maxItemId() > static_cast(b).maxItemId(); }); return true; } -BoxController::Row *BoxController::rowForItem(HistoryItem *item) { +BoxController::Row *BoxController::rowForItem(not_null item) { auto v = delegate(); if (auto fullRowsCount = v->peerListFullRowsCount()) { auto itemId = item->id; @@ -343,7 +351,8 @@ BoxController::Row *BoxController::rowForItem(HistoryItem *item) { return nullptr; } -std::unique_ptr BoxController::createRow(HistoryItem *item) const { +std::unique_ptr BoxController::createRow( + not_null item) const { auto row = std::make_unique(item); return std::move(row); } diff --git a/Telegram/SourceFiles/calls/calls_box_controller.h b/Telegram/SourceFiles/calls/calls_box_controller.h index cbaf79d9d7..d078d86ca9 100644 --- a/Telegram/SourceFiles/calls/calls_box_controller.h +++ b/Telegram/SourceFiles/calls/calls_box_controller.h @@ -24,7 +24,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org namespace Calls { -class BoxController : public PeerListController, private base::Subscriber, private MTP::Sender { +class BoxController + : public PeerListController + , private base::Subscriber + , private MTP::Sender { public: void prepare() override; void rowClicked(not_null row) override; @@ -36,14 +39,15 @@ private: void refreshAbout(); class Row; - Row *rowForItem(HistoryItem *item); + Row *rowForItem(not_null item); enum class InsertWay { Append, Prepend, }; - bool insertRow(HistoryItem *item, InsertWay way); - std::unique_ptr createRow(HistoryItem *item) const; + bool insertRow(not_null item, InsertWay way); + std::unique_ptr createRow( + not_null item) const; MsgId _offsetId = 0; mtpRequestId _loadRequestId = 0; diff --git a/Telegram/SourceFiles/data/data_document.cpp b/Telegram/SourceFiles/data/data_document.cpp index 7e195f0b7d..dbd7e371bb 100644 --- a/Telegram/SourceFiles/data/data_document.cpp +++ b/Telegram/SourceFiles/data/data_document.cpp @@ -22,23 +22,14 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "lang/lang_keys.h" #include "inline_bots/inline_bot_layout_item.h" -//#include "observer_peer.h" #include "mainwidget.h" -//#include "application.h" -//#include "storage/file_upload.h" -//#include "mainwindow.h" #include "core/file_utilities.h" -//#include "apiwrap.h" -//#include "boxes/confirm_box.h" #include "media/media_audio.h" #include "storage/localstorage.h" #include "platform/platform_specific.h" #include "history/history_media_types.h" -//#include "styles/style_history.h" -//#include "window/themes/window_theme.h" -//#include "auth_session.h" +#include "auth_session.h" #include "messenger.h" -//#include "storage/file_download.h" QString joinList(const QStringList &list, const QString &sep) { QString result; @@ -728,7 +719,7 @@ void DocumentData::cancel() { void DocumentData::notifyLayoutChanged() const { auto &items = App::documentItems(); for (auto item : items.value(const_cast(this))) { - Notify::historyItemLayoutChanged(item); + Auth().data().markItemLayoutChanged(item); } if (auto items = InlineBots::Layout::documentItems()) { diff --git a/Telegram/SourceFiles/data/data_photo.cpp b/Telegram/SourceFiles/data/data_photo.cpp index 3862d8f88d..41359df524 100644 --- a/Telegram/SourceFiles/data/data_photo.cpp +++ b/Telegram/SourceFiles/data/data_photo.cpp @@ -20,24 +20,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org */ #include "data/data_photo.h" -//#include "lang/lang_keys.h" -//#include "inline_bots/inline_bot_layout_item.h" -//#include "observer_peer.h" #include "mainwidget.h" -//#include "application.h" -//#include "storage/file_upload.h" -//#include "mainwindow.h" -//#include "core/file_utilities.h" -//#include "apiwrap.h" -//#include "boxes/confirm_box.h" -//#include "media/media_audio.h" -//#include "storage/localstorage.h" #include "history/history_media_types.h" -//#include "styles/style_history.h" -//#include "window/themes/window_theme.h" -//#include "auth_session.h" +#include "auth_session.h" #include "messenger.h" -//#include "storage/file_download.h" PhotoData::PhotoData(const PhotoId &id, const uint64 &access, int32 date, const ImagePtr &thumb, const ImagePtr &medium, const ImagePtr &full) : id(id) @@ -90,7 +76,7 @@ void PhotoData::notifyLayoutChanged() const { auto i = items.constFind(const_cast(this)); if (i != items.cend()) { for_const (auto item, i.value()) { - Notify::historyItemLayoutChanged(item); + Auth().data().markItemLayoutChanged(item); } } } diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index 482c85b078..f81089ff95 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -92,9 +92,16 @@ DialogsInner::DialogsInner(QWidget *parent, not_null contro _cancelSearchFromUser->hide(); subscribe(Auth().downloaderTaskFinished(), [this] { update(); }); - subscribe(Global::RefItemRemoved(), [this](HistoryItem *item) { - itemRemoved(item); - }); + Auth().data().itemRemoved() + | rpl::start_with_next( + [this](auto item) { itemRemoved(item); }, + lifetime()); + Auth().data().itemRepaintRequest() + | rpl::start_with_next([this](auto item) { + if (item->history()->lastMsg == item) { + item->history()->updateChatListEntry(); + } + }, lifetime()); subscribe(App::histories().sendActionAnimationUpdated(), [this](const Histories::SendActionAnimationUpdate &update) { auto updateRect = Dialogs::Layout::RowPainter::sendActionAnimationRect(update.width, update.height, getFullWidth(), update.textUpdated); updateDialogRow(update.history->peer, MsgId(0), updateRect, UpdateRowSection::Default | UpdateRowSection::Filtered); @@ -1436,7 +1443,7 @@ void DialogsInner::visibleTopBottomUpdated( } } -void DialogsInner::itemRemoved(HistoryItem *item) { +void DialogsInner::itemRemoved(not_null item) { int wasCount = _searchResults.size(); for (auto i = _searchResults.begin(); i != _searchResults.end();) { if ((*i)->item() == item) { diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h index 997a08bf31..c525d0cfb0 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h @@ -176,7 +176,7 @@ private: } void handlePeerNameChange(not_null peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars); - void itemRemoved(HistoryItem *item); + void itemRemoved(not_null item); enum class UpdateRowSection { Default = (1 << 0), Filtered = (1 << 1), diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index 774f97ef7b..9dd53c639b 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -250,12 +250,6 @@ bool isLayerShown() { return false; } -void repaintHistoryItem(not_null item) { - if (auto main = App::main()) { - main->ui_repaintHistoryItem(item); - } -} - void autoplayMediaInlineAsync(const FullMsgId &msgId) { if (auto main = App::main()) { InvokeQueued(main, [msgId] { @@ -355,10 +349,6 @@ void migrateUpdated(PeerData *peer) { if (MainWidget *m = App::main()) m->notify_migrateUpdated(peer); } -void historyItemLayoutChanged(const HistoryItem *item) { - if (MainWidget *m = App::main()) m->notify_historyItemLayoutChanged(item); -} - void historyMuteUpdated(History *history) { if (MainWidget *m = App::main()) m->notify_historyMuteUpdated(history); } @@ -370,7 +360,7 @@ void handlePendingHistoryUpdate() { Auth().data().pendingHistoryResize().notify(true); for (auto item : base::take(Global::RefPendingRepaintItems())) { - Ui::repaintHistoryItem(item); + Auth().data().requestItemRepaint(item); // Start the video if it is waiting for that. if (item->pendingInitDimensions()) { @@ -639,7 +629,6 @@ struct Data { base::Variable WorkMode = { dbiwmWindowAndTray }; - base::Observable ItemRemoved; base::Observable UnreadCounterUpdate; base::Observable PeerChooseCancel; @@ -762,7 +751,6 @@ DefineRefVar(Global, base::Observable, LocalPasscodeChanged); DefineRefVar(Global, base::Variable, WorkMode); -DefineRefVar(Global, base::Observable, ItemRemoved); DefineRefVar(Global, base::Observable, UnreadCounterUpdate); DefineRefVar(Global, base::Observable, PeerChooseCancel); diff --git a/Telegram/SourceFiles/facades.h b/Telegram/SourceFiles/facades.h index 8df6419e50..67fed2a362 100644 --- a/Telegram/SourceFiles/facades.h +++ b/Telegram/SourceFiles/facades.h @@ -122,7 +122,6 @@ void hideLayer(anim::type animated = anim::type::normal); void hideSettingsAndLayer(anim::type animated = anim::type::normal); bool isLayerShown(); -void repaintHistoryItem(not_null item); void autoplayMediaInlineAsync(const FullMsgId &msgId); void showPeerProfile(const PeerId &peer); @@ -181,7 +180,6 @@ bool switchInlineBotButtonReceived(const QString &query, UserData *samePeerBot = void migrateUpdated(PeerData *peer); -void historyItemLayoutChanged(const HistoryItem *item); void historyMuteUpdated(History *history); // handle pending resize() / paint() on history items @@ -399,7 +397,6 @@ DeclareRefVar(base::Observable, LocalPasscodeChanged); DeclareRefVar(base::Variable, WorkMode); -DeclareRefVar(base::Observable, ItemRemoved); DeclareRefVar(base::Observable, UnreadCounterUpdate); DeclareRefVar(base::Observable, PeerChooseCancel); diff --git a/Telegram/SourceFiles/history/history_admin_log_inner.cpp b/Telegram/SourceFiles/history/history_admin_log_inner.cpp index 660f7beb23..b22bd46e64 100644 --- a/Telegram/SourceFiles/history/history_admin_log_inner.cpp +++ b/Telegram/SourceFiles/history/history_admin_log_inner.cpp @@ -205,7 +205,11 @@ void InnerWidget::enumerateDates(Method method) { enumerateItems(dateCallback); } -InnerWidget::InnerWidget(QWidget *parent, not_null controller, not_null channel) : TWidget(parent) +InnerWidget::InnerWidget( + QWidget *parent, + not_null controller, + not_null channel) +: RpWidget(parent) , _controller(controller) , _channel(channel) , _history(App::history(channel)) @@ -213,11 +217,12 @@ InnerWidget::InnerWidget(QWidget *parent, not_null controll , _emptyText(st::historyAdminLogEmptyWidth - st::historyAdminLogEmptyPadding.left() - st::historyAdminLogEmptyPadding.left()) { setMouseTracking(true); _scrollDateHideTimer.setCallback([this] { scrollDateHideByTimer(); }); - subscribe(Auth().data().repaintLogEntry(), [this](not_null historyItem) { - if (_history == historyItem->history()) { - repaintItem(historyItem); - } - }); + Auth().data().itemRepaintRequest() + | rpl::start_with_next([this](auto item) { + if (item->isLogEntry() && _history == item->history()) { + repaintItem(item); + } + }, lifetime()); subscribe(Auth().data().pendingHistoryResize(), [this] { handlePendingHistoryResize(); }); subscribe(Auth().data().queryItemVisibility(), [this](const AuthSessionData::ItemVisibilityQuery &query) { if (_history != query.item->history() || !query.item->isLogEntry() || !isVisible()) { diff --git a/Telegram/SourceFiles/history/history_admin_log_inner.h b/Telegram/SourceFiles/history/history_admin_log_inner.h index 2a5ce5eacf..03fbbe4140 100644 --- a/Telegram/SourceFiles/history/history_admin_log_inner.h +++ b/Telegram/SourceFiles/history/history_admin_log_inner.h @@ -23,6 +23,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "history/history_admin_log_item.h" #include "history/history_admin_log_section.h" #include "ui/widgets/tooltip.h" +#include "ui/rp_widget.h" #include "mtproto/sender.h" #include "base/timer.h" @@ -38,9 +39,16 @@ namespace AdminLog { class SectionMemento; -class InnerWidget final : public TWidget, public Ui::AbstractTooltipShower, private MTP::Sender, private base::Subscriber { +class InnerWidget final + : public Ui::RpWidget + , public Ui::AbstractTooltipShower + , private MTP::Sender + , private base::Subscriber { public: - InnerWidget(QWidget *parent, not_null controller, not_null channel); + InnerWidget( + QWidget *parent, + not_null controller, + not_null channel); base::Observable showSearchSignal; base::Observable scrollToSignal; diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 6e3a27a87d..c3aae7f8c2 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -84,7 +84,12 @@ int BinarySearchBlocksOrItems(const T &list, int edge) { // flick scroll taken from http://qt-project.org/doc/qt-4.8/demos-embedded-anomaly-src-flickcharm-cpp.html -HistoryInner::HistoryInner(HistoryWidget *historyWidget, not_null controller, Ui::ScrollArea *scroll, History *history) : TWidget(nullptr) +HistoryInner::HistoryInner( + HistoryWidget *historyWidget, + not_null controller, + Ui::ScrollArea *scroll, + History *history) +: RpWidget(nullptr) , _controller(controller) , _peer(history->peer) , _migrated(history->migrateFrom()) @@ -105,9 +110,10 @@ HistoryInner::HistoryInner(HistoryWidget *historyWidget, not_nullgifPauseLevelChanged(), [this] { if (!_controller->isGifPausedAtLeastFor(Window::GifPauseReason::Any)) { update(); @@ -389,7 +395,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) { adjustCurrent(clip.top()); auto selEnd = _selected.cend(); - auto hasSel = !_selected.isEmpty(); + auto hasSel = !_selected.empty(); auto drawToY = clip.y() + clip.height(); @@ -420,9 +426,9 @@ void HistoryInner::paintEvent(QPaintEvent *e) { sel = FullSelection; } } else if (hasSel) { - auto i = _selected.constFind(item); + auto i = _selected.find(item); if (i != selEnd) { - sel = i.value(); + sel = i->second; } } item->draw(p, clip.translated(0, -y), sel, ms); @@ -471,9 +477,9 @@ void HistoryInner::paintEvent(QPaintEvent *e) { sel = FullSelection; } } else if (hasSel) { - auto i = _selected.constFind(item); + auto i = _selected.find(item); if (i != selEnd) { - sel = i.value(); + sel = i->second; } } item->draw(p, historyRect.translated(0, -y), sel, ms); @@ -570,15 +576,18 @@ void HistoryInner::paintEvent(QPaintEvent *e) { } } -bool HistoryInner::event(QEvent *e) { - if (e->type() == QEvent::TouchBegin || e->type() == QEvent::TouchUpdate || e->type() == QEvent::TouchEnd || e->type() == QEvent::TouchCancel) { +bool HistoryInner::eventHook(QEvent *e) { + if (e->type() == QEvent::TouchBegin + || e->type() == QEvent::TouchUpdate + || e->type() == QEvent::TouchEnd + || e->type() == QEvent::TouchCancel) { QTouchEvent *ev = static_cast(e); if (ev->device()->type() == QTouchDevice::TouchScreen) { touchEvent(ev); return true; } } - return TWidget::event(e); + return RpWidget::eventHook(e); } void HistoryInner::onTouchScrollTimer() { @@ -811,9 +820,9 @@ void HistoryInner::mouseActionStart(const QPoint &screenPos, Qt::MouseButton but if (ClickHandler::getPressed()) { _mouseAction = MouseAction::PrepareDrag; - } else if (!_selected.isEmpty()) { - if (_selected.cbegin().value() == FullSelection) { - if (_selected.constFind(_mouseActionItem) != _selected.cend() && App::hoveredItem()) { + } else if (!_selected.empty()) { + if (_selected.cbegin()->second == FullSelection) { + if (_selected.find(_mouseActionItem) != _selected.cend() && App::hoveredItem()) { _mouseAction = MouseAction::PrepareDrag; // start items drag } else if (!_pressWasInactive) { _mouseAction = MouseAction::PrepareSelect; // start items select @@ -828,12 +837,12 @@ void HistoryInner::mouseActionStart(const QPoint &screenPos, Qt::MouseButton but dragState = _mouseActionItem->getState(_dragStartPosition, request); if (dragState.cursor == HistoryInTextCursorState) { TextSelection selStatus = { dragState.symbol, dragState.symbol }; - if (selStatus != FullSelection && (_selected.isEmpty() || _selected.cbegin().value() != FullSelection)) { - if (!_selected.isEmpty()) { - repaintItem(_selected.cbegin().key()); + if (selStatus != FullSelection && (_selected.empty() || _selected.cbegin()->second != FullSelection)) { + if (!_selected.empty()) { + repaintItem(_selected.cbegin()->first); _selected.clear(); } - _selected.insert(_mouseActionItem, selStatus); + _selected.emplace(_mouseActionItem, selStatus); _mouseTextSymbol = dragState.symbol; _mouseAction = MouseAction::Selecting; _mouseSelectType = TextSelectType::Paragraphs; @@ -851,13 +860,12 @@ void HistoryInner::mouseActionStart(const QPoint &screenPos, Qt::MouseButton but _mouseTextSymbol = dragState.symbol; bool uponSelected = (dragState.cursor == HistoryInTextCursorState); if (uponSelected) { - if (_selected.isEmpty() || - _selected.cbegin().value() == FullSelection || - _selected.cbegin().key() != _mouseActionItem - ) { + if (_selected.empty() + || _selected.cbegin()->second == FullSelection + || _selected.cbegin()->first != _mouseActionItem) { uponSelected = false; } else { - uint16 selFrom = _selected.cbegin().value().from, selTo = _selected.cbegin().value().to; + uint16 selFrom = _selected.cbegin()->second.from, selTo = _selected.cbegin()->second.to; if (_mouseTextSymbol < selFrom || _mouseTextSymbol >= selTo) { uponSelected = false; } @@ -871,12 +879,12 @@ void HistoryInner::mouseActionStart(const QPoint &screenPos, Qt::MouseButton but } else { if (dragState.afterSymbol) ++_mouseTextSymbol; TextSelection selStatus = { _mouseTextSymbol, _mouseTextSymbol }; - if (selStatus != FullSelection && (_selected.isEmpty() || _selected.cbegin().value() != FullSelection)) { - if (!_selected.isEmpty()) { - repaintItem(_selected.cbegin().key()); + if (selStatus != FullSelection && (_selected.empty() || _selected.cbegin()->second != FullSelection)) { + if (!_selected.empty()) { + repaintItem(_selected.cbegin()->first); _selected.clear(); } - _selected.insert(_mouseActionItem, selStatus); + _selected.emplace(_mouseActionItem, selStatus); _mouseAction = MouseAction::Selecting; repaintItem(_mouseActionItem); } else { @@ -911,21 +919,20 @@ void HistoryInner::performDrag() { bool uponSelected = false; if (_mouseActionItem) { - if (!_selected.isEmpty() && _selected.cbegin().value() == FullSelection) { - uponSelected = _selected.contains(_mouseActionItem); + if (!_selected.empty() && _selected.cbegin()->second == FullSelection) { + uponSelected = (_selected.find(_mouseActionItem) != _selected.cend()); } else { HistoryStateRequest request; request.flags |= Text::StateRequest::Flag::LookupSymbol; auto dragState = _mouseActionItem->getState(_dragStartPosition, request); uponSelected = (dragState.cursor == HistoryInTextCursorState); if (uponSelected) { - if (_selected.isEmpty() || - _selected.cbegin().value() == FullSelection || - _selected.cbegin().key() != _mouseActionItem - ) { + if (_selected.empty() + || _selected.cbegin()->second == FullSelection + || _selected.cbegin()->first != _mouseActionItem) { uponSelected = false; } else { - uint16 selFrom = _selected.cbegin().value().from, selTo = _selected.cbegin().value().to; + uint16 selFrom = _selected.cbegin()->second.from, selTo = _selected.cbegin()->second.to; if (dragState.symbol < selFrom || dragState.symbol >= selTo) { uponSelected = false; } @@ -997,7 +1004,7 @@ void HistoryInner::performDrag() { } } -void HistoryInner::itemRemoved(HistoryItem *item) { +void HistoryInner::itemRemoved(not_null item) { if (_history != item->history() && _migrated != item->history()) { return; } @@ -1032,7 +1039,7 @@ void HistoryInner::mouseActionFinish(const QPoint &screenPos, Qt::MouseButton bu } else if (auto pressed = App::pressedLinkItem()) { // if we are in selecting items mode perhaps we want to // toggle selection instead of activating the pressed link - if (_mouseAction == MouseAction::PrepareDrag && !_pressWasInactive && !_selected.isEmpty() && _selected.cbegin().value() == FullSelection && button != Qt::RightButton) { + if (_mouseAction == MouseAction::PrepareDrag && !_pressWasInactive && !_selected.empty() && _selected.cbegin()->second == FullSelection && button != Qt::RightButton) { if (auto media = pressed->getMedia()) { if (media->toggleSelectionByHandlerClick(activated)) { activated.clear(); @@ -1052,27 +1059,27 @@ void HistoryInner::mouseActionFinish(const QPoint &screenPos, Qt::MouseButton bu App::activateClickHandler(activated, button); return; } - if (_mouseAction == MouseAction::PrepareSelect && !_pressWasInactive && !_selected.isEmpty() && _selected.cbegin().value() == FullSelection) { + if (_mouseAction == MouseAction::PrepareSelect && !_pressWasInactive && !_selected.empty() && _selected.cbegin()->second == FullSelection) { SelectedItems::iterator i = _selected.find(_mouseActionItem); if (i == _selected.cend() && !_mouseActionItem->serviceMsg() && _mouseActionItem->id > 0) { if (_selected.size() < MaxSelectedItems) { - if (!_selected.isEmpty() && _selected.cbegin().value() != FullSelection) { + if (!_selected.empty() && _selected.cbegin()->second != FullSelection) { _selected.clear(); } - _selected.insert(_mouseActionItem, FullSelection); + _selected.emplace(_mouseActionItem, FullSelection); } } else { _selected.erase(i); } repaintItem(_mouseActionItem); } else if (_mouseAction == MouseAction::PrepareDrag && !_pressWasInactive && button != Qt::RightButton) { - SelectedItems::iterator i = _selected.find(_mouseActionItem); - if (i != _selected.cend() && i.value() == FullSelection) { + auto i = _selected.find(_mouseActionItem); + if (i != _selected.cend() && i->second == FullSelection) { _selected.erase(i); repaintItem(_mouseActionItem); - } else if (i == _selected.cend() && !_mouseActionItem->serviceMsg() && _mouseActionItem->id > 0 && !_selected.isEmpty() && _selected.cbegin().value() == FullSelection) { + } else if (i == _selected.cend() && !_mouseActionItem->serviceMsg() && _mouseActionItem->id > 0 && !_selected.empty() && _selected.cbegin()->second == FullSelection) { if (_selected.size() < MaxSelectedItems) { - _selected.insert(_mouseActionItem, FullSelection); + _selected.emplace(_mouseActionItem, FullSelection); repaintItem(_mouseActionItem); } } else { @@ -1083,8 +1090,8 @@ void HistoryInner::mouseActionFinish(const QPoint &screenPos, Qt::MouseButton bu if (_dragSelFrom && _dragSelTo) { applyDragSelection(); _dragSelFrom = _dragSelTo = 0; - } else if (!_selected.isEmpty() && !_pressWasInactive) { - auto sel = _selected.cbegin().value(); + } else if (!_selected.empty() && !_pressWasInactive) { + auto sel = _selected.cbegin()->second; if (sel != FullSelection && sel.from == sel.to) { _selected.clear(); App::wnd()->setInnerFocus(); @@ -1098,8 +1105,8 @@ void HistoryInner::mouseActionFinish(const QPoint &screenPos, Qt::MouseButton bu _widget->updateTopBarSelection(); #if defined Q_OS_LINUX32 || defined Q_OS_LINUX64 - if (!_selected.isEmpty() && _selected.cbegin().value() != FullSelection) { - setToClipboard(_selected.cbegin().key()->selectedText(_selected.cbegin().value()), QClipboard::Selection); + if (!_selected.empty() && _selected.cbegin()->second != FullSelection) { + setToClipboard(_selected.cbegin()->first->selectedText(_selected.cbegin()->second), QClipboard::Selection); } #endif // Q_OS_LINUX32 || Q_OS_LINUX64 } @@ -1115,7 +1122,7 @@ void HistoryInner::mouseDoubleClickEvent(QMouseEvent *e) { if (!_history) return; mouseActionStart(e->globalPos(), e->button()); - if (((_mouseAction == MouseAction::Selecting && !_selected.isEmpty() && _selected.cbegin().value() != FullSelection) || (_mouseAction == MouseAction::None && (_selected.isEmpty() || _selected.cbegin().value() != FullSelection))) && _mouseSelectType == TextSelectType::Letters && _mouseActionItem) { + if (((_mouseAction == MouseAction::Selecting && !_selected.empty() && _selected.cbegin()->second != FullSelection) || (_mouseAction == MouseAction::None && (_selected.empty() || _selected.cbegin()->second != FullSelection))) && _mouseSelectType == TextSelectType::Letters && _mouseActionItem) { HistoryStateRequest request; request.flags |= Text::StateRequest::Flag::LookupSymbol; auto dragState = _mouseActionItem->getState(_dragStartPosition, request); @@ -1125,11 +1132,11 @@ void HistoryInner::mouseDoubleClickEvent(QMouseEvent *e) { if (_mouseAction == MouseAction::None) { _mouseAction = MouseAction::Selecting; TextSelection selStatus = { dragState.symbol, dragState.symbol }; - if (!_selected.isEmpty()) { - repaintItem(_selected.cbegin().key()); + if (!_selected.empty()) { + repaintItem(_selected.cbegin()->first); _selected.clear(); } - _selected.insert(_mouseActionItem, selStatus); + _selected.emplace(_mouseActionItem, selStatus); } mouseMoveEvent(e); @@ -1158,17 +1165,17 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { // -2 - has full selected items, but not over, -1 - has selection, but no over, 0 - no selection, 1 - over text, 2 - over full selected items auto isUponSelected = 0; auto hasSelected = 0;; - if (!_selected.isEmpty()) { + if (!_selected.empty()) { isUponSelected = -1; - if (_selected.cbegin().value() == FullSelection) { + if (_selected.cbegin()->second == FullSelection) { hasSelected = 2; - if (App::hoveredItem() && _selected.constFind(App::hoveredItem()) != _selected.cend()) { + if (App::hoveredItem() && _selected.find(App::hoveredItem()) != _selected.cend()) { isUponSelected = 2; } else { isUponSelected = -2; } } else { - uint16 selFrom = _selected.cbegin().value().from, selTo = _selected.cbegin().value().to; + uint16 selFrom = _selected.cbegin()->second.from, selTo = _selected.cbegin()->second.to; hasSelected = (selTo > selFrom) ? 1 : 0; if (App::mousedItem() && App::mousedItem() == App::hoveredItem()) { auto mousePos = mapPointToItem(mapFromGlobal(_mousePosition), App::mousedItem()); @@ -1520,21 +1527,21 @@ TextWithEntities HistoryInner::getSelectedText() const { applyDragSelection(&sel); } - if (sel.isEmpty()) { + if (sel.empty()) { return TextWithEntities(); } - if (sel.cbegin().value() != FullSelection) { - return sel.cbegin().key()->selectedText(sel.cbegin().value()); + if (sel.cbegin()->second != FullSelection) { + return sel.cbegin()->first->selectedText(sel.cbegin()->second); } int fullSize = 0; QString timeFormat(qsl(", [dd.MM.yy hh:mm]\n")); QMap texts; - for (auto i = sel.cbegin(), e = sel.cend(); i != e; ++i) { - HistoryItem *item = i.key(); + for (auto &selected : sel) { + auto item = selected.first; if (item->detached()) continue; - QString time = item->date.toString(timeFormat); + auto time = item->date.toString(timeFormat); TextWithEntities part, unwrapped = item->selectedText(FullSelection); int size = item->author()->name.size() + time.size() + unwrapped.text.size(); part.text.reserve(size); @@ -1563,7 +1570,7 @@ TextWithEntities HistoryInner::getSelectedText() const { void HistoryInner::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Escape) { _widget->onListEscapePressed(); - } else if (e == QKeySequence::Copy && !_selected.isEmpty()) { + } else if (e == QKeySequence::Copy && !_selected.empty()) { copySelectedText(); #ifdef Q_OS_MAC } else if (e->key() == Qt::Key_E && e->modifiers().testFlag(Qt::ControlModifier)) { @@ -1850,7 +1857,7 @@ HistoryInner::~HistoryInner() { } bool HistoryInner::focusNextPrevChild(bool next) { - if (_selected.isEmpty()) { + if (_selected.empty()) { return TWidget::focusNextPrevChild(next); } else { clearSelectedItems(); @@ -1932,7 +1939,7 @@ HistoryItem *HistoryInner::nextItem(HistoryItem *item) { } bool HistoryInner::canCopySelected() const { - return !_selected.isEmpty(); + return !_selected.empty(); } bool HistoryInner::canDeleteSelected() const { @@ -1942,13 +1949,13 @@ bool HistoryInner::canDeleteSelected() const { Window::TopBarWidget::SelectedState HistoryInner::getSelectionState() const { auto result = Window::TopBarWidget::SelectedState {}; - for (auto i = _selected.cbegin(), e = _selected.cend(); i != e; ++i) { - if (i.value() == FullSelection) { + for (auto &selected : _selected) { + if (selected.second == FullSelection) { ++result.count; - if (i.key()->canDelete()) { + if (selected.first->canDelete()) { ++result.canDeleteCount; } - if (i.key()->canForward()) { + if (selected.first->canForward()) { ++result.canForwardCount; } } else { @@ -1959,7 +1966,7 @@ Window::TopBarWidget::SelectedState HistoryInner::getSelectionState() const { } void HistoryInner::clearSelectedItems(bool onlyTextSelection) { - if (!_selected.isEmpty() && (!onlyTextSelection || _selected.cbegin().value() != FullSelection)) { + if (!_selected.empty() && (!onlyTextSelection || _selected.cbegin()->second != FullSelection)) { _selected.clear(); _widget->updateTopBarSelection(); _widget->update(); @@ -1968,12 +1975,12 @@ void HistoryInner::clearSelectedItems(bool onlyTextSelection) { SelectedItemSet HistoryInner::getSelectedItems() const { auto result = SelectedItemSet(); - if (_selected.isEmpty() || _selected.cbegin().value() != FullSelection) { + if (_selected.empty() || _selected.cbegin()->second != FullSelection) { return result; } - for (auto i = _selected.cbegin(), e = _selected.cend(); i != e; ++i) { - auto item = i.key(); + for (auto &selected : _selected) { + auto item = selected.first; if (item && item->toHistoryMessage() && item->id > 0) { if (item->history() == _migrated) { result.insert(item->id - ServerMaxMsgId, item); @@ -1986,12 +1993,12 @@ SelectedItemSet HistoryInner::getSelectedItems() const { } void HistoryInner::selectItem(HistoryItem *item) { - if (!_selected.isEmpty() && _selected.cbegin().value() != FullSelection) { + if (!_selected.empty() && _selected.cbegin()->second != FullSelection) { _selected.clear(); - } else if (_selected.size() == MaxSelectedItems && _selected.constFind(item) == _selected.cend()) { + } else if (_selected.size() == MaxSelectedItems && _selected.find(item) == _selected.cend()) { return; } - _selected.insert(item, FullSelection); + _selected.emplace(item, FullSelection); _widget->updateTopBarSelection(); _widget->update(); } @@ -2037,7 +2044,7 @@ void HistoryInner::onUpdateSelected() { HistoryTextState dragState; ClickHandlerHost *lnkhost = nullptr; - bool selectingText = (item == _mouseActionItem && item == App::hoveredItem() && !_selected.isEmpty() && _selected.cbegin().value() != FullSelection); + bool selectingText = (item == _mouseActionItem && item == App::hoveredItem() && !_selected.empty() && _selected.cbegin()->second != FullSelection); if (point.y() < _historyPaddingTop) { if (_botAbout && !_botAbout->info->text.isEmpty() && _botAbout->height > 0) { dragState = _botAbout->info->text.getState(point - _botAbout->rect.topLeft() - QPoint(st::msgPadding.left(), st::msgPadding.top() + st::botDescSkip + st::msgNameFont->height), _botAbout->width); @@ -2146,7 +2153,7 @@ void HistoryInner::onUpdateSelected() { _mouseCursorState = dragState.cursor; if (dragState.link) { cur = style::cur_pointer; - } else if (_mouseCursorState == HistoryInTextCursorState && (_selected.isEmpty() || _selected.cbegin().value() != FullSelection)) { + } else if (_mouseCursorState == HistoryInTextCursorState && (_selected.empty() || _selected.cbegin()->second != FullSelection)) { cur = style::cur_text; } else if (_mouseCursorState == HistoryInDateCursorState) { // cur = style::cur_cross; @@ -2203,8 +2210,8 @@ void HistoryInner::onUpdateSelected() { dragFirstAffected = (dragFirstAffected == dragSelTo) ? 0 : (selectingDown ? nextItem(dragFirstAffected) : prevItem(dragFirstAffected)); } if (dragFirstAffected) { - auto i = _selected.constFind(dragFirstAffected); - dragSelecting = (i == _selected.cend() || i.value() != FullSelection); + auto i = _selected.find(dragFirstAffected); + dragSelecting = (i == _selected.cend() || i->second != FullSelection); } updateDragSelection(dragSelFrom, dragSelTo, dragSelecting); } @@ -2213,7 +2220,7 @@ void HistoryInner::onUpdateSelected() { if (ClickHandler::getPressed()) { cur = style::cur_pointer; - } else if (_mouseAction == MouseAction::Selecting && !_selected.isEmpty() && _selected.cbegin().value() != FullSelection) { + } else if (_mouseAction == MouseAction::Selecting && !_selected.empty() && _selected.cbegin()->second != FullSelection) { if (!_dragSelFrom || !_dragSelTo) { cur = style::cur_text; } @@ -2353,16 +2360,16 @@ void HistoryInner::applyDragSelection() { void HistoryInner::addSelectionRange(SelectedItems *toItems, int32 fromblock, int32 fromitem, int32 toblock, int32 toitem, History *h) const { if (fromblock >= 0 && fromitem >= 0 && toblock >= 0 && toitem >= 0) { for (; fromblock <= toblock; ++fromblock) { - HistoryBlock *block = h->blocks[fromblock]; + auto block = h->blocks[fromblock]; for (int32 cnt = (fromblock < toblock) ? block->items.size() : (toitem + 1); fromitem < cnt; ++fromitem) { - HistoryItem *item = block->items[fromitem]; - SelectedItems::iterator i = toItems->find(item); + auto item = block->items[fromitem]; + auto i = toItems->find(item); if (item->id > 0 && !item->serviceMsg()) { if (i == toItems->cend()) { if (toItems->size() >= MaxSelectedItems) break; - toItems->insert(item, FullSelection); - } else if (i.value() != FullSelection) { - *i = FullSelection; + toItems->emplace(item, FullSelection); + } else if (i->second != FullSelection) { + i->second = FullSelection; } } else { if (i != toItems->cend()) { @@ -2383,7 +2390,7 @@ void HistoryInner::applyDragSelection(SelectedItems *toItems) const { } seltoy += _dragSelTo->height(); - if (!toItems->isEmpty() && toItems->cbegin().value() != FullSelection) { + if (!toItems->empty() && toItems->cbegin()->second != FullSelection) { toItems->clear(); } if (_dragSelecting) { @@ -2407,8 +2414,8 @@ void HistoryInner::applyDragSelection(SelectedItems *toItems) const { } addSelectionRange(toItems, fromblock, fromitem, toblock, toitem, _history); } else { - for (SelectedItems::iterator i = toItems->begin(); i != toItems->cend();) { - int32 iy = itemTop(i.key()); + for (auto i = toItems->begin(); i != toItems->cend();) { + auto iy = itemTop(i->first); if (iy < 0) { if (iy < -1) i = toItems->erase(i); continue; diff --git a/Telegram/SourceFiles/history/history_inner_widget.h b/Telegram/SourceFiles/history/history_inner_widget.h index b2bb2069bc..a6caed83d2 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.h +++ b/Telegram/SourceFiles/history/history_inner_widget.h @@ -20,6 +20,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org */ #pragma once +#include "ui/rp_widget.h" #include "ui/widgets/tooltip.h" #include "ui/widgets/scroll_area.h" #include "window/top_bar_widget.h" @@ -33,11 +34,18 @@ class PopupMenu; } // namespace Ui class HistoryWidget; -class HistoryInner : public TWidget, public Ui::AbstractTooltipShower, private base::Subscriber { +class HistoryInner + : public Ui::RpWidget + , public Ui::AbstractTooltipShower + , private base::Subscriber { Q_OBJECT public: - HistoryInner(HistoryWidget *historyWidget, not_null controller, Ui::ScrollArea *scroll, History *history); + HistoryInner( + HistoryWidget *historyWidget, + not_null controller, + Ui::ScrollArea *scroll, + History *history); void messagesReceived(PeerData *peer, const QVector &messages); void messagesReceivedDown(PeerData *peer, const QVector &messages); @@ -91,7 +99,7 @@ public: protected: bool focusNextPrevChild(bool next) override; - bool event(QEvent *e) override; // calls touchEvent when necessary + bool eventHook(QEvent *e) override; // calls touchEvent when necessary void touchEvent(QTouchEvent *e); void paintEvent(QPaintEvent *e) override; void mouseMoveEvent(QMouseEvent *e) override; @@ -141,7 +149,7 @@ private: void showContextMenu(QContextMenuEvent *e, bool showFromTouch = false); - void itemRemoved(HistoryItem *item); + void itemRemoved(not_null item); void savePhotoToFile(PhotoData *photo); void saveDocumentToFile(DocumentData *document); void copyContextImage(PhotoData *photo); @@ -206,7 +214,7 @@ private: bool _firstLoading = false; style::cursor _cursor = style::cur_default; - using SelectedItems = QMap; + using SelectedItems = std::map>; SelectedItems _selected; void applyDragSelection(); void applyDragSelection(SelectedItems *toItems) const; diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 0dcd17416f..d752912559 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -685,7 +685,7 @@ void HistoryItem::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool activ } } App::hoveredLinkItem(active ? this : nullptr); - Ui::repaintHistoryItem(this); + Auth().data().requestItemRepaint(this); } void HistoryItem::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) { @@ -695,7 +695,7 @@ void HistoryItem::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pres } } App::pressedLinkItem(pressed ? this : nullptr); - Ui::repaintHistoryItem(this); + Auth().data().requestItemRepaint(this); } void HistoryItem::addLogEntryOriginal(WebPageId localId, const QString &label, const TextWithEntities &content) { @@ -1103,14 +1103,14 @@ void HistoryItem::clipCallback(Media::Clip::Notification notification) { } if (!stopped) { setPendingInitDimensions(); - Notify::historyItemLayoutChanged(this); + Auth().data().markItemLayoutChanged(this); Global::RefPendingRepaintItems().insert(this); } } break; case NotificationRepaint: { if (!reader->currentDisplayed()) { - Ui::repaintHistoryItem(this); + Auth().data().requestItemRepaint(this); } } break; } diff --git a/Telegram/SourceFiles/history/history_media_types.cpp b/Telegram/SourceFiles/history/history_media_types.cpp index 3db137d0ea..b16343f049 100644 --- a/Telegram/SourceFiles/history/history_media_types.cpp +++ b/Telegram/SourceFiles/history/history_media_types.cpp @@ -176,11 +176,11 @@ void HistoryFileMedia::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool } void HistoryFileMedia::thumbAnimationCallback() { - Ui::repaintHistoryItem(_parent); + Auth().data().requestItemRepaint(_parent); } void HistoryFileMedia::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) { - Ui::repaintHistoryItem(_parent); + Auth().data().requestItemRepaint(_parent); } void HistoryFileMedia::setLinks(ClickHandlerPtr &&openl, ClickHandlerPtr &&savel, ClickHandlerPtr &&cancell) { @@ -206,7 +206,7 @@ void HistoryFileMedia::setStatusSize(int32 newSize, int32 fullSize, int32 durati void HistoryFileMedia::step_radial(TimeMs ms, bool timer) { if (timer) { - Ui::repaintHistoryItem(_parent); + Auth().data().requestItemRepaint(_parent); } else { _animation->radial.update(dataProgress(), dataFinished(), ms); if (!_animation->radial.animating()) { @@ -1553,7 +1553,7 @@ void HistoryDocument::updatePressed(QPoint point) { nameright = st::msgFilePadding.left(); } voice->setSeekingCurrent(snap((point.x() - nameleft) / float64(_width - nameleft - nameright), 0., 1.)); - Ui::repaintHistoryItem(_parent); + Auth().data().requestItemRepaint(_parent); } } } @@ -1748,7 +1748,7 @@ void HistoryDocument::step_voiceProgress(float64 ms, bool timer) { } else { voice->_playback->a_progress.update(qMin(dt, 1.), anim::linear); } - if (timer) Ui::repaintHistoryItem(_parent); + if (timer) Auth().data().requestItemRepaint(_parent); } } } @@ -2613,7 +2613,7 @@ bool HistoryGif::playInline(bool autoplay) { if (mode == Mode::Video) { _roundPlayback = std::make_unique(); _roundPlayback->setValueChangedCallback([this](float64 value) { - Ui::repaintHistoryItem(_parent); + Auth().data().requestItemRepaint(_parent); }); if (App::main()) { App::main()->mediaMarkRead(_data); @@ -2638,7 +2638,7 @@ void HistoryGif::stopInline() { clearClipReader(); _parent->setPendingInitDimensions(); - Notify::historyItemLayoutChanged(_parent); + Auth().data().markItemLayoutChanged(_parent); } void HistoryGif::setClipReader(Media::Clip::ReaderPointer gif) { diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index 602a868977..c64913dbd8 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -556,7 +556,7 @@ const style::TextStyle &HistoryMessage::KeyboardStyle::textStyle() const { } void HistoryMessage::KeyboardStyle::repaint(not_null item) const { - Ui::repaintHistoryItem(item); + Auth().data().requestItemRepaint(item); } int HistoryMessage::KeyboardStyle::buttonRadius() const { @@ -1581,7 +1581,7 @@ void HistoryMessage::setViewsCount(int32 count) { views->_viewsText = (views->_views >= 0) ? formatViewsCount(views->_views) : QString(); views->_viewsWidth = views->_viewsText.isEmpty() ? 0 : st::msgDateFont->width(views->_viewsText); if (was == views->_viewsWidth) { - Ui::repaintHistoryItem(this); + Auth().data().requestItemRepaint(this); } else { if (_text.hasSkipBlock()) { _text.setSkipBlock(HistoryMessage::skipBlockWidth(), HistoryMessage::skipBlockHeight()); @@ -1596,7 +1596,7 @@ void HistoryMessage::setId(MsgId newId) { bool wasPositive = (id > 0), positive = (newId > 0); HistoryItem::setId(newId); if (wasPositive == positive) { - Ui::repaintHistoryItem(this); + Auth().data().requestItemRepaint(this); } else { if (_text.hasSkipBlock()) { _text.setSkipBlock(HistoryMessage::skipBlockWidth(), HistoryMessage::skipBlockHeight()); diff --git a/Telegram/SourceFiles/history/history_shared_media.cpp b/Telegram/SourceFiles/history/history_shared_media.cpp index 1381070304..e9ba8f83c2 100644 --- a/Telegram/SourceFiles/history/history_shared_media.cpp +++ b/Telegram/SourceFiles/history/history_shared_media.cpp @@ -605,8 +605,7 @@ SharedMediaMergedSlice SharedMediaMergedSliceBuilder::snapshot() const { return SharedMediaMergedSlice( _key, _part, - _migrated - ); + _migrated); } rpl::producer SharedMediaMergedViewer( @@ -615,7 +614,7 @@ rpl::producer SharedMediaMergedViewer( int limitAfter) { Expects(IsServerMsgId(key.universalId) || (key.universalId == 0) - || (IsServerMsgId(-key.universalId) && key.migratedPeerId != 0)); + || (IsServerMsgId(ServerMaxMsgId + key.universalId) && key.migratedPeerId != 0)); Expects((key.universalId != 0) || (limitBefore == 0 && limitAfter == 0)); return [=](auto consumer) { diff --git a/Telegram/SourceFiles/history/history_shared_media.h b/Telegram/SourceFiles/history/history_shared_media.h index 8cb6f8f28e..d90f2fc8b6 100644 --- a/Telegram/SourceFiles/history/history_shared_media.h +++ b/Telegram/SourceFiles/history/history_shared_media.h @@ -131,7 +131,9 @@ public: return { key.migratedPeerId, key.type, - (key.universalId <= 0) ? (-key.universalId) : (ServerMaxMsgId - 1) + (key.universalId < 0) + ? (ServerMaxMsgId + key.universalId) + : (key.universalId > 0) ? (ServerMaxMsgId - 1) : 0 }; } @@ -157,9 +159,9 @@ private: return ComputeId(slice.key().peerId, slice[index]); }; static FullMsgId ComputeId(const Key &key) { - return (key.universalId > 0) + return (key.universalId >= 0) ? ComputeId(key.peerId, key.universalId) - : ComputeId(key.migratedPeerId, -key.universalId); + : ComputeId(key.migratedPeerId, ServerMaxMsgId + key.universalId); } static base::optional Add( const base::optional &a, @@ -181,7 +183,7 @@ private: && (!_migrated || _part.skippedBefore() != 0); } bool isolatedInMigrated() const { - return IsServerMsgId(-_key.universalId) + return IsServerMsgId(ServerMaxMsgId + _key.universalId) && (_migrated->skippedAfter() != 0); } @@ -298,9 +300,9 @@ private: } static Value ComputeId(const Key &key) { if (auto messageId = base::get_if(&key.universalId)) { - return (*messageId > 0) + return (*messageId >= 0) ? ComputeId(key.peerId, *messageId) - : ComputeId(key.migratedPeerId, -*messageId); + : ComputeId(key.migratedPeerId, ServerMaxMsgId + *messageId); } return *base::get_if>(&key.universalId); } diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 0c12be8a93..671c17d97d 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -171,35 +171,35 @@ void ReportSpamPanel::setReported(bool reported, PeerData *onPeer) { update(); } -HistoryHider::HistoryHider(MainWidget *parent, const SelectedItemSet &items) : TWidget(parent) +HistoryHider::HistoryHider(MainWidget *parent, const SelectedItemSet &items) : RpWidget(parent) , _forwardItems(items) , _send(this, langFactory(lng_forward_send), st::defaultBoxButton) , _cancel(this, langFactory(lng_cancel), st::defaultBoxButton) { init(); } -HistoryHider::HistoryHider(MainWidget *parent, UserData *sharedContact) : TWidget(parent) +HistoryHider::HistoryHider(MainWidget *parent, UserData *sharedContact) : RpWidget(parent) , _sharedContact(sharedContact) , _send(this, langFactory(lng_forward_send), st::defaultBoxButton) , _cancel(this, langFactory(lng_cancel), st::defaultBoxButton) { init(); } -HistoryHider::HistoryHider(MainWidget *parent) : TWidget(parent) +HistoryHider::HistoryHider(MainWidget *parent) : RpWidget(parent) , _sendPath(true) , _send(this, langFactory(lng_forward_send), st::defaultBoxButton) , _cancel(this, langFactory(lng_cancel), st::defaultBoxButton) { init(); } -HistoryHider::HistoryHider(MainWidget *parent, const QString &botAndQuery) : TWidget(parent) +HistoryHider::HistoryHider(MainWidget *parent, const QString &botAndQuery) : RpWidget(parent) , _botAndQuery(botAndQuery) , _send(this, langFactory(lng_forward_send), st::defaultBoxButton) , _cancel(this, langFactory(lng_cancel), st::defaultBoxButton) { init(); } -HistoryHider::HistoryHider(MainWidget *parent, const QString &url, const QString &text) : TWidget(parent) +HistoryHider::HistoryHider(MainWidget *parent, const QString &url, const QString &text) : RpWidget(parent) , _shareUrl(url) , _shareText(text) , _send(this, langFactory(lng_forward_send), st::defaultBoxButton) @@ -210,17 +210,18 @@ HistoryHider::HistoryHider(MainWidget *parent, const QString &url, const QString void HistoryHider::init() { subscribe(Lang::Current().updated(), [this] { refreshLang(); }); if (!_forwardItems.empty()) { - subscribe(Global::RefItemRemoved(), [this](HistoryItem *item) { - for (auto i = _forwardItems.begin(); i != _forwardItems.end(); ++i) { - if (i->get() == item) { - i = _forwardItems.erase(i); - break; + Auth().data().itemRemoved() + | rpl::start_with_next([this](auto item) { + for (auto i = _forwardItems.begin(); i != _forwardItems.end(); ++i) { + if (i->get() == item) { + i = _forwardItems.erase(i); + break; + } } - } - if (_forwardItems.empty()) { - startHide(); - } - }); + if (_forwardItems.empty()) { + startHide(); + } + }, lifetime()); } connect(_send, SIGNAL(clicked()), this, SLOT(forward())); connect(_cancel, SIGNAL(clicked()), this, SLOT(startHide())); @@ -665,9 +666,14 @@ HistoryWidget::HistoryWidget(QWidget *parent, not_null cont connect(&_updateEditTimeLeftDisplay, SIGNAL(timeout()), this, SLOT(updateField())); subscribe(Adaptive::Changed(), [this] { update(); }); - subscribe(Global::RefItemRemoved(), [this](HistoryItem *item) { - itemRemoved(item); - }); + Auth().data().itemRemoved() + | rpl::start_with_next( + [this](auto item) { itemRemoved(item); }, + lifetime()); + Auth().data().itemRepaintRequest() + | rpl::start_with_next( + [this](auto item) { repaintHistoryItem(item); }, + lifetime()); subscribe(Auth().data().contactsLoaded(), [this](bool) { if (_peer) { updateReportSpamStatus(); @@ -740,6 +746,16 @@ HistoryWidget::HistoryWidget(QWidget *parent, not_null cont } } }); + Auth().data().itemLayoutChanged() + | rpl::start_with_next([this](auto item) { + if (_peer && _list) { + if ((item == App::mousedItem()) + || (item == App::hoveredItem()) + || (item == App::hoveredLinkItem())) { + _list->onUpdateSelected(); + } + } + }, lifetime()); orderWidgets(); } @@ -913,7 +929,7 @@ void HistoryWidget::updateHighlightedMessage() { return stopMessageHighlight(); } - Ui::repaintHistoryItem(item); + Auth().data().requestItemRepaint(item); } TimeMs HistoryWidget::highlightStartTime(not_null item) const { @@ -3272,7 +3288,7 @@ void HistoryWidget::app_sendBotCallback(const HistoryMessageReplyMarkup::Button sendData = button->data; } button->requestId = MTP::send(MTPmessages_GetBotCallbackAnswer(MTP_flags(flags), _peer->input, MTP_int(msg->id), MTP_bytes(sendData)), rpcDone(&HistoryWidget::botCallbackDone, info), rpcFail(&HistoryWidget::botCallbackFail, info)); - Ui::repaintHistoryItem(msg); + Auth().data().requestItemRepaint(msg); if (_replyToId == msg->id) { cancelReply(); @@ -3290,7 +3306,7 @@ void HistoryWidget::botCallbackDone(BotCallbackInfo info, const MTPmessages_BotC if (info.row < markup->rows.size() && info.col < markup->rows.at(info.row).size()) { if (markup->rows.at(info.row).at(info.col).requestId == req) { markup->rows.at(info.row).at(info.col).requestId = 0; - Ui::repaintHistoryItem(item); + Auth().data().requestItemRepaint(item); } } } @@ -3325,7 +3341,7 @@ bool HistoryWidget::botCallbackFail(BotCallbackInfo info, const RPCError &error, if (info.row < markup->rows.size() && info.col < markup->rows.at(info.row).size()) { if (markup->rows.at(info.row).at(info.col).requestId == req) { markup->rows.at(info.row).at(info.col).requestId = 0; - Ui::repaintHistoryItem(item); + Auth().data().requestItemRepaint(item); } } } @@ -4449,7 +4465,7 @@ void HistoryWidget::onPhotoProgress(const FullMsgId &newId) { if (!item->isPost()) { updateSendAction(item->history(), SendAction::Type::UploadPhoto, 0); } - Ui::repaintHistoryItem(item); + Auth().data().requestItemRepaint(item); } } @@ -4460,17 +4476,16 @@ void HistoryWidget::onDocumentProgress(const FullMsgId &newId) { if (!item->isPost()) { updateSendAction(item->history(), (document && document->voice()) ? SendAction::Type::UploadVoice : SendAction::Type::UploadFile, document ? document->uploadOffset : 0); } - Ui::repaintHistoryItem(item); + Auth().data().requestItemRepaint(item); } } void HistoryWidget::onPhotoFailed(const FullMsgId &newId) { - HistoryItem *item = App::histItemById(newId); - if (item) { + if (auto item = App::histItemById(newId)) { if (!item->isPost()) { updateSendAction(item->history(), SendAction::Type::UploadPhoto, -1); } -// Ui::repaintHistoryItem(item); + Auth().data().requestItemRepaint(item); } } @@ -4481,7 +4496,7 @@ void HistoryWidget::onDocumentFailed(const FullMsgId &newId) { if (!item->isPost()) { updateSendAction(item->history(), (document && document->voice()) ? SendAction::Type::UploadVoice : SendAction::Type::UploadFile, -1); } - Ui::repaintHistoryItem(item); + Auth().data().requestItemRepaint(item); } } @@ -4585,13 +4600,16 @@ void HistoryWidget::grabFinish() { _topShadow->show(); } -void HistoryWidget::ui_repaintHistoryItem(not_null item) { - if (_peer && _list && (item->history() == _history || (_migrated && item->history() == _migrated))) { +void HistoryWidget::repaintHistoryItem( + not_null item) { + auto itemHistory = item->history(); + if (itemHistory == _history || itemHistory == _migrated) { auto ms = getms(); if (_lastScrolled + kSkipRepaintWhileScrollMs <= ms) { _list->repaintItem(item); } else { - _updateHistoryItems.start(_lastScrolled + kSkipRepaintWhileScrollMs - ms); + _updateHistoryItems.start( + _lastScrolled + kSkipRepaintWhileScrollMs - ms); } } } @@ -4611,12 +4629,6 @@ PeerData *HistoryWidget::ui_getPeerForMouseAction() { return _peer; } -void HistoryWidget::notify_historyItemLayoutChanged(const HistoryItem *item) { - if (_peer && _list && (item == App::mousedItem() || item == App::hoveredItem() || item == App::hoveredLinkItem())) { - _list->onUpdateSelected(); - } -} - void HistoryWidget::handlePendingHistoryUpdate() { if (hasPendingResizedItems() || _updateHistoryGeometryRequired) { if (_list) { @@ -4689,7 +4701,7 @@ void HistoryWidget::updateControlsGeometry() { st::lineWidth); } -void HistoryWidget::itemRemoved(HistoryItem *item) { +void HistoryWidget::itemRemoved(not_null item) { if (item == _replyEditMsg) { if (_editMsgId) { cancelEdit(); @@ -4707,6 +4719,17 @@ void HistoryWidget::itemRemoved(HistoryItem *item) { onKbToggle(); _kbReplyTo = 0; } + for (auto i = _toForward.begin(); i != _toForward.end(); ++i) { + if (i->get() == item) { + i = _toForward.erase(i); + updateForwardingTexts(); + if (_toForward.empty()) { + updateControlsVisibility(); + updateControlsGeometry(); + } + break; + } + } } void HistoryWidget::itemEdited(HistoryItem *item) { @@ -6148,7 +6171,6 @@ void HistoryWidget::updateForwarding() { } else { _toForward.clear(); } - updateForwardingItemRemovedSubscription(); updateControlsVisibility(); updateControlsGeometry(); } @@ -6199,24 +6221,6 @@ void HistoryWidget::checkForwardingInfo() { } } -void HistoryWidget::updateForwardingItemRemovedSubscription() { - if (_toForward.isEmpty()) { - unsubscribe(_forwardingItemRemovedSubscription); - _forwardingItemRemovedSubscription = 0; - } else if (!_forwardingItemRemovedSubscription) { - _forwardingItemRemovedSubscription = subscribe(Global::RefItemRemoved(), [this](HistoryItem *item) { - for (auto i = _toForward.begin(); i != _toForward.end(); ++i) { - if (i->get() == item) { - i = _toForward.erase(i); - updateForwardingItemRemovedSubscription(); - updateForwardingTexts(); - break; - } - } - }); - } -} - void HistoryWidget::updateReplyToName() { if (_editMsgId) return; if (!_replyEditMsg && (_replyToId || !_kbReplyTo)) return; diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h index 36c274724c..e673bf4e8c 100644 --- a/Telegram/SourceFiles/history/history_widget.h +++ b/Telegram/SourceFiles/history/history_widget.h @@ -27,6 +27,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "window/section_widget.h" #include "core/single_timer.h" #include "ui/widgets/input_fields.h" +#include "ui/rp_widget.h" #include "base/flags.h" namespace InlineBots { @@ -97,7 +98,7 @@ private: }; -class HistoryHider : public TWidget, private base::Subscriber { +class HistoryHider : public Ui::RpWidget, private base::Subscriber { Q_OBJECT public: @@ -276,7 +277,6 @@ public: void cancelEdit(); void updateForwarding(); void updateForwardingTexts(); - void updateForwardingItemRemovedSubscription(); void clearReplyReturns(); void pushReplyReturn(HistoryItem *item); @@ -350,10 +350,8 @@ public: void app_sendBotCallback(const HistoryMessageReplyMarkup::Button *button, not_null msg, int row, int col); - void ui_repaintHistoryItem(not_null item); PeerData *ui_getPeerForMouseAction(); - void notify_historyItemLayoutChanged(const HistoryItem *item); void notify_botCommandsChanged(UserData *user); void notify_inlineBotRequesting(bool requesting); void notify_replyMarkupUpdated(const HistoryItem *item); @@ -480,6 +478,7 @@ private: using TabbedPanel = ChatHelpers::TabbedPanel; using TabbedSelector = ChatHelpers::TabbedSelector; + void repaintHistoryItem(not_null item); void handlePendingHistoryUpdate(); void fullPeerUpdated(PeerData *peer); void topBarClick(); @@ -519,7 +518,7 @@ private: // If an empty filepath is found we upload (possible) "image" with (possible) "content". void uploadFilesAfterConfirmation(const QStringList &files, const QByteArray &content, const QImage &image, std::unique_ptr information, SendMediaType type, QString caption); - void itemRemoved(HistoryItem *item); + void itemRemoved(not_null item); // Updates position of controls around the message field, // like send button, emoji button and others. @@ -560,7 +559,6 @@ private: SelectedItemSet _toForward; Text _toForwardFrom, _toForwardText; int _toForwardNameVersion = 0; - int _forwardingItemRemovedSubscription = 0; MsgId _editMsgId = 0; diff --git a/Telegram/SourceFiles/info/info_wrap_widget.cpp b/Telegram/SourceFiles/info/info_wrap_widget.cpp index c1406d1b55..bcfd7d6061 100644 --- a/Telegram/SourceFiles/info/info_wrap_widget.cpp +++ b/Telegram/SourceFiles/info/info_wrap_widget.cpp @@ -218,10 +218,10 @@ void WrapWidget::showBackFromStack() { if (!_historyStack.empty()) { auto last = std::move(_historyStack.back()); _historyStack.pop_back(); - _anotherTabMemento = std::move(last.anotherTab); showNewContent( last.section.get(), params); + _anotherTabMemento = std::move(last.anotherTab); } else { controller()->showBackFromStack(params); } @@ -237,6 +237,7 @@ not_null WrapWidget::topWidget() const { void WrapWidget::showContent(object_ptr content) { _content = std::move(content); _content->show(); + _anotherTabMemento = nullptr; finishShowContent(); } diff --git a/Telegram/SourceFiles/info/media/info_media_inner_widget.cpp b/Telegram/SourceFiles/info/media/info_media_inner_widget.cpp index 0314f82299..6511b45cbb 100644 --- a/Telegram/SourceFiles/info/media/info_media_inner_widget.cpp +++ b/Telegram/SourceFiles/info/media/info_media_inner_widget.cpp @@ -187,8 +187,7 @@ Type InnerWidget::type() const { void InnerWidget::visibleTopBottomUpdated( int visibleTop, int visibleBottom) { - _visibleTop = visibleTop; - _visibleBottom = visibleBottom; + setChildVisibleTopBottom(_list, visibleTop, visibleBottom); } bool InnerWidget::showInternal(not_null memento) { diff --git a/Telegram/SourceFiles/info/media/info_media_inner_widget.h b/Telegram/SourceFiles/info/media/info_media_inner_widget.h index d43652a4c9..8682c39953 100644 --- a/Telegram/SourceFiles/info/media/info_media_inner_widget.h +++ b/Telegram/SourceFiles/info/media/info_media_inner_widget.h @@ -81,9 +81,6 @@ private: object_ptr _otherTabsShadow = { nullptr }; object_ptr _list = { nullptr }; - int _visibleTop = 0; - int _visibleBottom = 0; - }; } // namespace Media diff --git a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp index e47d458f36..10bb822166 100644 --- a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp +++ b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp @@ -23,7 +23,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "overview/overview_layout.h" #include "history/history_media_types.h" #include "window/themes/window_theme.h" +#include "storage/file_download.h" #include "lang/lang_keys.h" +#include "auth_session.h" #include "styles/style_overview.h" #include "styles/style_info.h" @@ -36,6 +38,21 @@ namespace { constexpr auto kIdsLimit = 256; using ItemBase = Layout::ItemBase; +using UniversalMsgId = int32; + +UniversalMsgId GetUniversalId(FullMsgId itemId) { + return (itemId.channel != 0) + ? itemId.msg + : (itemId.msg - ServerMaxMsgId); +} + +UniversalMsgId GetUniversalId(not_null item) { + return GetUniversalId(item->fullId()); +} + +UniversalMsgId GetUniversalId(not_null layout) { + return GetUniversalId(layout->getItem()->fullId()); +} } // namespace @@ -55,11 +72,30 @@ public: return _items.empty(); } + UniversalMsgId minId() const { + Expects(!empty()); + return _items.back().first; + } + UniversalMsgId maxId() const { + Expects(!empty()); + return _items.front().first; + } + + void setTop(int top) { + _top = top; + } + int top() const { + return _top; + } void resizeToWidth(int newWidth); int height() const { return _height; } + bool removeItem(UniversalMsgId universalId); + base::optional findItemRect( + UniversalMsgId universalId) const; + void paint( Painter &p, QRect clip, @@ -67,20 +103,33 @@ public: TimeMs ms) const; private: + using Items = base::flat_map< + UniversalMsgId, + not_null, + std::greater<>>; int headerHeight() const; void appendItem(not_null item); void setHeader(not_null item); bool belongsHere(not_null item) const; - int countRowHeight(not_null item) const; + Items::iterator findItemAfterTop(int top); + Items::const_iterator findItemAfterTop(int top) const; + Items::const_iterator findItemAfterBottom( + Items::const_iterator from, + int bottom) const; + QRect findItemRect(not_null item) const; + + int recountHeight() const; + void refreshHeight(); Type _type = Type::Photo; Text _header; - std::vector> _items; + Items _items; int _itemsLeft = 0; int _itemsTop = 0; int _itemWidth = 0; int _itemsInRow = 1; - int _rowsCount = 0; + mutable int _rowsCount = 0; + int _top = 0; int _height = 0; }; @@ -120,7 +169,7 @@ bool ListWidget::Section::belongsHere( not_null item) const { Expects(!_items.empty()); auto date = item->getItem()->date.date(); - auto myDate = _items.back()->getItem()->date.date(); + auto myDate = _items.back().second->getItem()->date.date(); switch (_type) { case Type::Photo: @@ -142,25 +191,70 @@ bool ListWidget::Section::belongsHere( Unexpected("Type in ListWidget::Section::belongsHere()"); } -int ListWidget::Section::countRowHeight( - not_null item) const { - switch (_type) { - case Type::Photo: - case Type::Video: - case Type::RoundFile: - return _itemWidth + st::infoMediaSkip; - - case Type::VoiceFile: - case Type::File: - case Type::Link: - case Type::MusicFile: - return item->height(); - } - Unexpected("Type in ListWidget::Section::countRowHeight()"); +void ListWidget::Section::appendItem(not_null item) { + _items.emplace(GetUniversalId(item), item); } -void ListWidget::Section::appendItem(not_null item) { - _items.push_back(item); +bool ListWidget::Section::removeItem(UniversalMsgId universalId) { + if (auto it = _items.find(universalId); it != _items.end()) { + it = _items.erase(it); + refreshHeight(); + return true; + } + return false; +} + +base::optional ListWidget::Section::findItemRect( + UniversalMsgId universalId) const { + if (auto it = _items.find(universalId); it != _items.end()) { + return findItemRect(it->second); + } + return base::none; +} + +QRect ListWidget::Section::findItemRect( + not_null item) const { + auto position = item->position(); + auto top = position / _itemsInRow; + auto indexInRow = position % _itemsInRow; + auto left = _itemsLeft + + indexInRow * (_itemWidth + st::infoMediaSkip); + return QRect(left, top, _itemWidth, item->height()); +} + +auto ListWidget::Section::findItemAfterTop( + int top) -> Items::iterator { + return base::lower_bound( + _items, + top, + [this](const auto &item, int top) { + auto itemTop = item.second->position() / _itemsInRow; + return (itemTop + item.second->height()) <= top; + }); +} + +auto ListWidget::Section::findItemAfterTop( + int top) const -> Items::const_iterator { + return base::lower_bound( + _items, + top, + [this](const auto &item, int top) { + auto itemTop = item.second->position() / _itemsInRow; + return (itemTop + item.second->height()) <= top; + }); +} + +auto ListWidget::Section::findItemAfterBottom( + Items::const_iterator from, + int bottom) const -> Items::const_iterator { + return std::lower_bound( + from, + _items.end(), + bottom, + [this](const auto &item, int bottom) { + auto itemTop = item.second->position() / _itemsInRow; + return itemTop < bottom; + }); } void ListWidget::Section::paint( @@ -169,9 +263,8 @@ void ListWidget::Section::paint( int outerWidth, TimeMs ms) const { auto baseIndex = 0; - auto top = _itemsTop; auto header = headerHeight(); - if (header) { + if (QRect(0, 0, outerWidth, header).intersects(clip)) { p.setPen(st::infoMediaHeaderFg); _header.drawLeftElided( p, @@ -179,14 +272,14 @@ void ListWidget::Section::paint( st::infoMediaHeaderPosition.y(), outerWidth - 2 * st::infoMediaHeaderPosition.x(), outerWidth); - top += header; } - auto fromitem = floorclamp( + auto top = header + _itemsTop; + auto fromcol = floorclamp( clip.x() - _itemsLeft, _itemWidth, 0, _itemsInRow); - auto tillitem = ceilclamp( + auto tillcol = ceilclamp( clip.x() + clip.width() - _itemsLeft, _itemWidth, 0, @@ -194,35 +287,23 @@ void ListWidget::Section::paint( Layout::PaintContext context(ms, false); context.isAfterDate = (header > 0); - // #TODO ranges, binary search for visible slice. - for (auto row = 0; row != _rowsCount; ++row) { - auto rowHeight = countRowHeight(_items[baseIndex]); - auto increment = gsl::finally([&] { - top += rowHeight; - baseIndex += _itemsInRow; - context.isAfterDate = false; - }); - - if (top >= clip.y() + clip.height()) { - break; - } else if (top + rowHeight <= clip.y()) { - continue; - } - for (auto col = fromitem; col != tillitem; ++col) { - auto index = baseIndex + col; - if (index >= int(_items.size())) { - break; - } - auto item = _items[index]; - auto left = _itemsLeft - + col * (_itemWidth + st::infoMediaSkip); - p.translate(left, top); + auto fromIt = findItemAfterTop(clip.y()); + auto tillIt = findItemAfterBottom( + fromIt, + clip.y() + clip.height()); + for (auto it = fromIt; it != tillIt; ++it) { + auto item = it->second; + auto rect = findItemRect(item); + context.isAfterDate = (header > 0) + && (rect.y() <= header + _itemsTop); + if (rect.intersects(clip)) { + p.translate(rect.topLeft()); item->paint( p, - clip.translated(-left, -top), + clip.translated(-rect.topLeft()), TextSelection(), &context); - p.translate(-left, -top); + p.translate(-rect.topLeft()); } } } @@ -237,7 +318,6 @@ void ListWidget::Section::resizeToWidth(int newWidth) { return; } - _height = headerHeight(); switch (_type) { case Type::Photo: case Type::Video: @@ -248,42 +328,70 @@ void ListWidget::Section::resizeToWidth(int newWidth) { / (st::infoMediaMinGridSize + st::infoMediaSkip); _itemWidth = ((newWidth - _itemsLeft) / _itemsInRow) - st::infoMediaSkip; - auto itemHeight = _itemWidth + st::infoMediaSkip; - _rowsCount = (int(_items.size()) + _itemsInRow - 1) - / _itemsInRow; - _height += _itemsTop + _rowsCount * itemHeight; + for (auto &item : _items) { + item.second->resizeGetHeight(_itemWidth); + } } break; case Type::VoiceFile: case Type::File: - case Type::MusicFile: { - _itemsLeft = 0; - _itemsTop = 0; - _itemsInRow = 1; - _itemWidth = newWidth; - auto itemHeight = _items.empty() ? 0 : _items.front()->height(); - _rowsCount = _items.size(); - _height += _rowsCount * itemHeight; - } break; - + case Type::MusicFile: case Type::Link: _itemsLeft = 0; _itemsTop = 0; _itemsInRow = 1; _itemWidth = newWidth; - auto top = 0; - for (auto item : _items) { - top += item->resizeGetHeight(_itemWidth); + for (auto &item : _items) { + item.second->resizeGetHeight(_itemWidth); } - _height += top; break; } - if (_type != Type::Link) { - for (auto item : _items) { - item->resizeGetHeight(_itemWidth); + refreshHeight(); +} + +int ListWidget::Section::recountHeight() const { + auto result = headerHeight(); + + switch (_type) { + case Type::Photo: + case Type::Video: + case Type::RoundFile: { + auto itemHeight = _itemWidth + st::infoMediaSkip; + auto index = 0; + result += _itemsTop; + for (auto &item : _items) { + item.second->setPosition(_itemsInRow * result + index); + if (++index == _itemsInRow) { + result += itemHeight; + index = 0; + } } + if (_items.size() % _itemsInRow) { + _rowsCount = int(_items.size()) / _itemsInRow + 1; + result += itemHeight; + } else { + _rowsCount = int(_items.size()) / _itemsInRow; + } + } break; + + case Type::VoiceFile: + case Type::File: + case Type::MusicFile: + case Type::Link: + for (auto &item : _items) { + item.second->setPosition(result); + result += item.second->height(); + } + _rowsCount = _items.size(); + break; } + + return result; +} + +void ListWidget::Section::refreshHeight() { + _height = recountHeight(); } ListWidget::ListWidget( @@ -296,13 +404,72 @@ ListWidget::ListWidget( , _peer(peer) , _type(type) , _slice(sliceKey()) { + start(); refreshViewer(); +} + +void ListWidget::start() { ObservableViewer(*Window::Theme::Background()) | rpl::start_with_next([this](const auto &update) { if (update.paletteChanged()) { invalidatePaletteCache(); } }, lifetime()); + ObservableViewer(Auth().downloader().taskFinished()) + | rpl::start_with_next([this] { update(); }, lifetime()); + Auth().data().itemLayoutChanged() + | rpl::start_with_next([this](auto item) { + if ((item == App::mousedItem()) + || (item == App::hoveredItem()) + || (item == App::hoveredLinkItem())) { + updateSelected(); + } + }, lifetime()); + Auth().data().itemRemoved() + | rpl::start_with_next([this](auto item) { + itemRemoved(item); + }, lifetime()); + Auth().data().itemRepaintRequest() + | rpl::start_with_next([this](auto item) { + repaintItem(item); + }, lifetime()); +} + +void ListWidget::itemRemoved(not_null item) { + if (myItem(item)) { + auto universalId = GetUniversalId(item); + auto sectionIt = findSectionByItem(universalId); + if (sectionIt != _sections.end()) { + if (sectionIt->removeItem(universalId)) { + auto top = sectionIt->top(); + if (sectionIt->empty()) { + _sections.erase(sectionIt); + } + refreshHeight(); + } + } + } +} + +void ListWidget::repaintItem(not_null item) { + if (myItem(item)) { + repaintItem(GetUniversalId(item)); + } +} + +void ListWidget::repaintItem(UniversalMsgId universalId) { + auto sectionIt = findSectionByItem(universalId); + if (sectionIt != _sections.end()) { + if (auto rect = sectionIt->findItemRect(universalId)) { + auto top = padding().top() + sectionIt->top(); + rtlupdate(rect->translated(0, top)); + } + } +} + +bool ListWidget::myItem(not_null item) const { + auto peer = item->history()->peer; + return (_peer == peer || _peer == peer->migrateTo()); } void ListWidget::invalidatePaletteCache() { @@ -314,9 +481,7 @@ void ListWidget::invalidatePaletteCache() { SharedMediaMergedSlice::Key ListWidget::sliceKey() const { auto universalId = _universalAroundId; using Key = SharedMediaMergedSlice::Key; - if (auto migrateTo = _peer->migrateTo()) { - return Key(migrateTo->id, _peer->id, _type, universalId); - } else if (auto migrateFrom = _peer->migrateFrom()) { + if (auto migrateFrom = _peer->migrateFrom()) { return Key(_peer->id, migrateFrom->id, _type, universalId); } return Key(_peer->id, 0, _type, universalId); @@ -338,11 +503,14 @@ int ListWidget::countIdsLimit() const { } ItemBase *ListWidget::getLayout(const FullMsgId &itemId) { - auto it = _layouts.find(itemId); + auto universalId = GetUniversalId(itemId); + auto it = _layouts.find(universalId); if (it == _layouts.end()) { if (auto layout = createLayout(itemId, _type)) { layout->initDimensions(); - it = _layouts.emplace(itemId, std::move(layout)).first; + it = _layouts.emplace( + universalId, + std::move(layout)).first; } else { return nullptr; } @@ -372,34 +540,37 @@ std::unique_ptr ListWidget::createLayout( } return nullptr; }; + + auto &fileSt = st::overviewFileLayout; + using namespace Layout; switch (type) { case Type::Photo: if (auto photo = getPhoto()) { - return std::make_unique(photo, item); + return std::make_unique(item, photo); } return nullptr; case Type::Video: if (auto file = getFile()) { - return std::make_unique(file, item); + return std::make_unique