From 728ed02a1cf3400091b37a72659bdb046e793e85 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 1 Nov 2023 09:17:08 +0400 Subject: [PATCH] Allow selecting text in webpage previews. --- .../history/history_inner_widget.cpp | 9 +++- .../history/view/history_view_element.cpp | 7 ++- .../history/view/history_view_element.h | 2 + .../history/view/history_view_message.cpp | 10 ++++ .../history/view/history_view_message.h | 2 + .../history/view/media/history_view_game.cpp | 51 ++++++++++++++++--- .../history/view/media/history_view_game.h | 17 ++++--- .../history/view/media/history_view_media.cpp | 5 ++ .../history/view/media/history_view_media.h | 2 + .../view/media/history_view_web_page.cpp | 5 ++ .../view/media/history_view_web_page.h | 2 + 11 files changed, 98 insertions(+), 14 deletions(-) diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 552905840..eee1ff08d 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -1673,7 +1673,10 @@ void HistoryInner::mouseActionStart(const QPoint &screenPos, Qt::MouseButton but Ui::MarkInactivePress(_controller->widget(), false); } - if (ClickHandler::getPressed()) { + const auto pressed = ClickHandler::getPressed(); + if (pressed + && (!Element::Hovered() + || !Element::Hovered()->allowTextSelectionByHandler(pressed))) { _mouseAction = MouseAction::PrepareDrag; } else if (inSelectionMode()) { if (_dragStateItem @@ -3837,6 +3840,10 @@ void HistoryInner::mouseActionUpdate() { selState = view->adjustSelection(selState, _mouseSelectType); } } + if (!selState.empty()) { + // We started selecting text in web page preview. + ClickHandler::unpressed(); + } if (_selected[_mouseActionItem] != selState) { _selected[_mouseActionItem] = selState; repaintItem(_mouseActionItem); diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp index 991d158dc..eeec9d86b 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.cpp +++ b/Telegram/SourceFiles/history/view/history_view_element.cpp @@ -1424,7 +1424,12 @@ HistoryMessageReply *Element::displayedReply() const { } bool Element::toggleSelectionByHandlerClick( - const ClickHandlerPtr &handler) const { + const ClickHandlerPtr &handler) const { + return false; +} + +bool Element::allowTextSelectionByHandler( + const ClickHandlerPtr &handler) const { return false; } diff --git a/Telegram/SourceFiles/history/view/history_view_element.h b/Telegram/SourceFiles/history/view/history_view_element.h index 3cd78c711..db3933b98 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.h +++ b/Telegram/SourceFiles/history/view/history_view_element.h @@ -462,6 +462,8 @@ public: } [[nodiscard]] virtual bool toggleSelectionByHandlerClick( const ClickHandlerPtr &handler) const; + [[nodiscard]] virtual bool allowTextSelectionByHandler( + const ClickHandlerPtr &handler) const; struct VerticalRepaintRange { int top = 0; diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index e5f2bb640..f5a6d655d 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -3148,6 +3148,16 @@ bool Message::toggleSelectionByHandlerClick( return false; } +bool Message::allowTextSelectionByHandler( + const ClickHandlerPtr &handler) const { + if (const auto media = this->media()) { + if (media->allowTextSelectionByHandler(handler)) { + return true; + } + } + return false; +} + bool Message::hasFromName() const { switch (context()) { case Context::AdminLog: diff --git a/Telegram/SourceFiles/history/view/history_view_message.h b/Telegram/SourceFiles/history/view/history_view_message.h index aa66b0cf5..aeba7c1aa 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.h +++ b/Telegram/SourceFiles/history/view/history_view_message.h @@ -140,6 +140,8 @@ public: [[nodiscard]] HistoryMessageReply *displayedReply() const override; [[nodiscard]] bool toggleSelectionByHandlerClick( const ClickHandlerPtr &handler) const override; + [[nodiscard]] bool allowTextSelectionByHandler( + const ClickHandlerPtr &handler) const override; [[nodiscard]] int infoWidth() const override; [[nodiscard]] int bottomInfoFirstLineWidth() const override; [[nodiscard]] bool bottomInfoIsWide() const override; diff --git a/Telegram/SourceFiles/history/view/media/history_view_game.cpp b/Telegram/SourceFiles/history/view/media/history_view_game.cpp index a91f8350d..24ba5187c 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_game.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_game.cpp @@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/text/text_utilities.h" #include "ui/cached_round_corners.h" #include "ui/chat/chat_style.h" +#include "ui/effects/ripple_animation.h" #include "ui/painter.h" #include "ui/power_saving.h" #include "core/ui_integration.h" @@ -226,6 +227,13 @@ void Game::draw(Painter &p, const PaintContext &context) const { Ui::Text::ValidateQuotePaintCache(*cache, _st); Ui::Text::FillQuotePaint(p, outer, *cache, _st); + if (_ripple) { + _ripple->paint(p, outer.x(), outer.y(), width(), &cache->bg); + if (_ripple->empty()) { + _ripple = nullptr; + } + } + auto lineHeight = UnitedLineHeight(); if (_titleLines) { p.setPen(cache->icon); @@ -322,7 +330,6 @@ TextState Game::textState(QPoint point, StateRequest request) const { auto tshift = inner.top(); auto paintw = inner.width(); - auto inThumb = false; auto symbolAdd = 0; auto lineHeight = UnitedLineHeight(); if (_titleLines) { @@ -353,11 +360,7 @@ TextState Game::textState(QPoint point, StateRequest request) const { } tshift += _descriptionLines * lineHeight; } - if (inThumb) { - if (_parent->data()->isHistoryEntry()) { - result.link = _openl; - } - } else if (_attach) { + if (_attach) { auto attachAtTop = !_titleLines && !_descriptionLines; if (!attachAtTop) tshift += st::mediaInBubbleSkip; @@ -375,6 +378,12 @@ TextState Game::textState(QPoint point, StateRequest request) const { } } } + if (_parent->data()->isHistoryEntry()) { + if (!result.link && outer.contains(point)) { + result.link = _openl; + } + } + _lastPoint = point - outer.topLeft(); result.symbol += symbolAdd; return result; @@ -399,11 +408,41 @@ void Game::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) { } void Game::clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) { + if (p == _openl) { + if (pressed) { + if (!_ripple) { + const auto full = QRect(0, 0, width(), height()); + const auto outer = full.marginsRemoved(inBubblePadding()); + const auto owner = &parent()->history()->owner(); + _ripple = std::make_unique( + st::defaultRippleAnimation, + Ui::RippleAnimation::RoundRectMask( + outer.size(), + _st.radius), + [=] { owner->requestViewRepaint(parent()); }); + } + _ripple->add(_lastPoint); + } else if (_ripple) { + _ripple->lastStop(); + } + } if (_attach) { _attach->clickHandlerPressedChanged(p, pressed); } } +bool Game::toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const { + return _attach && _attach->toggleSelectionByHandlerClick(p); +} + +bool Game::allowTextSelectionByHandler(const ClickHandlerPtr &p) const { + return (p == _openl); +} + +bool Game::dragItemByHandler(const ClickHandlerPtr &p) const { + return _attach && _attach->dragItemByHandler(p); +} + TextForMimeData Game::selectedText(TextSelection selection) const { auto titleResult = _title.toTextForMimeData(selection); auto descriptionResult = _description.toTextForMimeData( diff --git a/Telegram/SourceFiles/history/view/media/history_view_game.h b/Telegram/SourceFiles/history/view/media/history_view_game.h index 9cec01fdc..b7618bf69 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_game.h +++ b/Telegram/SourceFiles/history/view/media/history_view_game.h @@ -11,6 +11,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL class ReplyMarkupClickHandler; +namespace Ui { +class RippleAnimation; +} // namespace Ui + namespace HistoryView { class Game : public Media { @@ -35,12 +39,11 @@ public: return false; // we do not add _title and _description in FullSelection text copy. } - bool toggleSelectionByHandlerClick(const ClickHandlerPtr &p) const override { - return _attach && _attach->toggleSelectionByHandlerClick(p); - } - bool dragItemByHandler(const ClickHandlerPtr &p) const override { - return _attach && _attach->dragItemByHandler(p); - } + bool toggleSelectionByHandlerClick( + const ClickHandlerPtr &p) const override; + bool allowTextSelectionByHandler( + const ClickHandlerPtr &p) const override; + bool dragItemByHandler(const ClickHandlerPtr &p) const override; TextForMimeData selectedText(TextSelection selection) const override; @@ -102,7 +105,9 @@ private: const not_null _data; std::shared_ptr _openl; std::unique_ptr _attach; + mutable std::unique_ptr _ripple; + mutable QPoint _lastPoint; int _gameTagWidth = 0; int _descriptionLines = 0; int _titleLines = 0; diff --git a/Telegram/SourceFiles/history/view/media/history_view_media.cpp b/Telegram/SourceFiles/history/view/media/history_view_media.cpp index 69cad0ec4..8f9923c36 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_media.cpp @@ -181,6 +181,11 @@ Storage::SharedMediaTypesMask Media::sharedMediaTypes() const { return {}; } +bool Media::allowTextSelectionByHandler( + const ClickHandlerPtr &handler) const { + return false; +} + not_null Media::parent() const { return _parent; } diff --git a/Telegram/SourceFiles/history/view/media/history_view_media.h b/Telegram/SourceFiles/history/view/media/history_view_media.h index 392806575..7dadb5f87 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media.h +++ b/Telegram/SourceFiles/history/view/media/history_view_media.h @@ -136,6 +136,8 @@ public: // toggle selection instead of activating the pressed link [[nodiscard]] virtual bool toggleSelectionByHandlerClick( const ClickHandlerPtr &p) const = 0; + [[nodiscard]] virtual bool allowTextSelectionByHandler( + const ClickHandlerPtr &p) const; [[nodiscard]] virtual TextSelection adjustSelection( TextSelection selection, diff --git a/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp b/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp index 844963a5e..a6bb07528 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_web_page.cpp @@ -904,6 +904,11 @@ bool WebPage::toggleSelectionByHandlerClick( return _attach && _attach->toggleSelectionByHandlerClick(p); } +bool WebPage::allowTextSelectionByHandler( + const ClickHandlerPtr &p) const { + return (p == _openl); +} + bool WebPage::dragItemByHandler(const ClickHandlerPtr &p) const { return _attach && _attach->dragItemByHandler(p); } diff --git a/Telegram/SourceFiles/history/view/media/history_view_web_page.h b/Telegram/SourceFiles/history/view/media/history_view_web_page.h index 0946714ff..bf9254e65 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_web_page.h +++ b/Telegram/SourceFiles/history/view/media/history_view_web_page.h @@ -50,6 +50,8 @@ public: bool toggleSelectionByHandlerClick( const ClickHandlerPtr &p) const override; + bool allowTextSelectionByHandler( + const ClickHandlerPtr &p) const override; bool dragItemByHandler(const ClickHandlerPtr &p) const override; TextForMimeData selectedText(TextSelection selection) const override;