From 54cc3e631523461f1293464021787316c8950a4f Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 13 Oct 2017 22:07:04 +0300 Subject: [PATCH] Shared media multiple items selection. --- Telegram/SourceFiles/base/build_config.h | 6 - Telegram/SourceFiles/base/flat_map.h | 121 ++- Telegram/SourceFiles/base/flat_set.h | 64 +- .../chat_helpers/gifs_list_widget.cpp | 4 +- .../history/history_inner_widget.cpp | 35 +- .../history/history_inner_widget.h | 2 +- Telegram/SourceFiles/history/history_item.h | 16 +- Telegram/SourceFiles/history/history_media.h | 28 +- .../SourceFiles/history/history_media_types.h | 32 +- .../SourceFiles/history/history_service.h | 4 +- .../info/media/info_media_list_widget.cpp | 972 +++++++++++------- .../info/media/info_media_list_widget.h | 124 ++- .../inline_bot_layout_internal.cpp | 94 +- .../inline_bots/inline_bot_layout_internal.h | 32 +- .../inline_bots/inline_results_widget.cpp | 6 +- Telegram/SourceFiles/layout.h | 15 +- .../media/player/media_player_list.cpp | 4 +- Telegram/SourceFiles/mediaview.cpp | 6 +- Telegram/SourceFiles/mtproto/sender.h | 24 +- .../SourceFiles/overview/overview_layout.cpp | 77 +- .../SourceFiles/overview/overview_layout.h | 20 +- Telegram/SourceFiles/overviewwidget.cpp | 8 +- Telegram/SourceFiles/ui/text/text.h | 2 +- 23 files changed, 1117 insertions(+), 579 deletions(-) diff --git a/Telegram/SourceFiles/base/build_config.h b/Telegram/SourceFiles/base/build_config.h index 1731630389..e708ab5d47 100644 --- a/Telegram/SourceFiles/base/build_config.h +++ b/Telegram/SourceFiles/base/build_config.h @@ -64,12 +64,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #error Please add support for your architecture in base/build_config.h #endif -#if defined(COMPILER_GCC) || defined(COMPILER_CLANG) -#define WARN_UNUSED_RESULT __attribute__((warn_unused_result)) -#else -#define WARN_UNUSED_RESULT -#endif - #if defined(__GNUC__) #define FORCE_INLINE inline __attribute__((always_inline)) #elif defined(_MSC_VER) diff --git a/Telegram/SourceFiles/base/flat_map.h b/Telegram/SourceFiles/base/flat_map.h index fe076adb50..36117f3f99 100644 --- a/Telegram/SourceFiles/base/flat_map.h +++ b/Telegram/SourceFiles/base/flat_map.h @@ -109,7 +109,18 @@ public: flat_multi_map_iterator_base_impl operator-(difference_type offset) const { return _impl - offset; } - difference_type operator-(const flat_multi_map_iterator_base_impl &right) const { + template < + typename other_iterator_impl, + typename other_pointer_impl, + typename other_reference_impl> + difference_type operator-( + const flat_multi_map_iterator_base_impl< + Key, + Type, + Compare, + other_iterator_impl, + other_pointer_impl, + other_reference_impl> &right) const { return _impl - right._impl; } reference operator[](difference_type offset) { @@ -119,13 +130,46 @@ public: return _impl[offset]; } - bool operator==(const flat_multi_map_iterator_base_impl &right) const { + template < + typename other_iterator_impl, + typename other_pointer_impl, + typename other_reference_impl> + bool operator==( + const flat_multi_map_iterator_base_impl< + Key, + Type, + Compare, + other_iterator_impl, + other_pointer_impl, + other_reference_impl> &right) const { return _impl == right._impl; } - bool operator!=(const flat_multi_map_iterator_base_impl &right) const { + template < + typename other_iterator_impl, + typename other_pointer_impl, + typename other_reference_impl> + bool operator!=( + const flat_multi_map_iterator_base_impl< + Key, + Type, + Compare, + other_iterator_impl, + other_pointer_impl, + other_reference_impl> &right) const { return _impl != right._impl; } - bool operator<(const flat_multi_map_iterator_base_impl &right) const { + template < + typename other_iterator_impl, + typename other_pointer_impl, + typename other_reference_impl> + bool operator<( + const flat_multi_map_iterator_base_impl< + Key, + Type, + Compare, + other_iterator_impl, + other_pointer_impl, + other_reference_impl> &right) const { return _impl < right._impl; } @@ -133,6 +177,15 @@ private: iterator_impl _impl; friend class flat_multi_map; + template < + typename OtherKey, + typename OtherType, + typename OtherCompare, + typename other_iterator_impl, + typename other_pointer_impl, + typename other_reference_impl> + friend class flat_multi_map_iterator_base_impl; + }; template @@ -418,10 +471,10 @@ public: return (range.second - range.first); } - iterator erase(iterator where) { + iterator erase(const_iterator where) { return _impl.erase(where._impl); } - iterator erase(iterator from, iterator till) { + iterator erase(const_iterator from, const_iterator till) { return _impl.erase(from._impl, till._impl); } @@ -525,37 +578,69 @@ public: using parent::erase; using parent::contains; - iterator insert(const value_type &value) { + std::pair insert(const value_type &value) { if (this->empty() || compare()(value.first, this->front().first)) { this->_impl.push_front(value); - return this->begin(); + return { this->begin(), true }; } else if (compare()(this->back().first, value.first)) { this->_impl.push_back(value); - return (this->end() - 1); + return { this->end() - 1, true }; } auto where = this->getLowerBound(value.first); if (compare()(value.first, where->first)) { - return this->_impl.insert(where, value); + return { this->_impl.insert(where, value), true }; } - return this->end(); + return { where, false }; } - iterator insert(value_type &&value) { + std::pair insert(value_type &&value) { if (this->empty() || compare()(value.first, this->front().first)) { this->_impl.push_front(std::move(value)); - return this->begin(); + return { this->begin(), true }; } else if (compare()(this->back().first, value.first)) { this->_impl.push_back(std::move(value)); - return (this->end() - 1); + return { this->end() - 1, true }; } auto where = this->getLowerBound(value.first); if (compare()(value.first, where->first)) { - return this->_impl.insert(where, std::move(value)); + return { this->_impl.insert(where, std::move(value)), true }; } - return this->end(); + return { where, false }; } template - iterator emplace(Args&&... args) { - return this->insert(value_type(std::forward(args)...)); + std::pair emplace( + const Key &key, + Args&&... args) { + return this->insert(value_type( + key, + Type(std::forward(args)...))); + } + template + std::pair try_emplace( + const Key &key, + Args&&... args) { + if (this->empty() || compare()(key, this->front().first)) { + this->_impl.push_front(value_type( + key, + Type(std::forward(args)...))); + return { this->begin(), true }; + } else if (compare()(this->back().first, key)) { + this->_impl.push_back(value_type( + key, + Type(std::forward(args)...))); + return { this->end() - 1, true }; + } + auto where = this->getLowerBound(key); + if (compare()(key, where->first)) { + return { + this->_impl.insert( + where, + value_type( + key, + Type(std::forward(args)...))), + true + }; + } + return { where, false }; } bool remove(const Key &key) { diff --git a/Telegram/SourceFiles/base/flat_set.h b/Telegram/SourceFiles/base/flat_set.h index 184a0e0f1e..661350d52e 100644 --- a/Telegram/SourceFiles/base/flat_set.h +++ b/Telegram/SourceFiles/base/flat_set.h @@ -47,6 +47,13 @@ public: flat_multi_set_iterator_base_impl(iterator_impl impl = iterator_impl()) : _impl(impl) { } + template + flat_multi_set_iterator_base_impl( + const flat_multi_set_iterator_base_impl< + Type, + Compare, + other_iterator_impl> &other) : _impl(other._impl) { + } reference operator*() const { return *_impl; @@ -82,20 +89,40 @@ public: flat_multi_set_iterator_base_impl operator-(difference_type offset) const { return _impl - offset; } - difference_type operator-(const flat_multi_set_iterator_base_impl &right) const { + template + difference_type operator-( + const flat_multi_set_iterator_base_impl< + Type, + Compare, + other_iterator_impl> &right) const { return _impl - right._impl; } reference operator[](difference_type offset) const { return _impl[offset]; } - bool operator==(const flat_multi_set_iterator_base_impl &right) const { + template + bool operator==( + const flat_multi_set_iterator_base_impl< + Type, + Compare, + other_iterator_impl> &right) const { return _impl == right._impl; } - bool operator!=(const flat_multi_set_iterator_base_impl &right) const { + template + bool operator!=( + const flat_multi_set_iterator_base_impl< + Type, + Compare, + other_iterator_impl> &right) const { return _impl != right._impl; } - bool operator<(const flat_multi_set_iterator_base_impl &right) const { + template + bool operator<( + const flat_multi_set_iterator_base_impl< + Type, + Compare, + other_iterator_impl> &right) const { return _impl < right._impl; } @@ -104,6 +131,12 @@ private: friend class flat_multi_set; friend class flat_set; + template < + typename OtherType, + typename OtherCompare, + typename other_iterator_impl> + friend class flat_multi_set_iterator_base_impl; + Type &wrapped() { return _impl->wrapped(); } @@ -205,43 +238,24 @@ public: using pointer = const Type*; using reference = const Type&; - class const_iterator; class iterator : public iterator_base { public: using iterator_base::iterator_base; - iterator(const iterator_base &other) : iterator_base(other) { - } - friend class const_iterator; }; class const_iterator : public const_iterator_base { public: using const_iterator_base::const_iterator_base; - const_iterator(const_iterator_base other) : const_iterator_base(other) { - } - const_iterator(const iterator &other) : const_iterator_base(other._impl) { - } }; - class const_reverse_iterator; class reverse_iterator : public reverse_iterator_base { public: using reverse_iterator_base::reverse_iterator_base; - reverse_iterator(reverse_iterator_base other) - : reverse_iterator_base(other) { - } - friend class const_reverse_iterator; }; class const_reverse_iterator : public const_reverse_iterator_base { public: using const_reverse_iterator_base::const_reverse_iterator_base; - const_reverse_iterator(const_reverse_iterator_base other) - : const_reverse_iterator_base(other) { - } - const_reverse_iterator(const reverse_iterator &other) - : const_reverse_iterator_base(other._impl) { - } }; @@ -366,10 +380,10 @@ public: return (range.second - range.first); } - iterator erase(iterator where) { + iterator erase(const_iterator where) { return _impl.erase(where._impl); } - iterator erase(iterator from, iterator till) { + iterator erase(const_iterator from, const_iterator till) { return _impl.erase(from._impl, till._impl); } diff --git a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp index 7d42c76f53..a68bfa3ab9 100644 --- a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp @@ -894,7 +894,9 @@ void GifsListWidget::updateSelected() { } if (col < inlineItems.size()) { sel = row * MatrixRowShift + col; - inlineItems.at(col)->getState(lnk, cursor, QPoint(sx, sy)); + auto result = inlineItems[col]->getState(QPoint(sx, sy), HistoryStateRequest()); + lnk = result.link; + cursor = result.cursor; lnkhost = inlineItems.at(col); } else { row = col = -1; diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index c3aae7f8c2..f1129857bb 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -1033,10 +1033,11 @@ void HistoryInner::itemRemoved(not_null item) { void HistoryInner::mouseActionFinish(const QPoint &screenPos, Qt::MouseButton button) { mouseActionUpdate(screenPos); - ClickHandlerPtr activated = ClickHandler::unpressed(); + auto pressedLinkItem = App::pressedLinkItem(); + auto activated = ClickHandler::unpressed(); if (_mouseAction == MouseAction::Dragging) { activated.clear(); - } else if (auto pressed = App::pressedLinkItem()) { + } else if (auto pressed = 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.empty() && _selected.cbegin()->second == FullSelection && button != Qt::RightButton) { @@ -2249,23 +2250,21 @@ void HistoryInner::onUpdateSelected() { } } -void HistoryInner::updateDragSelection(HistoryItem *dragSelFrom, HistoryItem *dragSelTo, bool dragSelecting, bool force) { - if (_dragSelFrom != dragSelFrom || _dragSelTo != dragSelTo || _dragSelecting != dragSelecting) { - _dragSelFrom = dragSelFrom; - _dragSelTo = dragSelTo; - int32 fromy = itemTop(_dragSelFrom), toy = itemTop(_dragSelTo); - if (fromy >= 0 && toy >= 0 && fromy > toy) { - qSwap(_dragSelFrom, _dragSelTo); - } - _dragSelecting = dragSelecting; - if (!_wasSelectedText && _dragSelFrom && _dragSelTo && _dragSelecting) { - _wasSelectedText = true; - setFocus(); - } - force = true; +void HistoryInner::updateDragSelection(HistoryItem *dragSelFrom, HistoryItem *dragSelTo, bool dragSelecting) { + if (_dragSelFrom == dragSelFrom && _dragSelTo == dragSelTo && _dragSelecting == dragSelecting) { + return; + } + _dragSelFrom = dragSelFrom; + _dragSelTo = dragSelTo; + int32 fromy = itemTop(_dragSelFrom), toy = itemTop(_dragSelTo); + if (fromy >= 0 && toy >= 0 && fromy > toy) { + qSwap(_dragSelFrom, _dragSelTo); + } + _dragSelecting = dragSelecting; + if (!_wasSelectedText && _dragSelFrom && _dragSelTo && _dragSelecting) { + _wasSelectedText = true; + setFocus(); } - if (!force) return; - update(); } diff --git a/Telegram/SourceFiles/history/history_inner_widget.h b/Telegram/SourceFiles/history/history_inner_widget.h index a6caed83d2..3ac2020718 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.h +++ b/Telegram/SourceFiles/history/history_inner_widget.h @@ -164,7 +164,7 @@ private: void adjustCurrent(int32 y, History *history) const; HistoryItem *prevItem(HistoryItem *item); HistoryItem *nextItem(HistoryItem *item); - void updateDragSelection(HistoryItem *dragSelFrom, HistoryItem *dragSelTo, bool dragSelecting, bool force = false); + void updateDragSelection(HistoryItem *dragSelFrom, HistoryItem *dragSelTo, bool dragSelecting); void setToClipboard(const TextWithEntities &forClipboard, QClipboard::Mode mode = QClipboard::Clipboard); diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index ea7a6c76b1..7d557cf61c 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -91,6 +91,8 @@ struct HistoryTextState { symbol = state.symbol; return *this; } + HistoryTextState(ClickHandlerPtr link) : link(link) { + } HistoryCursorState cursor = HistoryDefaultCursorState; ClickHandlerPtr link; bool afterSymbol = false; @@ -656,11 +658,15 @@ public: return false; } - virtual HistoryTextState getState(QPoint point, HistoryStateRequest request) const WARN_UNUSED_RESULT = 0; + [[nodiscard]] virtual HistoryTextState getState( + QPoint point, + HistoryStateRequest request) const = 0; virtual void updatePressed(QPoint point) { } - virtual TextSelection adjustSelection(TextSelection selection, TextSelectType type) const WARN_UNUSED_RESULT { + [[nodiscard]] virtual TextSelection adjustSelection( + TextSelection selection, + TextSelectType type) const { return selection; } @@ -1051,10 +1057,12 @@ protected: return nullptr; } - TextSelection skipTextSelection(TextSelection selection) const WARN_UNUSED_RESULT { + [[nodiscard]] TextSelection skipTextSelection( + TextSelection selection) const { return internal::unshiftSelection(selection, _text); } - TextSelection unskipTextSelection(TextSelection selection) const WARN_UNUSED_RESULT { + [[nodiscard]] TextSelection unskipTextSelection( + TextSelection selection) const { return internal::shiftSelection(selection, _text); } diff --git a/Telegram/SourceFiles/history/history_media.h b/Telegram/SourceFiles/history/history_media.h index b106c016a6..f706b41f98 100644 --- a/Telegram/SourceFiles/history/history_media.h +++ b/Telegram/SourceFiles/history/history_media.h @@ -93,27 +93,37 @@ public: // if we are in selecting items mode perhaps we want to // toggle selection instead of activating the pressed link - virtual bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const = 0; + virtual bool toggleSelectionByHandlerClick( + const ClickHandlerPtr &p) const = 0; // if we press and drag on this media should we drag the item - virtual bool dragItem() const WARN_UNUSED_RESULT { + [[nodiscard]] virtual bool dragItem() const { return false; } - virtual TextSelection adjustSelection(TextSelection selection, TextSelectType type) const WARN_UNUSED_RESULT { + [[nodiscard]] virtual TextSelection adjustSelection( + TextSelection selection, + TextSelectType type) const { return selection; } - virtual bool consumeMessageText(const TextWithEntities &textWithEntities) WARN_UNUSED_RESULT { + [[nodiscard]] virtual bool consumeMessageText( + const TextWithEntities &textWithEntities) { return false; } - virtual uint16 fullSelectionLength() const WARN_UNUSED_RESULT { + [[nodiscard]] virtual uint16 fullSelectionLength() const { return 0; } - TextSelection skipSelection(TextSelection selection) const WARN_UNUSED_RESULT { - return internal::unshiftSelection(selection, fullSelectionLength()); + [[nodiscard]] TextSelection skipSelection( + TextSelection selection) const { + return internal::unshiftSelection( + selection, + fullSelectionLength()); } - TextSelection unskipSelection(TextSelection selection) const WARN_UNUSED_RESULT { - return internal::shiftSelection(selection, fullSelectionLength()); + [[nodiscard]] TextSelection unskipSelection( + TextSelection selection) const { + return internal::shiftSelection( + selection, + fullSelectionLength()); } // if we press and drag this link should we drag the item diff --git a/Telegram/SourceFiles/history/history_media_types.h b/Telegram/SourceFiles/history/history_media_types.h index 155ecb9482..a7d33a5c19 100644 --- a/Telegram/SourceFiles/history/history_media_types.h +++ b/Telegram/SourceFiles/history/history_media_types.h @@ -144,7 +144,9 @@ public: void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override; HistoryTextState getState(QPoint point, HistoryStateRequest request) const override; - TextSelection adjustSelection(TextSelection selection, TextSelectType type) const override WARN_UNUSED_RESULT { + [[nodiscard]] TextSelection adjustSelection( + TextSelection selection, + TextSelectType type) const override { return _caption.adjustSelection(selection, type); } uint16 fullSelectionLength() const override { @@ -230,7 +232,9 @@ public: void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override; HistoryTextState getState(QPoint point, HistoryStateRequest request) const override; - TextSelection adjustSelection(TextSelection selection, TextSelectType type) const override WARN_UNUSED_RESULT { + [[nodiscard]] TextSelection adjustSelection( + TextSelection selection, + TextSelectType type) const override { return _caption.adjustSelection(selection, type); } uint16 fullSelectionLength() const override { @@ -382,7 +386,9 @@ public: HistoryTextState getState(QPoint point, HistoryStateRequest request) const override; void updatePressed(QPoint point) override; - TextSelection adjustSelection(TextSelection selection, TextSelectType type) const override WARN_UNUSED_RESULT { + [[nodiscard]] TextSelection adjustSelection( + TextSelection selection, + TextSelectType type) const override { if (auto captioned = Get()) { return captioned->_caption.adjustSelection(selection, type); } @@ -495,7 +501,9 @@ public: void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override; HistoryTextState getState(QPoint point, HistoryStateRequest request) const override; - TextSelection adjustSelection(TextSelection selection, TextSelectType type) const override WARN_UNUSED_RESULT { + [[nodiscard]] TextSelection adjustSelection( + TextSelection selection, + TextSelectType type) const override { return _caption.adjustSelection(selection, type); } uint16 fullSelectionLength() const override { @@ -795,7 +803,9 @@ public: void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override; HistoryTextState getState(QPoint point, HistoryStateRequest request) const override; - TextSelection adjustSelection(TextSelection selection, TextSelectType type) const override WARN_UNUSED_RESULT; + [[nodiscard]] TextSelection adjustSelection( + TextSelection selection, + TextSelectType type) const override; uint16 fullSelectionLength() const override { return _title.length() + _description.length(); } @@ -902,7 +912,9 @@ public: void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override; HistoryTextState getState(QPoint point, HistoryStateRequest request) const override; - TextSelection adjustSelection(TextSelection selection, TextSelectType type) const override WARN_UNUSED_RESULT; + [[nodiscard]] TextSelection adjustSelection( + TextSelection selection, + TextSelectType type) const override; uint16 fullSelectionLength() const override { return _title.length() + _description.length(); } @@ -1016,7 +1028,9 @@ public: void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override; HistoryTextState getState(QPoint point, HistoryStateRequest request) const override; - TextSelection adjustSelection(TextSelection selection, TextSelectType type) const override WARN_UNUSED_RESULT; + [[nodiscard]] TextSelection adjustSelection( + TextSelection selection, + TextSelectType type) const override; uint16 fullSelectionLength() const override { return _title.length() + _description.length(); } @@ -1102,7 +1116,9 @@ public: void draw(Painter &p, const QRect &r, TextSelection selection, TimeMs ms) const override; HistoryTextState getState(QPoint point, HistoryStateRequest request) const override; - TextSelection adjustSelection(TextSelection selection, TextSelectType type) const override WARN_UNUSED_RESULT; + [[nodiscard]] TextSelection adjustSelection( + TextSelection selection, + TextSelectType type) const override; uint16 fullSelectionLength() const override { return _title.length() + _description.length(); } diff --git a/Telegram/SourceFiles/history/history_service.h b/Telegram/SourceFiles/history/history_service.h index a1a6bbb80b..84223c5090 100644 --- a/Telegram/SourceFiles/history/history_service.h +++ b/Telegram/SourceFiles/history/history_service.h @@ -88,7 +88,9 @@ public: bool hasPoint(QPoint point) const override; HistoryTextState getState(QPoint point, HistoryStateRequest request) const override; - TextSelection adjustSelection(TextSelection selection, TextSelectType type) const override WARN_UNUSED_RESULT { + [[nodiscard]] TextSelection adjustSelection( + TextSelection selection, + TextSelectType type) const override { return _text.adjustSelection(selection, type); } diff --git a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp index 3d6d06b99b..958c133824 100644 --- a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp +++ b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp @@ -59,11 +59,12 @@ UniversalMsgId GetUniversalId(not_null layout) { } // namespace -ListWidget::CachedItem::CachedItem(std::unique_ptr item) -: item(std::move(item)) { -} - -ListWidget::CachedItem::~CachedItem() = default; +struct ListWidget::Context { + Layout::PaintContext layoutContext; + not_null selected; + not_null dragSelected; + DragSelectAction dragSelectAction; +}; class ListWidget::Section { public: @@ -101,9 +102,9 @@ public: void paint( Painter &p, + const Context &context, QRect clip, - int outerWidth, - TimeMs ms) const; + int outerWidth) const; static int MinItemHeight(Type type, int width); @@ -121,10 +122,13 @@ private: Items::const_iterator findItemAfterBottom( Items::const_iterator from, int bottom) const; - QRect findItemRect(not_null item) const; + QRect findItemRect(not_null item) const; FoundItem completeResult( not_null item, bool exact) const; + TextSelection itemSelection( + not_null item, + const Context &context) const; int recountHeight() const; void refreshHeight(); @@ -142,6 +146,45 @@ private: }; +bool ListWidget::IsAfter( + const CursorState &a, + const CursorState &b) { + if (a.itemId != b.itemId) { + return (a.itemId < b.itemId); + } + auto xAfter = a.cursor.x() - b.cursor.x(); + auto yAfter = a.cursor.y() - b.cursor.y(); + return (xAfter + yAfter >= 0); +} + +bool ListWidget::SkipSelectFromItem(const CursorState &state) { + if (state.cursor.y() >= state.size.height() + && state.cursor.x() >= 0) { + return true; + } else if (state.cursor.x() >= state.size.width() + && state.cursor.y() >= 0) { + return true; + } + return false; +} + +bool ListWidget::SkipSelectTillItem(const CursorState &state) { + if (state.cursor.y() < state.size.height() + && state.cursor.x() < 0) { + return true; + } else if (state.cursor.x() < state.size.width() + && state.cursor.y() < 0) { + return true; + } + return false; +} + +ListWidget::CachedItem::CachedItem(std::unique_ptr item) +: item(std::move(item)) { +} + +ListWidget::CachedItem::~CachedItem() = default; + bool ListWidget::Section::addItem(not_null item) { if (_items.empty() || belongsHere(item)) { if (_items.empty()) setHeader(item); @@ -213,7 +256,7 @@ bool ListWidget::Section::removeItem(UniversalMsgId universalId) { } QRect ListWidget::Section::findItemRect( - not_null item) const { + not_null item) const { auto position = item->position(); auto top = position / _itemsInRow; auto indexInRow = position % _itemsInRow; @@ -232,19 +275,26 @@ auto ListWidget::Section::findItemByPoint( QPoint point) const -> FoundItem { Expects(!_items.empty()); auto itemIt = findItemAfterTop(point.y()); - auto shift = floorclamp( - point.x(), - (_itemWidth + st::infoMediaSkip), - 0, - _itemsInRow); - while (shift-- && itemIt != _items.end()) { - ++itemIt; - } if (itemIt == _items.end()) { --itemIt; } auto item = itemIt->second; auto rect = findItemRect(item); + if (point.y() >= rect.top()) { + auto shift = floorclamp( + point.x(), + (_itemWidth + st::infoMediaSkip), + 0, + _itemsInRow); + while (shift-- && itemIt != _items.end()) { + ++itemIt; + } + if (itemIt == _items.end()) { + --itemIt; + } + item = itemIt->second; + rect = findItemRect(item); + } return { item, rect, rect.contains(point) }; } @@ -302,9 +352,9 @@ auto ListWidget::Section::findItemAfterBottom( void ListWidget::Section::paint( Painter &p, + const Context &context, QRect clip, - int outerWidth, - TimeMs ms) const { + int outerWidth) const { auto baseIndex = 0; auto header = headerHeight(); if (QRect(0, 0, outerWidth, header).intersects(clip)) { @@ -327,8 +377,8 @@ void ListWidget::Section::paint( _itemWidth, 0, _itemsInRow); - Layout::PaintContext context(ms, false); - context.isAfterDate = (header > 0); + auto localContext = context.layoutContext; + localContext.isAfterDate = (header > 0); auto fromIt = findItemAfterTop(clip.y()); auto tillIt = findItemAfterBottom( @@ -337,20 +387,39 @@ void ListWidget::Section::paint( for (auto it = fromIt; it != tillIt; ++it) { auto item = it->second; auto rect = findItemRect(item); - context.isAfterDate = (header > 0) + localContext.isAfterDate = (header > 0) && (rect.y() <= header + _itemsTop); if (rect.intersects(clip)) { p.translate(rect.topLeft()); item->paint( p, clip.translated(-rect.topLeft()), - TextSelection(), - &context); + itemSelection(item, context), + &localContext); p.translate(-rect.topLeft()); } } } +TextSelection ListWidget::Section::itemSelection( + not_null item, + const Context &context) const { + auto universalId = GetUniversalId(item); + auto dragSelectAction = context.dragSelectAction; + if (dragSelectAction != DragSelectAction::None) { + auto i = context.dragSelected->find(universalId); + if (i != context.dragSelected->end()) { + return (dragSelectAction == DragSelectAction::Selecting) + ? FullSelection + : TextSelection(); + } + } + auto i = context.selected->find(universalId); + return (i == context.selected->cend()) + ? TextSelection() + : i->second.text; +} + int ListWidget::Section::headerHeight() const { return _header.isEmpty() ? 0 : st::infoMediaHeaderHeight; } @@ -510,12 +579,6 @@ void ListWidget::itemRemoved(not_null item) { if (isMyItem(item)) { auto universalId = GetUniversalId(item); - auto i = _selected.find(universalId); - if (i != _selected.cend()) { - _selected.erase(i); - // updateSelectedCounters(); - } - auto sectionIt = findSectionByItem(universalId); if (sectionIt != _sections.end()) { if (sectionIt->removeItem(universalId)) { @@ -527,22 +590,92 @@ void ListWidget::itemRemoved(not_null item) { } } - if (isItemLayout(item, _dragSelFrom) - || isItemLayout(item, _dragSelTo)) { - _dragSelFrom = _dragSelTo = nullptr; - update(); + if (isItemLayout(item, _overLayout)) { + _overLayout = nullptr; } _layouts.erase(universalId); - mouseActionUpdate(QCursor::pos()); + _dragSelected.remove(universalId); + + auto i = _selected.find(universalId); + if (i != _selected.cend()) { + removeItemSelection(i); + pushSelectedItems(); + } + + mouseActionUpdate(_mousePosition); } } +FullMsgId ListWidget::computeFullId( + UniversalMsgId universalId) const { + Expects(universalId != 0); + auto peerChannel = [&] { + return _peer->isChannel() ? _peer->bareId() : NoChannel; + }; + return (universalId > 0) + ? FullMsgId(peerChannel(), universalId) + : FullMsgId(NoChannel, ServerMaxMsgId + universalId); +} + +auto ListWidget::collectSelectedItems() const -> SelectedItems { + auto convert = [&]( + UniversalMsgId universalId, + const SelectionData &selection) { + auto result = SelectedItem(computeFullId(universalId)); + result.canDelete = selection.canDelete; + result.canForward = selection.canForward; + return result; + }; + auto transformation = [&](const auto &item) { + return convert(item.first, item.second); + }; + auto items = SelectedItems(); + items.reserve(_selected.size()); + std::transform( + _selected.begin(), + _selected.end(), + std::back_inserter(items), + transformation); + return items; +} + +void ListWidget::pushSelectedItems() { + _selectedItemsStream.fire(collectSelectedItems()); +} + +bool ListWidget::hasSelected() const { + return !_selected.empty(); +} + +bool ListWidget::isSelectedItem( + const SelectedMap::const_iterator &i) const { + return (i != _selected.end()) + && (i->second.text == FullSelection); +} + +void ListWidget::removeItemSelection( + const SelectedMap::const_iterator &i) { + Expects(i != _selected.cend()); + _selected.erase(i); + if (_selected.empty()) { + update(); + } +} + +bool ListWidget::hasSelectedText() const { + return hasSelected() + && !hasSelectedItems(); +} + +bool ListWidget::hasSelectedItems() const { + return isSelectedItem(_selected.cbegin()); +} + void ListWidget::itemLayoutChanged( not_null item) { - if (isItemLayout(item, _itemNearestToCursor) - || isItemLayout(item, _itemUnderCursor)) { - updateSelected(); + if (isItemLayout(item, _overLayout)) { + mouseActionUpdate(); } } @@ -554,7 +687,7 @@ void ListWidget::repaintItem(const HistoryItem *item) { void ListWidget::repaintItem(UniversalMsgId universalId) { if (auto item = findItemById(universalId)) { - rtlupdate(item->geometry); + repaintItem(item->geometry); } } @@ -564,6 +697,10 @@ void ListWidget::repaintItem(const BaseLayout *item) { } } +void ListWidget::repaintItem(QRect itemGeometry) { + rtlupdate(itemGeometry); +} + bool ListWidget::isMyItem(not_null item) const { auto peer = item->history()->peer; return (_peer == peer || _peer == peer->migrateTo()); @@ -610,11 +747,10 @@ void ListWidget::refreshViewer() { }, _viewerLifetime); } -BaseLayout *ListWidget::getLayout(const FullMsgId &itemId) { - auto universalId = GetUniversalId(itemId); +BaseLayout *ListWidget::getLayout(UniversalMsgId universalId) { auto it = _layouts.find(universalId); if (it == _layouts.end()) { - if (auto layout = createLayout(itemId, _type)) { + if (auto layout = createLayout(universalId, _type)) { layout->initDimensions(); it = _layouts.emplace( universalId, @@ -628,8 +764,7 @@ BaseLayout *ListWidget::getLayout(const FullMsgId &itemId) { } BaseLayout *ListWidget::getExistingLayout( - const FullMsgId &itemId) const { - auto universalId = GetUniversalId(itemId); + UniversalMsgId universalId) const { auto it = _layouts.find(universalId); return (it != _layouts.end()) ? it->second.item.get() @@ -637,9 +772,9 @@ BaseLayout *ListWidget::getExistingLayout( } std::unique_ptr ListWidget::createLayout( - const FullMsgId &itemId, + UniversalMsgId universalId, Type type) { - auto item = App::histItemById(itemId); + auto item = App::histItemById(computeFullId(universalId)); if (!item) { return nullptr; } @@ -703,8 +838,8 @@ void ListWidget::refreshRows() { auto section = Section(_type); auto count = _slice.size(); for (auto i = count; i != 0;) { - auto itemId = _slice[--i]; - if (auto layout = getLayout(itemId)) { + auto universalId = GetUniversalId(_slice[--i]); + if (auto layout = getLayout(universalId)) { if (!section.addItem(layout)) { _sections.push_back(std::move(section)); section = Section(_type); @@ -781,16 +916,23 @@ auto ListWidget::foundItemInSection( void ListWidget::visibleTopBottomUpdated( int visibleTop, int visibleBottom) { - auto visibleHeight = (visibleBottom - visibleTop); - if (width() <= 0 || visibleHeight <= 0 || _sections.empty() || _scrollTopId) { - return; - } - _visibleTop = visibleTop; _visibleBottom = visibleBottom; - auto topItem = findItemByPoint({ 0, visibleTop }); - auto bottomItem = findItemByPoint({ 0, visibleBottom }); + checkMoveToOtherViewer(); +} + +void ListWidget::checkMoveToOtherViewer() { + auto visibleHeight = (_visibleBottom - _visibleTop); + if (width() <= 0 + || visibleHeight <= 0 + || _sections.empty() + || _scrollTopId) { + return; + } + + auto topItem = findItemByPoint({ 0, _visibleTop }); + auto bottomItem = findItemByPoint({ 0, _visibleBottom }); auto preloadedHeight = kPreloadedScreensCountFull * visibleHeight; auto minItemHeight = Section::MinItemHeight(_type, width()); @@ -801,10 +943,10 @@ void ListWidget::visibleTopBottomUpdated( auto preloadBefore = kPreloadIfLessThanScreens * visibleHeight; auto after = _slice.skippedAfter(); - auto preloadTop = (visibleTop < preloadBefore); + auto preloadTop = (_visibleTop < preloadBefore); auto topLoaded = after && (*after == 0); auto before = _slice.skippedBefore(); - auto preloadBottom = (height() - visibleBottom < preloadBefore); + auto preloadBottom = (height() - _visibleBottom < preloadBefore); auto bottomLoaded = before && (*before == 0); auto minScreenDelta = kPreloadedScreensCount @@ -882,10 +1024,16 @@ void ListWidget::paintEvent(QPaintEvent *e) { auto tillSectionIt = findSectionAfterBottom( fromSectionIt, clip.y() + clip.height()); + auto context = Context { + Layout::PaintContext(ms, !_selected.empty()), + &_selected, + &_dragSelected, + _dragSelectAction + }; for (auto it = fromSectionIt; it != tillSectionIt; ++it) { auto top = it->top(); p.translate(0, top); - it->paint(p, clip.translated(0, -top), outerWidth, ms); + it->paint(p, context, clip.translated(0, -top), outerWidth); p.translate(0, -top); } } @@ -915,38 +1063,159 @@ void ListWidget::mouseReleaseEvent(QMouseEvent *e) { void ListWidget::mouseDoubleClickEvent(QMouseEvent *e) { mouseActionStart(e->globalPos(), e->button()); + trySwitchToWordSelection(); +} - //auto selectingSome = (_mouseAction == MouseAction::Selecting) - // && !_selected.empty() - // && (_selected.cbegin()->second != FullSelection); - //auto willSelectSome = (_mouseAction == MouseAction::None) - // && (_selected.empty() - // || _selected.cbegin()->second != FullSelection); - //auto checkSwitchToWordSelection = _itemUnderPress - // && (_mouseSelectType == TextSelectType::Letters) - // && (selectingSome || willSelectSome); - //if (checkSwitchToWordSelection) { - // HistoryStateRequest request; - // request.flags |= Text::StateRequest::Flag::LookupSymbol; - // auto dragState = _itemUnderPress->getState(_dragStartPosition, request); - // if (dragState.cursor == HistoryInTextCursorState) { - // _mouseTextSymbol = dragState.symbol; - // _mouseSelectType = TextSelectType::Words; - // if (_mouseAction == MouseAction::None) { - // _mouseAction = MouseAction::Selecting; - // TextSelection selStatus = { dragState.symbol, dragState.symbol }; - // if (!_selected.empty()) { - // repaintItem(_selected.cbegin()->first); - // _selected.clear(); - // } - // _selected.emplace(_itemUnderPress, selStatus); - // } - // mouseMoveEvent(e); +void ListWidget::trySwitchToWordSelection() { + auto selectingSome = (_mouseAction == MouseAction::Selecting) + && hasSelectedText(); + auto willSelectSome = (_mouseAction == MouseAction::None) + && !hasSelectedItems(); + auto checkSwitchToWordSelection = _overLayout + && (_mouseSelectType == TextSelectType::Letters) + && (selectingSome || willSelectSome); + if (checkSwitchToWordSelection) { + switchToWordSelection(); + } +} - // _trippleClickPoint = e->globalPos(); - // _trippleClickTimer.start(QApplication::doubleClickInterval()); - // } - //} +void ListWidget::switchToWordSelection() { + Expects(_overLayout != nullptr); + + HistoryStateRequest request; + request.flags |= Text::StateRequest::Flag::LookupSymbol; + auto dragState = _overLayout->getState(_pressState.cursor, request); + if (dragState.cursor != HistoryInTextCursorState) { + return; + } + _mouseTextSymbol = dragState.symbol; + _mouseSelectType = TextSelectType::Words; + if (_mouseAction == MouseAction::None) { + _mouseAction = MouseAction::Selecting; + clearSelected(); + auto selStatus = TextSelection { + dragState.symbol, + dragState.symbol + }; + applyItemSelection(_overState.itemId, selStatus); + } + mouseActionUpdate(); + + _trippleClickPoint = _mousePosition; + _trippleClickStartTime = getms(); +} + +void ListWidget::applyItemSelection( + UniversalMsgId universalId, + TextSelection selection) { + if (changeItemSelection( + _selected, + universalId, + selection)) { + repaintItem(universalId); + } +} + +void ListWidget::toggleItemSelection(UniversalMsgId universalId) { + auto it = _selected.find(universalId); + if (it == _selected.cend()) { + applyItemSelection(universalId, FullSelection); + } else { + removeItemSelection(it); + } +} + +bool ListWidget::changeItemSelection( + SelectedMap &selected, + UniversalMsgId universalId, + TextSelection selection) const { + auto changeExisting = [&](auto it) { + if (it == selected.cend()) { + return false; + } else if (it->second.text != selection) { + it->second.text = selection; + return true; + } + return false; + }; + if (selected.size() < MaxSelectedItems) { + auto [iterator, ok] = selected.try_emplace( + universalId, + selection); + if (ok) { + auto item = App::histItemById(computeFullId(universalId)); + if (!item) { + selected.erase(iterator); + return false; + } + iterator->second.canDelete = item->canDelete(); + iterator->second.canForward = item->canForward(); + return true; + } + return changeExisting(iterator); + } + return changeExisting(selected.find(universalId)); +} + +bool ListWidget::isItemUnderPressSelected() const { + return itemUnderPressSelection() != _selected.end(); +} + +auto ListWidget::itemUnderPressSelection() -> SelectedMap::iterator { + return (_pressState.itemId && _pressState.inside) + ? _selected.find(_pressState.itemId) + : _selected.end(); +} + +auto ListWidget::itemUnderPressSelection() const +-> SelectedMap::const_iterator { + return (_pressState.itemId && _pressState.inside) + ? _selected.find(_pressState.itemId) + : _selected.end(); +} + +bool ListWidget::requiredToStartDragging( + not_null layout) const { + if (_mouseCursorState == HistoryInDateCursorState) { + return true; + } +// return dynamic_cast(layout->getMedia()); + return false; +} + +bool ListWidget::isPressInSelectedText(HistoryTextState state) const { + if (state.cursor != HistoryInTextCursorState) { + return false; + } + if (!hasSelectedText() + || !isItemUnderPressSelected()) { + return false; + } + auto pressedSelection = itemUnderPressSelection(); + auto from = pressedSelection->second.text.from; + auto to = pressedSelection->second.text.to; + return (state.symbol >= from && state.symbol < to); +} + +void ListWidget::clearSelected() { + if (_selected.empty()) { + return; + } + if (hasSelectedText()) { + repaintItem(_selected.begin()->first); + } else { + update(); + } + _selected.clear(); +} + +void ListWidget::validateTrippleClickStartTime() { + if (_trippleClickStartTime) { + auto elapsed = (getms() - _trippleClickStartTime); + if (elapsed >= QApplication::doubleClickInterval()) { + _trippleClickStartTime = 0; + } + } } void ListWidget::enterEventHook(QEvent *e) { @@ -955,9 +1224,11 @@ void ListWidget::enterEventHook(QEvent *e) { } void ListWidget::leaveEventHook(QEvent *e) { - if (auto item = _itemUnderCursor) { - repaintItem(item); - _itemUnderCursor = nullptr; + if (auto item = _overLayout) { + if (_overState.inside) { + repaintItem(item); + _overState.inside = false; + } } ClickHandler::clearActive(); if (!ClickHandler::getPressed() && _cursor != style::cur_default) { @@ -984,26 +1255,35 @@ void ListWidget::mouseActionUpdate(const QPoint &screenPos) { auto local = mapFromGlobal(_mousePosition); auto point = clampMousePosition(local); auto [layout, geometry, inside] = findItemByPoint(point); + auto state = CursorState { + GetUniversalId(layout), + geometry.size(), + point - geometry.topLeft(), + inside + }; auto item = layout ? layout->getItem() : nullptr; - auto relative = point - geometry.topLeft(); - if (inside) { - if (_itemUnderCursor != layout) { - repaintItem(_itemUnderCursor); - _itemUnderCursor = layout; - repaintItem(_itemUnderCursor); - } - } else if (_itemUnderCursor) { - repaintItem(_itemUnderCursor); - _itemUnderCursor = nullptr; + if (_overLayout != layout) { + repaintItem(_overLayout); + _overLayout = layout; + repaintItem(geometry); } + _overState = state; - ClickHandlerPtr dragStateHandler; - HistoryCursorState dragStateCursor = HistoryDefaultCursorState; HistoryTextState dragState; ClickHandlerHost *lnkhost = nullptr; - bool selectingText = (layout == _itemUnderPress && layout == _itemUnderCursor && !_selected.empty() && _selected.cbegin()->second != FullSelection); - if (layout) { - if (layout != _itemUnderPress || (relative - _dragStartPosition).manhattanLength() >= QApplication::startDragDistance()) { + auto inTextSelection = _overState.inside + && (_overState.itemId == _pressState.itemId) + && hasSelectedText(); + auto cursorDeltaLength = [&] { + auto cursorDelta = (_overState.cursor - _pressState.cursor); + return cursorDelta.manhattanLength(); + }; + auto dragStartLength = [] { + return QApplication::startDragDistance(); + }; + if (_overLayout) { + if (_overState.itemId != _pressState.itemId + || cursorDeltaLength() >= dragStartLength()) { if (_mouseAction == MouseAction::PrepareDrag) { _mouseAction = MouseAction::Dragging; InvokeQueued(this, [this] { performDrag(); }); @@ -1015,112 +1295,127 @@ void ListWidget::mouseActionUpdate(const QPoint &screenPos) { if (_mouseAction == MouseAction::Selecting) { request.flags |= Text::StateRequest::Flag::LookupSymbol; } else { - selectingText = false; + inTextSelection = false; } - //dragState = layout->getState(relative, request); - layout->getState(dragState.link, dragState.cursor, relative); - lnkhost = layout; + dragState = _overLayout->getState(_overState.cursor, request); + lnkhost = _overLayout; } - auto lnkChanged = ClickHandler::setActive(dragState.link, lnkhost); + ClickHandler::setActive(dragState.link, lnkhost); - Qt::CursorShape cur = style::cur_default; if (_mouseAction == MouseAction::None) { _mouseCursorState = dragState.cursor; - if (dragState.link) { - cur = style::cur_pointer; - } else if (_mouseCursorState == HistoryInTextCursorState && (_selected.empty() || _selected.cbegin()->second != FullSelection)) { - cur = style::cur_text; - } else if (_mouseCursorState == HistoryInDateCursorState) { - // cur = style::cur_cross; + auto cursor = computeMouseCursor(); + if (_cursor != cursor) { + setCursor(_cursor = cursor); } - } else if (item) { - if (_mouseAction == MouseAction::Selecting) { - //auto canSelectMany = (_history != nullptr); - //if (selectingText) { - // uint16 second = dragState.symbol; - // if (dragState.afterSymbol && _mouseSelectType == TextSelectType::Letters) { - // ++second; - // } - // auto selState = TextSelection { qMin(second, _mouseTextSymbol), qMax(second, _mouseTextSymbol) }; - // if (_mouseSelectType != TextSelectType::Letters) { - // selState = _itemUnderPress->adjustSelection(selState, _mouseSelectType); - // } - // if (_selected[_itemUnderPress] != selState) { - // _selected[_itemUnderPress] = selState; - // repaintItem(_itemUnderPress); - // } - // if (!_wasSelectedText && (selState == FullSelection || selState.from != selState.to)) { - // _wasSelectedText = true; - // setFocus(); - // } - // updateDragSelection(0, 0, false); - //} else if (canSelectMany) { - // auto selectingDown = (itemTop(_itemUnderPress) < itemTop(item)) || (_itemUnderPress == item && _dragStartPosition.y() < m.y()); - // auto dragSelFrom = _itemUnderPress, dragSelTo = item; - // if (!dragSelFrom->hasPoint(_dragStartPosition)) { // maybe exclude dragSelFrom - // if (selectingDown) { - // if (_dragStartPosition.y() >= dragSelFrom->height() - dragSelFrom->marginBottom() || ((item == dragSelFrom) && (m.y() < _dragStartPosition.y() + QApplication::startDragDistance() || m.y() < dragSelFrom->marginTop()))) { - // dragSelFrom = (dragSelFrom == dragSelTo) ? 0 : nextItem(dragSelFrom); - // } - // } else { - // if (_dragStartPosition.y() < dragSelFrom->marginTop() || ((item == dragSelFrom) && (m.y() >= _dragStartPosition.y() - QApplication::startDragDistance() || m.y() >= dragSelFrom->height() - dragSelFrom->marginBottom()))) { - // dragSelFrom = (dragSelFrom == dragSelTo) ? 0 : prevItem(dragSelFrom); - // } - // } - // } - // if (_itemUnderPress != item) { // maybe exclude dragSelTo - // if (selectingDown) { - // if (m.y() < dragSelTo->marginTop()) { - // dragSelTo = (dragSelFrom == dragSelTo) ? 0 : prevItem(dragSelTo); - // } - // } else { - // if (m.y() >= dragSelTo->height() - dragSelTo->marginBottom()) { - // dragSelTo = (dragSelFrom == dragSelTo) ? 0 : nextItem(dragSelTo); - // } - // } - // } - // auto dragSelecting = false; - // auto dragFirstAffected = dragSelFrom; - // while (dragFirstAffected && (dragFirstAffected->id < 0 || dragFirstAffected->serviceMsg())) { - // dragFirstAffected = (dragFirstAffected == dragSelTo) ? 0 : (selectingDown ? nextItem(dragFirstAffected) : prevItem(dragFirstAffected)); - // } - // if (dragFirstAffected) { - // auto i = _selected.find(dragFirstAffected); - // dragSelecting = (i == _selected.cend() || i->second != FullSelection); - // } - // updateDragSelection(dragSelFrom, dragSelTo, dragSelecting); - //} - } else if (_mouseAction == MouseAction::Dragging) { - } - - if (ClickHandler::getPressed()) { - cur = style::cur_pointer; - } else if (_mouseAction == MouseAction::Selecting && !_selected.empty() && _selected.cbegin()->second != FullSelection) { - if (!_dragSelFrom || !_dragSelTo) { - cur = style::cur_text; + } else if (_mouseAction == MouseAction::Selecting) { + if (inTextSelection) { + auto second = dragState.symbol; + if (dragState.afterSymbol && _mouseSelectType == TextSelectType::Letters) { + ++second; } + auto selState = TextSelection { + qMin(second, _mouseTextSymbol), + qMax(second, _mouseTextSymbol) + }; + if (_mouseSelectType != TextSelectType::Letters) { + selState = _overLayout->adjustSelection(selState, _mouseSelectType); + } + applyItemSelection(_overState.itemId, selState); + auto hasSelection = (selState == FullSelection) + || (selState.from != selState.to); + if (!_wasSelectedText && hasSelection) { + _wasSelectedText = true; + setFocus(); + } + clearDragSelection(); + } else if (_pressState.itemId) { + updateDragSelection(); } + } else if (_mouseAction == MouseAction::Dragging) { } - // Voice message seek support. - //if (auto pressedItem = App::pressedLinkItem()) { - // if (!pressedItem->detached()) { - // if (pressedItem->history() == _history || pressedItem->history() == _migrated) { - // auto adjustedPoint = mapPointToItem(point, pressedItem); - // pressedItem->updatePressed(adjustedPoint); - // } - // } - //} - + // #TODO scroll by drag //if (_mouseAction == MouseAction::Selecting) { // _widget->checkSelectingScroll(mousePos); //} else { - // updateDragSelection(0, 0, false); + // clearDragSelection(); // _widget->noSelectingScroll(); //} +} - if (_mouseAction == MouseAction::None && (lnkChanged || cur != _cursor)) { - setCursor(_cursor = cur); +style::cursor ListWidget::computeMouseCursor() const { + if (ClickHandler::getPressed() || ClickHandler::getActive()) { + return style::cur_pointer; + } else if (!hasSelectedItems() + && (_mouseCursorState == HistoryInTextCursorState)) { + return style::cur_text; + } + return style::cur_default; +} + +void ListWidget::updateDragSelection() { + auto fromState = _pressState; + auto tillState = _overState; + auto swapStates = IsAfter(fromState, tillState); + if (swapStates) { + std::swap(fromState, tillState); + } + if (!fromState.itemId || !tillState.itemId) { + clearDragSelection(); + return; + } + auto fromId = SkipSelectFromItem(fromState) + ? (fromState.itemId - 1) + : fromState.itemId; + auto tillId = SkipSelectTillItem(tillState) + ? tillState.itemId + : (tillState.itemId - 1); + for (auto i = _dragSelected.begin(); i != _dragSelected.end();) { + auto itemId = i->first; + if (itemId > fromId || itemId <= tillId) { + i = _dragSelected.erase(i); + } else { + ++i; + } + } + for (auto &layoutItem : _layouts) { + auto &&universalId = layoutItem.first; + auto &&layout = layoutItem.second; + if (universalId <= fromId && universalId > tillId) { + changeItemSelection( + _dragSelected, + universalId, + FullSelection); + } + } + _dragSelectAction = [&] { + if (_dragSelected.empty()) { + return DragSelectAction::None; + } + auto &[firstDragItem, data] = swapStates + ? _dragSelected.front() + : _dragSelected.back(); + if (isSelectedItem(_selected.find(firstDragItem))) { + return DragSelectAction::Deselecting; + } else { + return DragSelectAction::Selecting; + } + }(); + if (!_wasSelectedText + && !_dragSelected.empty() + && _dragSelectAction == DragSelectAction::Selecting) { + _wasSelectedText = true; + setFocus(); + } + update(); +} + +void ListWidget::clearDragSelection() { + _dragSelectAction = DragSelectAction::None; + if (!_dragSelected.empty()) { + _dragSelected.clear(); + update(); } } @@ -1129,144 +1424,111 @@ void ListWidget::mouseActionStart(const QPoint &screenPos, Qt::MouseButton butto if (button != Qt::LeftButton) return; ClickHandler::pressed(); - if (_itemUnderPress != _itemUnderCursor) { - repaintItem(_itemUnderPress); - _itemUnderPress = _itemUnderCursor; - repaintItem(_itemUnderPress); + if (_pressState != _overState) { + if (_pressState.itemId != _overState.itemId) { + repaintItem(_pressState.itemId); + } + _pressState = _overState; + repaintItem(_overLayout); } + auto pressLayout = _overLayout; _mouseAction = MouseAction::None; - if (auto item = findItemDetails(_itemUnderPress)) { - _dragStartPosition = mapFromGlobal(screenPos) - item->geometry.topLeft(); - } else { - _dragStartPosition = QPoint(); - } _pressWasInactive = _controller->window()->wasInactivePress(); if (_pressWasInactive) _controller->window()->setInactivePress(false); if (ClickHandler::getPressed()) { _mouseAction = MouseAction::PrepareDrag; - } else if (!_selected.empty()) { - if (_selected.cbegin()->second == FullSelection) { - //if (_selected.find(_itemUnderPress) != _selected.cend() && _itemUnderCursor) { - // _mouseAction = MouseAction::PrepareDrag; // start items drag - //} else if (!_pressWasInactive) { - // _mouseAction = MouseAction::PrepareSelect; // start items select - //} + } else if (hasSelectedItems()) { + if (isItemUnderPressSelected()) { + _mouseAction = MouseAction::PrepareDrag; // start items drag + } else if (!_pressWasInactive) { + _mouseAction = MouseAction::PrepareSelect; // start items select } } - if (_mouseAction == MouseAction::None && _itemUnderPress) { + if (_mouseAction == MouseAction::None && pressLayout) { HistoryTextState dragState; - if (_trippleClickTimer.isActive() && (screenPos - _trippleClickPoint).manhattanLength() < QApplication::startDragDistance()) { - //HistoryStateRequest request; - //request.flags = Text::StateRequest::Flag::LookupSymbol; - //dragState = _itemUnderPress->getState(_dragStartPosition, request); - //if (dragState.cursor == HistoryInTextCursorState) { - // TextSelection selStatus = { dragState.symbol, dragState.symbol }; - // if (selStatus != FullSelection && (_selected.empty() || _selected.cbegin()->second != FullSelection)) { - // if (!_selected.empty()) { - // repaintItem(_selected.cbegin()->first); - // _selected.clear(); - // } - // _selected.emplace(_itemUnderPress, selStatus); - // _mouseTextSymbol = dragState.symbol; - // _mouseAction = MouseAction::Selecting; - // _mouseSelectType = TextSelectType::Paragraphs; - // mouseActionUpdate(_mousePosition); - // _trippleClickTimer.start(QApplication::doubleClickInterval()); - // } - //} - } else if (_itemUnderPress) { + validateTrippleClickStartTime(); + auto startDistance = (screenPos - _trippleClickPoint).manhattanLength(); + auto validStartPoint = startDistance < QApplication::startDragDistance(); + if (_trippleClickStartTime != 0 && validStartPoint) { HistoryStateRequest request; request.flags = Text::StateRequest::Flag::LookupSymbol; -// dragState = _itemUnderPress->getState(_dragStartPosition, request); - _itemUnderPress->getState(dragState.link, dragState.cursor, _dragStartPosition); + dragState = pressLayout->getState(_pressState.cursor, request); + if (dragState.cursor == HistoryInTextCursorState) { + TextSelection selStatus = { dragState.symbol, dragState.symbol }; + if (selStatus != FullSelection && !hasSelectedItems()) { + clearSelected(); + applyItemSelection(_pressState.itemId, selStatus); + _mouseTextSymbol = dragState.symbol; + _mouseAction = MouseAction::Selecting; + _mouseSelectType = TextSelectType::Paragraphs; + mouseActionUpdate(_mousePosition); + _trippleClickStartTime = getms(); + } + } + } else { + HistoryStateRequest request; + request.flags = Text::StateRequest::Flag::LookupSymbol; + dragState = pressLayout->getState(_pressState.cursor, request); } if (_mouseSelectType != TextSelectType::Paragraphs) { - if (_itemUnderPress) { - //_mouseTextSymbol = dragState.symbol; - //bool uponSelected = (dragState.cursor == HistoryInTextCursorState); - //if (uponSelected) { - // if (_selected.empty() - // || _selected.cbegin()->second == FullSelection - // || _selected.cbegin()->first != _itemUnderPress) { - // uponSelected = false; - // } else { - // uint16 selFrom = _selected.cbegin()->second.from, selTo = _selected.cbegin()->second.to; - // if (_mouseTextSymbol < selFrom || _mouseTextSymbol >= selTo) { - // uponSelected = false; - // } - // } - //} - //if (uponSelected) { - // _mouseAction = MouseAction::PrepareDrag; // start text drag - //} else if (!_pressWasInactive) { - // if (dynamic_cast(_itemUnderPress->getMedia()) || _mouseCursorState == HistoryInDateCursorState) { - // _mouseAction = MouseAction::PrepareDrag; // start sticker drag or by-date drag - // } else { - // if (dragState.afterSymbol) ++_mouseTextSymbol; - // TextSelection selStatus = { _mouseTextSymbol, _mouseTextSymbol }; - // if (selStatus != FullSelection && (_selected.empty() || _selected.cbegin()->second != FullSelection)) { - // if (!_selected.empty()) { - // repaintItem(_selected.cbegin()->first); - // _selected.clear(); - // } - // _selected.emplace(_itemUnderPress, selStatus); - // _mouseAction = MouseAction::Selecting; - // repaintItem(_itemUnderPress); - // } else { - // _mouseAction = MouseAction::PrepareSelect; - // } - // } - //} + if (_pressState.inside) { + _mouseTextSymbol = dragState.symbol; + if (isPressInSelectedText(dragState)) { + _mouseAction = MouseAction::PrepareDrag; // start text drag + } else if (!_pressWasInactive) { + if (requiredToStartDragging(pressLayout)) { + _mouseAction = MouseAction::PrepareDrag; + } else { + if (dragState.afterSymbol) ++_mouseTextSymbol; + TextSelection selStatus = { _mouseTextSymbol, _mouseTextSymbol }; + if (selStatus != FullSelection && !hasSelectedItems()) { + clearSelected(); + applyItemSelection(_pressState.itemId, selStatus); + _mouseAction = MouseAction::Selecting; + repaintItem(pressLayout); + } else { + _mouseAction = MouseAction::PrepareSelect; + } + } + } } else if (!_pressWasInactive) { _mouseAction = MouseAction::PrepareSelect; // start items select } } } - if (!_itemUnderPress) { + if (!pressLayout) { _mouseAction = MouseAction::None; } else if (_mouseAction == MouseAction::None) { - _itemUnderPress = nullptr; + mouseActionCancel(); } } void ListWidget::mouseActionCancel() { - _itemUnderPress = nullptr; + _pressState = CursorState(); _mouseAction = MouseAction::None; - _dragStartPosition = QPoint(0, 0); - _dragSelFrom = _dragSelTo = nullptr; + clearDragSelection(); _wasSelectedText = false; -// _widget->noSelectingScroll(); +// _widget->noSelectingScroll(); // #TODO scroll by drag } void ListWidget::performDrag() { if (_mouseAction != MouseAction::Dragging) return; - bool uponSelected = false; - if (_itemUnderPress) { - if (!_selected.empty() && _selected.cbegin()->second == FullSelection) { -// uponSelected = (_selected.find(_itemUnderPress) != _selected.cend()); - } else { + auto uponSelected = false; + if (_pressState.itemId && _pressState.inside) { + if (hasSelectedItems()) { + uponSelected = isItemUnderPressSelected(); + } else if (auto pressLayout = getExistingLayout( + _pressState.itemId)) { HistoryStateRequest request; request.flags |= Text::StateRequest::Flag::LookupSymbol; -// auto dragState = _itemUnderPress->getState(_dragStartPosition, request); - HistoryTextState dragState; - _itemUnderPress->getState(dragState.link, dragState.cursor, _dragStartPosition); - uponSelected = (dragState.cursor == HistoryInTextCursorState); - if (uponSelected) { - //if (_selected.empty() - // || _selected.cbegin()->second == FullSelection - // || _selected.cbegin()->first != _itemUnderPress) { - // uponSelected = false; - //} else { - // uint16 selFrom = _selected.cbegin()->second.from, selTo = _selected.cbegin()->second.to; - // if (dragState.symbol < selFrom || dragState.symbol >= selTo) { - // uponSelected = false; - // } - //} - } + auto dragState = pressLayout->getState( + _pressState.cursor, + request); + uponSelected = isPressInSelectedText(dragState); } } auto pressedHandler = ClickHandler::getPressed(); @@ -1286,7 +1548,7 @@ void ListWidget::performDrag() { //} } //if (auto mimeData = MimeDataFromTextWithEntities(sel)) { - // updateDragSelection(0, 0, false); + // clearDragSelection(); // _widget->noSelectingScroll(); // if (!urls.isEmpty()) mimeData->setUrls(urls); @@ -1301,7 +1563,7 @@ void ListWidget::performDrag() { //} else { // auto forwardMimeType = QString(); // auto pressedMedia = static_cast(nullptr); - // if (auto pressedItem = _itemUnderPress) { + // if (auto pressedItem = _pressState.layout) { // pressedMedia = pressedItem->getMedia(); // if (_mouseCursorState == HistoryInDateCursorState || (pressedMedia && pressedMedia->dragItem())) { // forwardMimeType = qsl("application/x-td-forward-pressed"); @@ -1336,84 +1598,79 @@ void ListWidget::performDrag() { void ListWidget::mouseActionFinish(const QPoint &screenPos, Qt::MouseButton button) { mouseActionUpdate(screenPos); - ClickHandlerPtr activated = ClickHandler::unpressed(); + auto pressState = base::take(_pressState); + repaintItem(pressState.itemId); + + auto simpleSelectionChange = pressState.itemId + && pressState.inside + && !_pressWasInactive + && (button != Qt::RightButton) + && (_mouseAction == MouseAction::PrepareDrag + || _mouseAction == MouseAction::PrepareSelect); + auto needSelectionToggle = simpleSelectionChange + && hasSelectedItems(); + auto needSelectionClear = simpleSelectionChange + && hasSelectedText(); + + auto activated = ClickHandler::unpressed(); if (_mouseAction == MouseAction::Dragging) { activated.clear(); - } 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.empty() && _selected.cbegin()->second == FullSelection && button != Qt::RightButton) { - if (auto media = pressed->getMedia()) { - if (media->toggleSelectionByHandlerClick(activated)) { - activated.clear(); - } - } - } - } - if (_itemUnderPress) { - repaintItem(_itemUnderPress); - _itemUnderPress = nullptr; + } else if (needSelectionToggle) { + activated.clear(); } _wasSelectedText = false; - if (activated) { mouseActionCancel(); App::activateClickHandler(activated, button); return; } - if (_mouseAction == MouseAction::PrepareSelect && !_pressWasInactive && !_selected.empty() && _selected.cbegin()->second == FullSelection) { - //SelectedItems::iterator i = _selected.find(_itemUnderPress); - //if (i == _selected.cend() && !_itemUnderPress->serviceMsg() && _itemUnderPress->id > 0) { - // if (_selected.size() < MaxSelectedItems) { - // if (!_selected.empty() && _selected.cbegin()->second != FullSelection) { - // _selected.clear(); - // } - // _selected.emplace(_itemUnderPress, FullSelection); - // } - //} else { - // _selected.erase(i); - //} - repaintItem(_itemUnderPress); - } else if (_mouseAction == MouseAction::PrepareDrag && !_pressWasInactive && button != Qt::RightButton) { - //auto i = _selected.find(_itemUnderPress); - //if (i != _selected.cend() && i->second == FullSelection) { - // _selected.erase(i); - // repaintItem(_itemUnderPress); - //} else if (i == _selected.cend() && !_itemUnderPress->serviceMsg() && _itemUnderPress->id > 0 && !_selected.empty() && _selected.cbegin()->second == FullSelection) { - // if (_selected.size() < MaxSelectedItems) { - // _selected.emplace(_itemUnderPress, FullSelection); - // repaintItem(_itemUnderPress); - // } - //} else { - // _selected.clear(); - // update(); - //} + + if (needSelectionToggle) { + toggleItemSelection(pressState.itemId); + } else if (needSelectionClear) { + clearSelected(); } else if (_mouseAction == MouseAction::Selecting) { - //if (_dragSelFrom && _dragSelTo) { - // applyDragSelection(); - // _dragSelFrom = _dragSelTo = 0; - //} else if (!_selected.empty() && !_pressWasInactive) { - // auto sel = _selected.cbegin()->second; - // if (sel != FullSelection && sel.from == sel.to) { - // _selected.clear(); - // App::wnd()->setInnerFocus(); - // } - //} + if (!_dragSelected.empty()) { + applyDragSelection(); + } else if (!_selected.empty() && !_pressWasInactive) { + auto selection = _selected.cbegin()->second; + if (selection.text != FullSelection + && selection.text.from == selection.text.to) { + clearSelected(); + //_controller->window()->setInnerFocus(); // #TODO focus + } + } } _mouseAction = MouseAction::None; - _itemUnderPress = nullptr; _mouseSelectType = TextSelectType::Letters; - //_widget->noSelectingScroll(); + //_widget->noSelectingScroll(); // #TODO scroll by drag //_widget->updateTopBarSelection(); #if defined Q_OS_LINUX32 || defined Q_OS_LINUX64 - //if (!_selected.empty() && _selected.cbegin()->second != FullSelection) { + //if (hasSelectedText()) { // #TODO linux clipboard // setToClipboard(_selected.cbegin()->first->selectedText(_selected.cbegin()->second), QClipboard::Selection); //} #endif // Q_OS_LINUX32 || Q_OS_LINUX64 } +void ListWidget::applyDragSelection() { + applyDragSelection(_selected); + clearDragSelection(); +} + +void ListWidget::applyDragSelection(SelectedMap &applyTo) const { + if (_dragSelectAction == DragSelectAction::Selecting) { + for (auto &[universalId,data] : _dragSelected) { + changeItemSelection(applyTo, universalId, FullSelection); + } + } else if (_dragSelectAction == DragSelectAction::Deselecting) { + for (auto &[universalId,data] : _dragSelected) { + applyTo.remove(universalId); + } + } +} + void ListWidget::refreshHeight() { resize(width(), recountHeight()); } @@ -1429,7 +1686,7 @@ int ListWidget::recountHeight() { + cachedPadding.bottom(); } -void ListWidget::updateSelected() { +void ListWidget::mouseActionUpdate() { mouseActionUpdate(_mousePosition); } @@ -1472,6 +1729,7 @@ auto ListWidget::findSectionAfterTop( return (section.top() + section.height()) <= top; }); } + auto ListWidget::findSectionAfterBottom( std::vector
::const_iterator from, int bottom) const -> std::vector
::const_iterator { diff --git a/Telegram/SourceFiles/info/media/info_media_list_widget.h b/Telegram/SourceFiles/info/media/info_media_list_widget.h index f406aa60d3..9343e37a81 100644 --- a/Telegram/SourceFiles/info/media/info_media_list_widget.h +++ b/Telegram/SourceFiles/info/media/info_media_list_widget.h @@ -66,6 +66,18 @@ public: rpl::producer scrollToRequests() const { return _scrollToRequests.events(); } + struct SelectedItem { + explicit SelectedItem(FullMsgId msgId) : msgId(msgId) { + } + + FullMsgId msgId; + bool canDelete = false; + bool canForward = false; + }; + using SelectedItems = std::vector; + rpl::producer selectedItemsValue() const { + return _selectedItemsStream.events(); + } ~ListWidget(); @@ -98,19 +110,51 @@ private: std::unique_ptr item; bool stale = false; }; + struct Context; class Section; struct FoundItem { not_null layout; QRect geometry; bool exact = false; }; + struct SelectionData { + explicit SelectionData(TextSelection text) : text(text) { + } + + TextSelection text; + bool canDelete = false; + bool canForward = false; + }; + using SelectedMap = base::flat_map< + UniversalMsgId, + SelectionData, + std::less<>>; + enum class DragSelectAction { + None, + Selecting, + Deselecting, + }; + struct CursorState { + UniversalMsgId itemId = 0; + QSize size; + QPoint cursor; + bool inside = false; + + inline bool operator==(const CursorState &other) const { + return (itemId == other.itemId) + && (cursor == other.cursor); + } + inline bool operator!=(const CursorState &other) const { + return !(*this == other); + } + + }; void start(); int recountHeight(); void refreshHeight(); QMargins padding() const; - void updateSelected(); bool isMyItem(not_null item) const; bool isItemLayout( not_null item, @@ -118,6 +162,7 @@ private: void repaintItem(const HistoryItem *item); void repaintItem(UniversalMsgId msgId); void repaintItem(const BaseLayout *item); + void repaintItem(QRect itemGeometry); void itemRemoved(not_null item); void itemLayoutChanged(not_null item); @@ -126,12 +171,46 @@ private: void refreshRows(); SharedMediaMergedSlice::Key sliceKey( UniversalMsgId universalId) const; - BaseLayout *getLayout(const FullMsgId &itemId); - BaseLayout *getExistingLayout(const FullMsgId &itemId) const; + BaseLayout *getLayout(UniversalMsgId universalId); + BaseLayout *getExistingLayout(UniversalMsgId universalId) const; std::unique_ptr createLayout( - const FullMsgId &itemId, + UniversalMsgId universalId, Type type); + SelectedItems collectSelectedItems() const; + void pushSelectedItems(); + FullMsgId computeFullId(UniversalMsgId universalId) const; + bool hasSelected() const; + bool isSelectedItem( + const SelectedMap::const_iterator &i) const; + void removeItemSelection( + const SelectedMap::const_iterator &i); + bool hasSelectedText() const; + bool hasSelectedItems() const; + void clearSelected(); + void applyItemSelection( + UniversalMsgId universalId, + TextSelection selection); + void toggleItemSelection( + UniversalMsgId universalId); + SelectedMap::iterator itemUnderPressSelection(); + SelectedMap::const_iterator itemUnderPressSelection() const; + bool isItemUnderPressSelected() const; + bool requiredToStartDragging(not_null layout) const; + bool isPressInSelectedText(HistoryTextState state) const; + void applyDragSelection(); + void applyDragSelection(SelectedMap &applyTo) const; + bool changeItemSelection( + SelectedMap &selected, + UniversalMsgId universalId, + TextSelection selection) const; + + static bool IsAfter( + const CursorState &a, + const CursorState &b); + static bool SkipSelectFromItem(const CursorState &state); + static bool SkipSelectTillItem(const CursorState &state); + void markLayoutsStale(); void clearStaleLayouts(); std::vector
::iterator findSectionByItem( @@ -157,11 +236,25 @@ private: const QPoint &screenPos, Qt::MouseButton button); void mouseActionUpdate(const QPoint &screenPos); + void mouseActionUpdate(); void mouseActionFinish( const QPoint &screenPos, Qt::MouseButton button); void mouseActionCancel(); void performDrag(); + style::cursor computeMouseCursor() const; + + void updateDragSelection(); + void clearDragSelection(); + void setDragSelection( + BaseLayout *dragSelectFrom, + BaseLayout *dragSelectTill, + DragSelectAction action); + + void trySwitchToWordSelection(); + void switchToWordSelection(); + void validateTrippleClickStartTime(); + void checkMoveToOtherViewer(); not_null _controller; not_null _peer; @@ -183,29 +276,24 @@ private: MouseAction _mouseAction = MouseAction::None; TextSelectType _mouseSelectType = TextSelectType::Letters; - QPoint _dragStartPosition; QPoint _mousePosition; - BaseLayout *_itemNearestToCursor = nullptr; - BaseLayout *_itemUnderCursor = nullptr; - BaseLayout *_itemUnderPress = nullptr; + CursorState _overState; + CursorState _pressState; + BaseLayout *_overLayout = nullptr; HistoryCursorState _mouseCursorState = HistoryDefaultCursorState; -// uint16 _mouseTextSymbol = 0; + uint16 _mouseTextSymbol = 0; bool _pressWasInactive = false; - using SelectedItems = std::map< - UniversalMsgId, - TextSelection, - std::less<>>; - SelectedItems _selected; + SelectedMap _selected; + SelectedMap _dragSelected; + rpl::event_stream _selectedItemsStream; style::cursor _cursor = style::cur_default; - BaseLayout *_dragSelFrom = nullptr; - BaseLayout *_dragSelTo = nullptr; -// bool _dragSelecting = false; + DragSelectAction _dragSelectAction = DragSelectAction::None; bool _wasSelectedText = false; // was some text selected in current drag action Ui::PopupMenu *_contextMenu = nullptr; ClickHandlerPtr _contextMenuLink; QPoint _trippleClickPoint; - QTimer _trippleClickTimer; + TimeMs _trippleClickStartTime = 0; rpl::lifetime _viewerLifetime; diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp index 861b338e8d..dc75a7f23a 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp @@ -213,14 +213,17 @@ void Gif::paint(Painter &p, const QRect &clip, const PaintContext *context) cons } } -void Gif::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, QPoint point) const { +HistoryTextState Gif::getState( + QPoint point, + HistoryStateRequest request) const { if (QRect(0, 0, _width, st::inlineMediaHeight).contains(point)) { if (_delete && rtlpoint(point, _width).x() >= _width - st::stickerPanDeleteIconBg.width() && point.y() < st::stickerPanDeleteIconBg.height()) { - link = _delete; + return _delete; } else { - link = _send; + return _send; } } + return {}; } void Gif::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) { @@ -404,10 +407,13 @@ void Sticker::paint(Painter &p, const QRect &clip, const PaintContext *context) } } -void Sticker::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, QPoint point) const { +HistoryTextState Sticker::getState( + QPoint point, + HistoryStateRequest request) const { if (QRect(0, 0, _width, st::inlineMediaHeight).contains(point)) { - link = _send; + return _send; } + return {}; } void Sticker::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) { @@ -491,10 +497,13 @@ void Photo::paint(Painter &p, const QRect &clip, const PaintContext *context) co } } -void Photo::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, QPoint point) const { +HistoryTextState Photo::getState( + QPoint point, + HistoryStateRequest request) const { if (QRect(0, 0, _width, st::inlineMediaHeight).contains(point)) { - link = _send; + return _send; } + return {}; } PhotoData *Photo::getShownPhoto() const { @@ -633,15 +642,16 @@ void Video::paint(Painter &p, const QRect &clip, const PaintContext *context) co } } -void Video::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, QPoint point) const { +HistoryTextState Video::getState( + QPoint point, + HistoryStateRequest request) const { if (QRect(0, st::inlineRowMargin, st::inlineThumbSize, st::inlineThumbSize).contains(point)) { - link = _link; - return; + return _link; } if (QRect(st::inlineThumbSize + st::inlineThumbSkip, 0, _width - st::inlineThumbSize - st::inlineThumbSkip, _height).contains(point)) { - link = _send; - return; + return _send; } + return {}; } void Video::prepareThumb(int32 width, int32 height) const { @@ -773,16 +783,18 @@ void File::paint(Painter &p, const QRect &clip, const PaintContext *context) con } } -void File::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, QPoint point) const { +HistoryTextState File::getState( + QPoint point, + HistoryStateRequest request) const { if (QRect(0, st::inlineRowMargin, st::msgFileSize, st::msgFileSize).contains(point)) { - link = getShownDocument()->loading() ? _cancel : _open; - return; - } - auto left = st::msgFileSize + st::inlineThumbSkip; - if (QRect(left, 0, _width - left, _height).contains(point)) { - link = _send; - return; + return getShownDocument()->loading() ? _cancel : _open; + } else { + auto left = st::msgFileSize + st::inlineThumbSkip; + if (QRect(left, 0, _width - left, _height).contains(point)) { + return _send; + } } + return {}; } void File::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) { @@ -933,15 +945,16 @@ void Contact::paint(Painter &p, const QRect &clip, const PaintContext *context) } } -void Contact::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, QPoint point) const { - if (QRect(0, st::inlineRowMargin, st::msgFileSize, st::inlineThumbSize).contains(point)) { - return; - } - auto left = (st::msgFileSize + st::inlineThumbSkip); - if (QRect(left, 0, _width - left, _height).contains(point)) { - link = _send; - return; +HistoryTextState Contact::getState( + QPoint point, + HistoryStateRequest request) const { + if (!QRect(0, st::inlineRowMargin, st::msgFileSize, st::inlineThumbSize).contains(point)) { + auto left = (st::msgFileSize + st::inlineThumbSkip); + if (QRect(left, 0, _width - left, _height).contains(point)) { + return _send; + } } + return {}; } void Contact::prepareThumb(int width, int height) const { @@ -1069,10 +1082,11 @@ void Article::paint(Painter &p, const QRect &clip, const PaintContext *context) } } -void Article::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, QPoint point) const { +HistoryTextState Article::getState( + QPoint point, + HistoryStateRequest request) const { if (_withThumb && QRect(0, st::inlineRowMargin, st::inlineThumbSize, st::inlineThumbSize).contains(point)) { - link = _link; - return; + return _link; } auto left = _withThumb ? (st::inlineThumbSize + st::inlineThumbSkip) : 0; if (QRect(left, 0, _width - left, _height).contains(point)) { @@ -1082,13 +1096,12 @@ void Article::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, QPoint auto descriptionLines = 2; auto descriptionHeight = qMin(_description.countHeight(_width - left), st::normalFont->height * descriptionLines); if (rtlrect(left, st::inlineRowMargin + titleHeight + descriptionHeight, _urlWidth, st::normalFont->height, _width).contains(point)) { - link = _url; - return; + return _url; } } - link = _send; - return; + return _send; } + return {}; } void Article::prepareThumb(int width, int height) const { @@ -1253,16 +1266,17 @@ void Game::paint(Painter &p, const QRect &clip, const PaintContext *context) con } } -void Game::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, QPoint point) const { +HistoryTextState Game::getState( + QPoint point, + HistoryStateRequest request) const { int left = st::inlineThumbSize + st::inlineThumbSkip; if (QRect(0, st::inlineRowMargin, st::inlineThumbSize, st::inlineThumbSize).contains(point)) { - link = _send; - return; + return _send; } if (QRect(left, 0, _width - left, _height).contains(point)) { - link = _send; - return; + return _send; } + return {}; } void Game::prepareThumb(int width, int height) const { diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.h b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.h index db578e338d..6890627de1 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.h +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.h @@ -73,7 +73,9 @@ public: } void paint(Painter &p, const QRect &clip, const PaintContext *context) const override; - void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, QPoint point) const override; + HistoryTextState getState( + QPoint point, + HistoryStateRequest request) const override; // ClickHandlerHost interface void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override; @@ -130,7 +132,9 @@ public: } void paint(Painter &p, const QRect &clip, const PaintContext *context) const override; - void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, QPoint point) const override; + HistoryTextState getState( + QPoint point, + HistoryStateRequest request) const override; private: PhotoData *getShownPhoto() const; @@ -160,7 +164,9 @@ public: void preload() const override; void paint(Painter &p, const QRect &clip, const PaintContext *context) const override; - void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, QPoint point) const override; + HistoryTextState getState( + QPoint point, + HistoryStateRequest request) const override; // ClickHandlerHost interface void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override; @@ -184,7 +190,9 @@ public: void initDimensions() override; void paint(Painter &p, const QRect &clip, const PaintContext *context) const override; - void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, QPoint point) const override; + HistoryTextState getState( + QPoint point, + HistoryStateRequest request) const override; private: ClickHandlerPtr _link; @@ -231,7 +239,9 @@ public: void initDimensions() override; void paint(Painter &p, const QRect &clip, const PaintContext *context) const override; - void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, QPoint point) const override; + HistoryTextState getState( + QPoint point, + HistoryStateRequest request) const override; // ClickHandlerHost interface void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override; @@ -294,7 +304,9 @@ public: int resizeGetHeight(int width) override; void paint(Painter &p, const QRect &clip, const PaintContext *context) const override; - void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, QPoint point) const override; + HistoryTextState getState( + QPoint point, + HistoryStateRequest request) const override; private: mutable QPixmap _thumb; @@ -312,7 +324,9 @@ public: int resizeGetHeight(int width) override; void paint(Painter &p, const QRect &clip, const PaintContext *context) const override; - void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, QPoint point) const override; + HistoryTextState getState( + QPoint point, + HistoryStateRequest request) const override; private: ClickHandlerPtr _url, _link; @@ -335,7 +349,9 @@ public: void initDimensions() override; void paint(Painter &p, const QRect &clip, const PaintContext *context) const override; - void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, QPoint point) const override; + HistoryTextState getState( + QPoint point, + HistoryStateRequest request) const override; private: void countFrameSize(); diff --git a/Telegram/SourceFiles/inline_bots/inline_results_widget.cpp b/Telegram/SourceFiles/inline_bots/inline_results_widget.cpp index e82a720802..dbf7253e1d 100644 --- a/Telegram/SourceFiles/inline_bots/inline_results_widget.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_results_widget.cpp @@ -644,8 +644,10 @@ void Inner::updateSelected() { } if (col < inlineItems.size()) { sel = row * MatrixRowShift + col; - inlineItems.at(col)->getState(lnk, cursor, QPoint(sx, sy)); - lnkhost = inlineItems.at(col); + auto result = inlineItems[col]->getState(QPoint(sx, sy), HistoryStateRequest()); + lnk = result.link; + cursor = result.cursor; + lnkhost = inlineItems[col]; } else { row = col = -1; } diff --git a/Telegram/SourceFiles/layout.h b/Telegram/SourceFiles/layout.h index 333db4394e..b8a99df66b 100644 --- a/Telegram/SourceFiles/layout.h +++ b/Telegram/SourceFiles/layout.h @@ -119,14 +119,15 @@ public: return _height; } - virtual void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, QPoint point) const { - link.clear(); - cursor = HistoryDefaultCursorState; + [[nodiscard]] virtual HistoryTextState getState( + QPoint point, + HistoryStateRequest request) const { + return {}; } - virtual void getSymbol(uint16 &symbol, bool &after, bool &upon, QPoint point) const { // from text - upon = hasPoint(point); - symbol = upon ? 0xFFFF : 0; - after = false; + [[nodiscard]] virtual TextSelection adjustSelection( + TextSelection selection, + TextSelectType type) const { + return selection; } int width() const { diff --git a/Telegram/SourceFiles/media/player/media_player_list.cpp b/Telegram/SourceFiles/media/player/media_player_list.cpp index fa3aa69e47..f11c953b9a 100644 --- a/Telegram/SourceFiles/media/player/media_player_list.cpp +++ b/Telegram/SourceFiles/media/player/media_player_list.cpp @@ -104,7 +104,9 @@ void ListWidget::mouseMoveEvent(QMouseEvent *e) { if (y <= m.y()) { if (auto media = layout->toMediaItem()) { item = media->getItem(); - media->getState(lnk, cursorState, m - QPoint(0, y)); + auto result = media->getState(m - QPoint(0, y), HistoryStateRequest()); + lnk = result.link; + cursorState = result.cursor; lnkhost = media; } } diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index 6f7f4d4090..afa77415b9 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -1153,7 +1153,11 @@ void MediaView::refreshMediaViewer() { } void MediaView::showPhoto(not_null photo, HistoryItem *context) { - setContext(context); + if (context) { + setContext(context); + } else { + setContext(base::none); + } _firstOpenedPeerPhoto = false; _saveMsgStarted = 0; diff --git a/Telegram/SourceFiles/mtproto/sender.h b/Telegram/SourceFiles/mtproto/sender.h index 11db35d9e7..d061dee179 100644 --- a/Telegram/SourceFiles/mtproto/sender.h +++ b/Telegram/SourceFiles/mtproto/sender.h @@ -214,39 +214,39 @@ public: SpecificRequestBuilder(SpecificRequestBuilder &&other) = default; public: - SpecificRequestBuilder &toDC(ShiftedDcId dcId) noexcept WARN_UNUSED_RESULT { + [[nodiscard]] SpecificRequestBuilder &toDC(ShiftedDcId dcId) noexcept { setToDC(dcId); return *this; } - SpecificRequestBuilder &canWait(TimeMs ms) noexcept WARN_UNUSED_RESULT { + [[nodiscard]] SpecificRequestBuilder &canWait(TimeMs ms) noexcept { setCanWait(ms); return *this; } - SpecificRequestBuilder &done(base::lambda_once callback) WARN_UNUSED_RESULT { + [[nodiscard]] SpecificRequestBuilder &done(base::lambda_once callback) { setDoneHandler(MakeShared>(sender(), std::move(callback))); return *this; } - SpecificRequestBuilder &done(base::lambda_once callback) WARN_UNUSED_RESULT { + [[nodiscard]] SpecificRequestBuilder &done(base::lambda_once callback) { setDoneHandler(MakeShared>(sender(), std::move(callback))); return *this; } - SpecificRequestBuilder &fail(base::lambda_once callback) noexcept WARN_UNUSED_RESULT { + [[nodiscard]] SpecificRequestBuilder &fail(base::lambda_once callback) noexcept { setFailHandler(std::move(callback)); return *this; } - SpecificRequestBuilder &fail(base::lambda_once callback) noexcept WARN_UNUSED_RESULT { + [[nodiscard]] SpecificRequestBuilder &fail(base::lambda_once callback) noexcept { setFailHandler(std::move(callback)); return *this; } - SpecificRequestBuilder &handleFloodErrors() noexcept WARN_UNUSED_RESULT { + [[nodiscard]] SpecificRequestBuilder &handleFloodErrors() noexcept { setFailSkipPolicy(FailSkipPolicy::HandleFlood); return *this; } - SpecificRequestBuilder &handleAllErrors() noexcept WARN_UNUSED_RESULT { + [[nodiscard]] SpecificRequestBuilder &handleAllErrors() noexcept { setFailSkipPolicy(FailSkipPolicy::HandleAll); return *this; } - SpecificRequestBuilder &after(mtpRequestId requestId) noexcept WARN_UNUSED_RESULT { + [[nodiscard]] SpecificRequestBuilder &after(mtpRequestId requestId) noexcept { setAfter(requestId); return *this; } @@ -280,11 +280,11 @@ public: }; template ::value>, typename = typename Request::Unboxed> - SpecificRequestBuilder request(Request &&request) noexcept WARN_UNUSED_RESULT; + [[nodiscard]] SpecificRequestBuilder request(Request &&request) noexcept; - SentRequestWrap request(mtpRequestId requestId) noexcept WARN_UNUSED_RESULT; + [[nodiscard]] SentRequestWrap request(mtpRequestId requestId) noexcept; - decltype(auto) requestCanceller() noexcept WARN_UNUSED_RESULT { + [[nodiscard]] decltype(auto) requestCanceller() noexcept { return [this](mtpRequestId requestId) { request(requestId).cancel(); }; diff --git a/Telegram/SourceFiles/overview/overview_layout.cpp b/Telegram/SourceFiles/overview/overview_layout.cpp index 14aa294a8b..1e39c71eaa 100644 --- a/Telegram/SourceFiles/overview/overview_layout.cpp +++ b/Telegram/SourceFiles/overview/overview_layout.cpp @@ -66,12 +66,16 @@ TextWithEntities ComposeNameWithEntities(DocumentData *document) { } // namespace -void ItemBase::clickHandlerActiveChanged(const ClickHandlerPtr &action, bool active) { +void ItemBase::clickHandlerActiveChanged( + const ClickHandlerPtr &action, + bool active) { App::hoveredLinkItem(active ? _parent.get() : nullptr); Auth().data().requestItemRepaint(_parent); } -void ItemBase::clickHandlerPressedChanged(const ClickHandlerPtr &action, bool pressed) { +void ItemBase::clickHandlerPressedChanged( + const ClickHandlerPtr &action, + bool pressed) { App::pressedLinkItem(pressed ? _parent.get() : nullptr); Auth().data().requestItemRepaint(_parent); } @@ -306,10 +310,13 @@ void Photo::ensureCheckboxCreated() { }); } -void Photo::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, QPoint point) const { +HistoryTextState Photo::getState( + QPoint point, + HistoryStateRequest request) const { if (hasPoint(point)) { - link = _link; + return _link; } + return {}; } void Photo::clickHandlerActiveChanged(const ClickHandlerPtr &action, bool active) { @@ -506,12 +513,15 @@ bool Video::iconAnimated() const { return true; } -void Video::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, QPoint point) const { +HistoryTextState Video::getState( + QPoint point, + HistoryStateRequest request) const { bool loaded = _data->loaded(); if (hasPoint(point)) { - link = loaded ? _openl : (_data->loading() ? _cancell : _savel); + return loaded ? _openl : (_data->loading() ? _cancell : _savel); } + return {}; } void Video::updateStatusText() { @@ -664,7 +674,9 @@ void Voice::paint(Painter &p, const QRect &clip, TextSelection selection, const } } -void Voice::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, QPoint point) const { +HistoryTextState Voice::getState( + QPoint point, + HistoryStateRequest request) const { bool loaded = _data->loaded(); int32 nameleft = 0, nametop = 0, nameright = 0, statustop = 0, datetop = 0; @@ -676,20 +688,20 @@ void Voice::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, QPoint p auto inner = rtlrect(_st.songPadding.left(), _st.songPadding.top(), _st.songThumbSize, _st.songThumbSize, _width); if (inner.contains(point)) { - link = loaded ? _openl : ((_data->loading() || _data->status == FileUploading) ? _cancell : _openl); - return; + return loaded ? _openl : ((_data->loading() || _data->status == FileUploading) ? _cancell : _openl); } + auto result = HistoryTextState(); if (rtlrect(nameleft, statustop, _width - nameleft - nameright, st::normalFont->height, _width).contains(point)) { if (_status.size() == FileStatusSizeLoaded || _status.size() == FileStatusSizeReady) { auto textState = _details.getStateLeft(point - QPoint(nameleft, statustop), _width, _width); - link = textState.link; - cursor = textState.uponSymbol ? HistoryInTextCursorState : HistoryDefaultCursorState; + result.link = textState.link; + result.cursor = textState.uponSymbol ? HistoryInTextCursorState : HistoryDefaultCursorState; } } - if (hasPoint(point) && !link && !_data->loading()) { - link = _namel; - return; + if (hasPoint(point) && !result.link && !_data->loading()) { + return _namel; } + return result; } float64 Voice::dataProgress() const { @@ -954,7 +966,9 @@ void Document::paint(Painter &p, const QRect &clip, TextSelection selection, con } } -void Document::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, QPoint point) const { +HistoryTextState Document::getState( + QPoint point, + HistoryStateRequest request) const { bool loaded = _data->loaded() || Local::willStickerImageLoad(_data->mediaKey()); int32 nameleft = 0, nametop = 0, nameright = 0, statustop = 0, datetop = 0; @@ -968,12 +982,10 @@ void Document::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, QPoin auto inner = rtlrect(_st.songPadding.left(), _st.songPadding.top(), _st.songThumbSize, _st.songThumbSize, _width); if (inner.contains(point)) { - link = loaded ? _openl : ((_data->loading() || _data->status == FileUploading) ? _cancell : _openl); - return; + return loaded ? _openl : ((_data->loading() || _data->status == FileUploading) ? _cancell : _openl); } if (hasPoint(point) && !_data->loading()) { - link = _namel; - return; + return _namel; } } else { nameleft = _st.fileThumbSize + _st.filePadding.right(); @@ -984,27 +996,24 @@ void Document::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, QPoin auto rthumb = rtlrect(0, st::linksBorder + _st.filePadding.top(), _st.fileThumbSize, _st.fileThumbSize, _width); if (rthumb.contains(point)) { - link = loaded ? _openl : ((_data->loading() || _data->status == FileUploading) ? _cancell : _savel); - return; + return loaded ? _openl : ((_data->loading() || _data->status == FileUploading) ? _cancell : _savel); } if (_data->status != FileUploadFailed) { if (rtlrect(nameleft, datetop, _datew, st::normalFont->height, _width).contains(point)) { - link = _msgl; - return; + return _msgl; } } if (!_data->loading() && _data->isValid()) { if (loaded && rtlrect(0, st::linksBorder, nameleft, _height - st::linksBorder, _width).contains(point)) { - link = _namel; - return; + return _namel; } if (rtlrect(nameleft, nametop, qMin(_width - nameleft - nameright, _name.maxWidth()), st::semiboldFont->height, _width).contains(point)) { - link = _namel; - return; + return _namel; } } } + return {}; } float64 Document::dataProgress() const { @@ -1301,11 +1310,12 @@ void Link::paint(Painter &p, const QRect &clip, TextSelection selection, const P } } -void Link::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, QPoint point) const { +HistoryTextState Link::getState( + QPoint point, + HistoryStateRequest request) const { int32 left = st::linksPhotoSize + st::linksPhotoPadding, top = st::linksMargin.top() + st::linksBorder, w = _width - left; if (rtlrect(0, top, st::linksPhotoSize, st::linksPhotoSize, _width).contains(point)) { - link = _photol; - return; + return _photol; } if (!_title.isEmpty() && _text.isEmpty() && _links.size() == 1) { @@ -1313,8 +1323,7 @@ void Link::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, QPoint po } if (!_title.isEmpty()) { if (rtlrect(left, top, qMin(w, _titlew), st::semiboldFont->height, _width).contains(point)) { - link = _photol; - return; + return _photol; } top += st::webPageTitleFont->height; } @@ -1323,11 +1332,11 @@ void Link::getState(ClickHandlerPtr &link, HistoryCursorState &cursor, QPoint po } for (int32 i = 0, l = _links.size(); i < l; ++i) { if (rtlrect(left, top, qMin(w, _links.at(i).width), st::normalFont->height, _width).contains(point)) { - link = _links.at(i).lnk; - return; + return _links.at(i).lnk; } top += st::normalFont->height; } + return {}; } Link::LinkEntry::LinkEntry(const QString &url, const QString &text) diff --git a/Telegram/SourceFiles/overview/overview_layout.h b/Telegram/SourceFiles/overview/overview_layout.h index 68424d6e1e..57d9a64ae3 100644 --- a/Telegram/SourceFiles/overview/overview_layout.h +++ b/Telegram/SourceFiles/overview/overview_layout.h @@ -189,7 +189,9 @@ public: void initDimensions() override; int32 resizeGetHeight(int32 width) override; void paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) override; - void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, QPoint point) const override; + HistoryTextState getState( + QPoint point, + HistoryStateRequest request) const override; void clickHandlerActiveChanged(const ClickHandlerPtr &action, bool active) override; void clickHandlerPressedChanged(const ClickHandlerPtr &action, bool pressed) override; @@ -220,7 +222,9 @@ public: void initDimensions() override; int32 resizeGetHeight(int32 width) override; void paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) override; - void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, QPoint point) const override; + HistoryTextState getState( + QPoint point, + HistoryStateRequest request) const override; void clickHandlerActiveChanged(const ClickHandlerPtr &action, bool active) override; void clickHandlerPressedChanged(const ClickHandlerPtr &action, bool pressed) override; @@ -260,7 +264,9 @@ public: void initDimensions() override; void paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) override; - void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, QPoint point) const override; + HistoryTextState getState( + QPoint point, + HistoryStateRequest request) const override; protected: float64 dataProgress() const override; @@ -292,7 +298,9 @@ public: void initDimensions() override; void paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) override; - void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, QPoint point) const override; + HistoryTextState getState( + QPoint point, + HistoryStateRequest request) const override; virtual DocumentData *getDocument() const override { return _data; @@ -333,7 +341,9 @@ public: void initDimensions() override; int32 resizeGetHeight(int32 width) override; void paint(Painter &p, const QRect &clip, TextSelection selection, const PaintContext *context) override; - void getState(ClickHandlerPtr &link, HistoryCursorState &cursor, QPoint point) const override; + HistoryTextState getState( + QPoint point, + HistoryStateRequest request) const override; private: ClickHandlerPtr _photol; diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index 301d44bc30..8dc56437c4 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -900,7 +900,9 @@ void OverviewInner::onUpdateSelected() { item = media->getItem(); index = i; if (upon) { - media->getState(lnk, cursorState, m - QPoint(col * w + st::overviewPhotoSkip, _marginTop + row * vsize + st::overviewPhotoSkip)); + auto result = media->getState(m - QPoint(col * w + st::overviewPhotoSkip, _marginTop + row * vsize + st::overviewPhotoSkip), HistoryStateRequest()); + lnk = result.link; + cursorState = result.cursor; lnkhost = media; } } @@ -936,7 +938,9 @@ void OverviewInner::onUpdateSelected() { if (auto media = _items.at(i)->toMediaItem()) { item = media->getItem(); index = i; - media->getState(lnk, cursorState, m - QPoint(_rowsLeft, _marginTop + top)); + auto result = media->getState(m - QPoint(_rowsLeft, _marginTop + top), HistoryStateRequest()); + lnk = result.link; + cursorState = result.cursor; lnkhost = media; } break; diff --git a/Telegram/SourceFiles/ui/text/text.h b/Telegram/SourceFiles/ui/text/text.h index f5bcd66d1f..99d8fb6e5e 100644 --- a/Telegram/SourceFiles/ui/text/text.h +++ b/Telegram/SourceFiles/ui/text/text.h @@ -165,7 +165,7 @@ public: return getStateElided(rtlpoint(point, outerw), width, request); } - TextSelection adjustSelection(TextSelection selection, TextSelectType selectType) const WARN_UNUSED_RESULT; + [[nodiscard]] TextSelection adjustSelection(TextSelection selection, TextSelectType selectType) const; bool isFullSelection(TextSelection selection) const { return (selection.from == 0) && (selection.to >= _text.size()); }