diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 80878834a9..ac7735ed23 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -951,6 +951,8 @@ PRIVATE history/view/history_view_draw_to_reply.h history/view/history_view_add_poll_option.cpp history/view/history_view_add_poll_option.h + history/view/history_view_element_overlay.cpp + history/view/history_view_element_overlay.h history/view/history_view_element.cpp history/view/history_view_element.h history/view/history_view_emoji_interactions.cpp diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index d1fdf16765..303f5ceaee 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -32,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_reaction_preview.h" #include "history/view/history_view_quick_action.h" #include "history/view/history_view_add_poll_option.h" +#include "history/view/history_view_element_overlay.h" #include "history/view/history_view_emoji_interactions.h" #include "history/view/history_view_top_peers_selector.h" #include "history/history_item_components.h" @@ -368,7 +369,10 @@ HistoryInner::HistoryInner( [=] { mouseActionUpdate(QCursor::pos()); setCursor(_cursor); }, [=] { return window()->isActiveWindow(); }) , _scrollDateCheck([this] { scrollDateCheck(); }) -, _scrollDateHideTimer([this] { scrollDateHideByTimer(); }) { +, _scrollDateHideTimer([this] { scrollDateHideByTimer(); }) +, _overlayHost(std::make_unique( + this, + [=](not_null view) { return itemTop(view); })) { _history->delegateMixin()->setCurrent(this); if (_migrated) { _migrated->delegateMixin()->setCurrent(this); @@ -1885,15 +1889,7 @@ void HistoryInner::mousePressEvent(QMouseEvent *e) { e->accept(); return; // ignore mouse press, that was hiding context menu } - if (_addPollOptionWidget && _addPollOptionView) { - const auto top = itemTop(_addPollOptionView); - const auto viewRect = (top >= 0) - ? QRect(0, top, width(), _addPollOptionView->height()) - : QRect(); - if (!viewRect.contains(e->pos())) { - hideAddPollOption(); - } - } + _overlayHost->handleClickOutside(e->pos()); if (_middleClickAutoscroll.active()) { _middleClickAutoscroll.stop(); e->accept(); @@ -2204,11 +2200,7 @@ void HistoryInner::itemRemoved(not_null item) { } void HistoryInner::viewRemoved(not_null view) { - if (_addPollOptionView == view) { - _addPollOptionWidget = nullptr; - _addPollOptionView = nullptr; - _addPollOptionContext = FullMsgId(); - } + _overlayHost->viewGone(view); const auto refresh = [&](auto &saved) { if (saved == view) { const auto now = viewByItem(view->data()); @@ -3974,7 +3966,7 @@ void HistoryInner::visibleAreaUpdated(int top, int bottom) { _visibleAreaTop, _visibleAreaBottom); - updateAddPollOptionPosition(); + _overlayHost->updatePosition(); } bool HistoryInner::displayScrollDate() const { @@ -4132,7 +4124,7 @@ void HistoryInner::leaveEventHook(QEvent *e) { } HistoryInner::~HistoryInner() { - hideAddPollOption(); + _overlayHost->hide(); _aboutView = nullptr; for (const auto &item : _animatedStickersPlayed) { if (const auto view = item->mainView()) { @@ -4309,91 +4301,45 @@ void HistoryInner::elementShowAddPollOption( not_null poll, FullMsgId context, QRect optionRect) { - showAddPollOption(view, poll, context); -} - -void HistoryInner::elementSubmitAddPollOption(FullMsgId context) { - if (_addPollOptionWidget && _addPollOptionContext == context) { - _addPollOptionWidget->triggerSubmit(); - } -} - -void HistoryInner::showAddPollOption( - not_null view, - not_null poll, - FullMsgId context) { - if (_addPollOptionWidget && _addPollOptionContext == context) { - hideAddPollOption(); - return; - } - hideAddPollOption(); - - _addPollOptionView = view; - _addPollOptionContext = context; - - if (const auto media = view->media()) { - media->setAddOptionActive(true); - } - - _addPollOptionWidget = base::make_unique_q< + auto widget = base::make_unique_q< HistoryView::AddPollOptionWidget>( this, poll, context, _controller); - - _addPollOptionWidget->submitted( - ) | rpl::on_next([=] { - hideAddPollOption(); - }, _addPollOptionWidget->lifetime()); - - _addPollOptionWidget->cancelled( - ) | rpl::on_next([=] { - hideAddPollOption(); - }, _addPollOptionWidget->lifetime()); - - updateAddPollOptionPosition(); - _addPollOptionWidget->show(); + const auto raw = widget.get(); + _overlayHost->show( + view, + context, + std::move(widget), + rpl::merge(raw->submitted(), raw->cancelled()), + [raw](not_null v, int top) { + const auto media = v->media(); + if (!media) { + return false; + } + const auto mediaPos = v->mediaTopLeft(); + const auto innerWidth = v->innerGeometry().width() + - st::msgPadding.left() + - st::msgPadding.right(); + const auto rect = media->addOptionRect(innerWidth); + raw->updatePosition( + QPoint( + mediaPos.x() + rect.x(), + top + mediaPos.y() + rect.y()), + rect.width()); + return true; + }, + [](not_null v, bool active) { + if (const auto media = v->media()) { + media->setAddOptionActive(active); + } + }, + [raw] { raw->triggerSubmit(); }); } -void HistoryInner::hideAddPollOption() { - if (!_addPollOptionWidget) { - return; - } - if (_addPollOptionView) { - if (const auto media = _addPollOptionView->media()) { - media->setAddOptionActive(false); - } - } - _addPollOptionWidget = nullptr; - _addPollOptionView = nullptr; - _addPollOptionContext = FullMsgId(); -} - -void HistoryInner::updateAddPollOptionPosition() { - if (!_addPollOptionWidget || !_addPollOptionView) { - return; - } - const auto view = _addPollOptionView; - const auto top = itemTop(view); - if (top < 0) { - return; - } - const auto media = view->media(); - if (!media) { - hideAddPollOption(); - return; - } - const auto mediaPos = view->mediaTopLeft(); - const auto innerWidth = view->innerGeometry().width() - - st::msgPadding.left() - - st::msgPadding.right(); - const auto rect = media->addOptionRect(innerWidth); - _addPollOptionWidget->updatePosition( - QPoint( - mediaPos.x() + rect.x(), - top + mediaPos.y() + rect.y()), - rect.width()); +void HistoryInner::elementSubmitAddPollOption(FullMsgId context) { + _overlayHost->triggerSubmit(context); } void HistoryInner::elementOpenPhoto( diff --git a/Telegram/SourceFiles/history/history_inner_widget.h b/Telegram/SourceFiles/history/history_inner_widget.h index 05d3968781..7254dc6345 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.h +++ b/Telegram/SourceFiles/history/history_inner_widget.h @@ -37,7 +37,7 @@ struct StateRequest; enum class CursorState : char; enum class PointState : char; enum class ElementChatMode : char; -class AddPollOptionWidget; +class ElementOverlayHost; class EmptyPainter; class Element; class TranslateTracker; @@ -262,13 +262,6 @@ protected: void contextMenuEvent(QContextMenuEvent *e) override; private: - void showAddPollOption( - not_null view, - not_null poll, - FullMsgId context); - void hideAddPollOption(); - void updateAddPollOptionPosition(); - void onTouchSelect(); void onTouchScrollTimer(); void markReadMetricsStale(); @@ -610,9 +603,7 @@ private: ClickHandlerPtr _scrollDateLink; ClickHandlerPtr _forumThreadBarLink; - base::unique_qptr _addPollOptionWidget; - Element *_addPollOptionView = nullptr; - FullMsgId _addPollOptionContext; + std::unique_ptr _overlayHost; }; diff --git a/Telegram/SourceFiles/history/view/history_view_element_overlay.cpp b/Telegram/SourceFiles/history/view/history_view_element_overlay.cpp new file mode 100644 index 0000000000..b98248a0b6 --- /dev/null +++ b/Telegram/SourceFiles/history/view/history_view_element_overlay.cpp @@ -0,0 +1,139 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "history/view/history_view_element_overlay.h" + +#include "history/view/history_view_element.h" +#include "ui/rp_widget.h" + +namespace HistoryView { + +ElementOverlayHost::ElementOverlayHost( + not_null container, + ItemTopFn itemTopFn) +: _container(container) +, _itemTopFn(std::move(itemTopFn)) { +} + +void ElementOverlayHost::show( + not_null view, + FullMsgId context, + base::unique_qptr widget, + rpl::producer<> closeRequests, + PositionFn positionFn, + MediaStateFn mediaStateFn, + Fn submitFn) { + if (_widget && _context == context) { + hide(); + return; + } + hide(); + + _view = view; + _context = context; + _positionFn = std::move(positionFn); + _mediaStateFn = std::move(mediaStateFn); + _submitFn = std::move(submitFn); + + if (_mediaStateFn) { + _mediaStateFn(view, true); + } + + _widget = std::move(widget); + + std::move( + closeRequests + ) | rpl::on_next([=] { + hide(); + }, _closeLifetime); + + updatePosition(); + _widget->show(); +} + +void ElementOverlayHost::hide() { + if (!_widget) { + return; + } + if (_view && _mediaStateFn) { + _mediaStateFn(_view, false); + } + _widget = nullptr; + _view = nullptr; + _context = FullMsgId(); + _positionFn = nullptr; + _mediaStateFn = nullptr; + _submitFn = nullptr; + _closeLifetime.destroy(); + if (const auto callback = _hiddenCallback) { + callback(); + } +} + +void ElementOverlayHost::viewGone(not_null view) { + if (_view != view.get()) { + return; + } + _widget = nullptr; + _view = nullptr; + _context = FullMsgId(); + _positionFn = nullptr; + _mediaStateFn = nullptr; + _submitFn = nullptr; + _closeLifetime.destroy(); + if (const auto callback = _hiddenCallback) { + callback(); + } +} + +void ElementOverlayHost::updatePosition() { + if (!_widget || !_view || !_positionFn) { + return; + } + const auto top = _itemTopFn(_view); + if (top < 0) { + return; + } + if (!_positionFn(_view, top)) { + hide(); + } +} + +bool ElementOverlayHost::handleClickOutside(QPoint clickPos) { + if (!_widget || !_view) { + return false; + } + const auto top = _itemTopFn(_view); + const auto viewRect = (top >= 0) + ? QRect(0, top, _container->width(), _view->height()) + : QRect(); + if (!viewRect.contains(clickPos)) { + hide(); + return true; + } + return false; +} + +void ElementOverlayHost::triggerSubmit(FullMsgId context) { + if (_widget && _context == context && _submitFn) { + _submitFn(); + } +} + +void ElementOverlayHost::setHiddenCallback(Fn callback) { + _hiddenCallback = std::move(callback); +} + +bool ElementOverlayHost::active() const { + return _widget != nullptr; +} + +FullMsgId ElementOverlayHost::context() const { + return _context; +} + +} // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/history_view_element_overlay.h b/Telegram/SourceFiles/history/view/history_view_element_overlay.h new file mode 100644 index 0000000000..4cd1252812 --- /dev/null +++ b/Telegram/SourceFiles/history/view/history_view_element_overlay.h @@ -0,0 +1,64 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "base/unique_qptr.h" +#include "data/data_messages.h" + +namespace Ui { +class RpWidget; +} // namespace Ui + +namespace HistoryView { + +class Element; + +class ElementOverlayHost final { +public: + using ItemTopFn = Fn)>; + using PositionFn = Fn, int itemTop)>; + using MediaStateFn = Fn, bool active)>; + + explicit ElementOverlayHost( + not_null container, + ItemTopFn itemTopFn); + + void show( + not_null view, + FullMsgId context, + base::unique_qptr widget, + rpl::producer<> closeRequests, + PositionFn positionFn, + MediaStateFn mediaStateFn = nullptr, + Fn submitFn = nullptr); + + void hide(); + void viewGone(not_null view); + void updatePosition(); + bool handleClickOutside(QPoint clickPos); + void triggerSubmit(FullMsgId context); + void setHiddenCallback(Fn callback); + + [[nodiscard]] bool active() const; + [[nodiscard]] FullMsgId context() const; + +private: + const not_null _container; + const ItemTopFn _itemTopFn; + + base::unique_qptr _widget; + Element *_view = nullptr; + FullMsgId _context; + PositionFn _positionFn; + MediaStateFn _mediaStateFn; + Fn _submitFn; + Fn _hiddenCallback; + rpl::lifetime _closeLifetime; +}; + +} // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index 42c7e92ecf..7981e221b4 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_translate_tracker.h" #include "history/view/history_view_read_metrics_tracker.h" #include "history/view/history_view_add_poll_option.h" +#include "history/view/history_view_element_overlay.h" #include "data/data_poll.h" #include "history/view/history_view_top_peers_selector.h" #include "history/view/history_view_quick_action.h" @@ -439,9 +440,15 @@ ListWidget::ListWidget( [=](int d) { _delegate->listScrollTo(_visibleTop + d, false); }, [=](const QCursor &cursor) { setCursor(cursor); }, [=] { mouseActionUpdate(QCursor::pos()); setCursor(_cursor); }, - [=] { return window()->isActiveWindow(); }) { + [=] { return window()->isActiveWindow(); }) +, _overlayHost(std::make_unique( + this, + [=](not_null view) { return itemTop(view); })) { setAttribute(Qt::WA_AcceptTouchEvents); setMouseTracking(true); + _overlayHost->setHiddenCallback([=] { + _delegate->listWindowSetInnerFocus(); + }); if (_readMetricsTracker) { Core::App().inAppKeyPressed( ) | rpl::on_next([=] { @@ -1176,7 +1183,7 @@ void ListWidget::visibleTopBottomUpdated( _applyUpdatedScrollState.call(); _emojiInteractions->visibleAreaUpdated(_visibleTop, _visibleBottom); - updateAddPollOptionPosition(); + _overlayHost->updatePosition(); } void ListWidget::applyUpdatedScrollState() { @@ -1897,91 +1904,44 @@ void ListWidget::elementShowAddPollOption( not_null poll, FullMsgId context, QRect optionRect) { - showAddPollOption(view, poll, context); -} - -void ListWidget::elementSubmitAddPollOption(FullMsgId context) { - if (_addPollOptionWidget && _addPollOptionContext == context) { - _addPollOptionWidget->triggerSubmit(); - } -} - -void ListWidget::showAddPollOption( - not_null view, - not_null poll, - FullMsgId context) { - if (_addPollOptionWidget && _addPollOptionContext == context) { - hideAddPollOption(); - return; - } - hideAddPollOption(); - - _addPollOptionView = view; - _addPollOptionContext = context; - - if (const auto media = view->media()) { - media->setAddOptionActive(true); - } - - _addPollOptionWidget = base::make_unique_q( + auto widget = base::make_unique_q( this, poll, context, controller()); - - _addPollOptionWidget->submitted( - ) | rpl::on_next([=] { - hideAddPollOption(); - }, _addPollOptionWidget->lifetime()); - - _addPollOptionWidget->cancelled( - ) | rpl::on_next([=] { - hideAddPollOption(); - }, _addPollOptionWidget->lifetime()); - - updateAddPollOptionPosition(); - _addPollOptionWidget->show(); + const auto raw = widget.get(); + _overlayHost->show( + view, + context, + std::move(widget), + rpl::merge(raw->submitted(), raw->cancelled()), + [raw](not_null v, int top) { + const auto media = v->media(); + if (!media) { + return false; + } + const auto mediaPos = v->mediaTopLeft(); + const auto innerWidth = v->innerGeometry().width() + - st::msgPadding.left() + - st::msgPadding.right(); + const auto rect = media->addOptionRect(innerWidth); + raw->updatePosition( + QPoint( + mediaPos.x() + rect.x(), + top + mediaPos.y() + rect.y()), + rect.width()); + return true; + }, + [](not_null v, bool active) { + if (const auto media = v->media()) { + media->setAddOptionActive(active); + } + }, + [raw] { raw->triggerSubmit(); }); } -void ListWidget::hideAddPollOption() { - if (!_addPollOptionWidget) { - return; - } - if (_addPollOptionView) { - if (const auto media = _addPollOptionView->media()) { - media->setAddOptionActive(false); - } - } - _addPollOptionWidget = nullptr; - _addPollOptionView = nullptr; - _addPollOptionContext = FullMsgId(); - _delegate->listWindowSetInnerFocus(); -} - -void ListWidget::updateAddPollOptionPosition() { - if (!_addPollOptionWidget || !_addPollOptionView) { - return; - } - const auto view = _addPollOptionView; - const auto top = itemTop(view); - if (top < 0) { - return; - } - const auto media = view->media(); - if (!media) { - hideAddPollOption(); - return; - } - const auto mediaPos = view->mediaTopLeft(); - const auto innerWidth = view->innerGeometry().width() - - st::msgPadding.left() - - st::msgPadding.right(); - const auto rect = media->addOptionRect(innerWidth); - _addPollOptionWidget->updatePosition( - QPoint( - mediaPos.x() + rect.x(), - top + mediaPos.y() + rect.y()), - rect.width()); +void ListWidget::elementSubmitAddPollOption(FullMsgId context) { + _overlayHost->triggerSubmit(context); } void ListWidget::elementOpenPhoto( @@ -3164,15 +3124,7 @@ void ListWidget::mousePressEvent(QMouseEvent *e) { e->accept(); return; // ignore mouse press, that was hiding context menu } - if (_addPollOptionWidget && _addPollOptionView) { - const auto top = itemTop(_addPollOptionView); - const auto viewRect = (top >= 0) - ? QRect(0, top, width(), _addPollOptionView->height()) - : QRect(); - if (!viewRect.contains(e->pos())) { - hideAddPollOption(); - } - } + _overlayHost->handleClickOutside(e->pos()); if (_middleClickAutoscroll.active()) { _middleClickAutoscroll.stop(); e->accept(); @@ -4461,9 +4413,7 @@ void ListWidget::showItemHighlight(not_null item) { } void ListWidget::viewReplaced(not_null was, Element *now) { - if (_addPollOptionView == was) { - hideAddPollOption(); - } + _overlayHost->viewGone(was); if (_visibleTopItem == was) _visibleTopItem = now; if (_scrollDateLastItem == was) _scrollDateLastItem = now; if (_overElement == was) _overElement = now; diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.h b/Telegram/SourceFiles/history/view/history_view_list_widget.h index 650a286af7..35de405114 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.h +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.h @@ -66,7 +66,7 @@ namespace HistoryView { struct TextState; struct StateRequest; -class AddPollOptionWidget; +class ElementOverlayHost; class EmojiInteractions; class TranslateTracker; class ReadMetricsTracker; @@ -547,13 +547,6 @@ private: Deselecting, }; - void showAddPollOption( - not_null view, - not_null poll, - FullMsgId context); - void hideAddPollOption(); - void updateAddPollOptionPosition(); - void onTouchSelect(); void onTouchScrollTimer(); void markReadMetricsStale(); @@ -907,9 +900,7 @@ private: rpl::event_stream _requestedToShowMessage; rpl::event_stream> _scrollKeyEvents; - base::unique_qptr _addPollOptionWidget; - Element *_addPollOptionView = nullptr; - FullMsgId _addPollOptionContext; + std::unique_ptr _overlayHost; rpl::lifetime _viewerLifetime;