diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp index d5a4b59c7..57c11d595 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.cpp @@ -508,6 +508,10 @@ void InnerWidget::elementAnimationAutoplayAsync( }); } +TimeMs InnerWidget::elementHighlightTime( + not_null element) { + return TimeMs(0); +} void InnerWidget::saveState(not_null memento) { memento->setFilter(std::move(_filter)); diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h index 2537f8ff5..4d3788b74 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h @@ -85,6 +85,8 @@ public: not_null view) override; void elementAnimationAutoplayAsync( not_null view) override; + TimeMs elementHighlightTime( + not_null element) override; ~InnerWidget(); diff --git a/Telegram/SourceFiles/history/feed/history_feed_section.cpp b/Telegram/SourceFiles/history/feed/history_feed_section.cpp index cfbda173b..7fb202ca5 100644 --- a/Telegram/SourceFiles/history/feed/history_feed_section.cpp +++ b/Telegram/SourceFiles/history/feed/history_feed_section.cpp @@ -159,7 +159,11 @@ void Widget::scrollDownClicked() { } void Widget::showAtPosition(Data::MessagePosition position) { - if (!showAtPositionNow(position)) { + if (showAtPositionNow(position)) { + if (const auto highlight = base::take(_highlightMessageId)) { + _inner->highlightMessage(highlight); + } + } else { _nextAnimatedScrollPosition = position; _nextAnimatedScrollDelta = _inner->isBelowPosition(position) ? -_scroll->height() @@ -424,6 +428,9 @@ void Widget::listContentRefreshed() { position, _nextAnimatedScrollDelta, HistoryView::ListWidget::AnimatedScroll::Part); + if (const auto highlight = base::take(_highlightMessageId)) { + _inner->highlightMessage(highlight); + } } } @@ -447,7 +454,7 @@ void Widget::restoreState(not_null memento) { _undefinedAroundPosition = !list->aroundPosition(); _inner->restoreState(memento->list()); if (const auto position = memento->position()) { - _currentMessageId = position.fullId; + _currentMessageId = _highlightMessageId = position.fullId; showAtPosition(position); } } diff --git a/Telegram/SourceFiles/history/feed/history_feed_section.h b/Telegram/SourceFiles/history/feed/history_feed_section.h index 8210d345f..2122d7eb8 100644 --- a/Telegram/SourceFiles/history/feed/history_feed_section.h +++ b/Telegram/SourceFiles/history/feed/history_feed_section.h @@ -129,6 +129,7 @@ private: std::unique_ptr _emptyTextView; FullMsgId _currentMessageId; + FullMsgId _highlightMessageId; base::optional _nextAnimatedScrollPosition; int _nextAnimatedScrollDelta = 0; diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 2e74a7f3b..9147caa1d 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -3006,6 +3006,18 @@ not_null HistoryInner::ElementDelegate() { } }); } + TimeMs elementHighlightTime( + not_null view) override { + const auto fullAnimMs = App::main()->highlightStartTime( + view->data()); + if (fullAnimMs > 0) { + const auto now = getms(); + if (fullAnimMs < now) { + return now - fullAnimMs; + } + } + return TimeMs(0); + } }; diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp index a84ae7957..11a0a77e2 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.cpp +++ b/Telegram/SourceFiles/history/view/history_view_element.cpp @@ -174,26 +174,34 @@ void Element::setY(int y) { _y = y; } -int Element::marginTop() const { - const auto item = data(); - auto result = 0; - if (!isHidden()) { - if (isAttachedToPrevious()) { - result += st::msgMarginTopAttached; - } else { - result += st::msgMargin.top(); - } +void Element::paintHighlight( + Painter &p, + int geometryHeight) const { + const auto animms = delegate()->elementHighlightTime(this); + if (!animms + || animms >= st::activeFadeInDuration + st::activeFadeOutDuration) { + return; } - result += displayedDateHeight(); - if (const auto bar = Get()) { - result += bar->height(); - } - return result; -} -int Element::marginBottom() const { - const auto item = data(); - return isHidden() ? 0 : st::msgMargin.bottom(); + const auto top = marginTop(); + const auto bottom = marginBottom(); + const auto fill = qMin(top, bottom); + const auto skiptop = top - fill; + const auto fillheight = fill + geometryHeight + fill; + + const auto dt = (animms > st::activeFadeInDuration) + ? (1. - (animms - st::activeFadeInDuration) + / float64(st::activeFadeOutDuration)) + : (animms / float64(st::activeFadeInDuration)); + const auto o = p.opacity(); + p.setOpacity(o * dt); + p.fillRect( + 0, + skiptop, + width(), + fillheight, + st::defaultTextPalette.selectOverlay); + p.setOpacity(o); } bool Element::isUnderCursor() const { diff --git a/Telegram/SourceFiles/history/view/history_view_element.h b/Telegram/SourceFiles/history/view/history_view_element.h index a020d49a1..18d82a170 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.h +++ b/Telegram/SourceFiles/history/view/history_view_element.h @@ -42,6 +42,8 @@ public: virtual bool elementUnderCursor(not_null view) = 0; virtual void elementAnimationAutoplayAsync( not_null element) = 0; + virtual TimeMs elementHighlightTime( + not_null element) = 0; }; @@ -126,8 +128,9 @@ public: int y() const; void setY(int y); - int marginTop() const; - int marginBottom() const; + virtual int marginTop() const = 0; + virtual int marginBottom() const = 0; + void setPendingResize(); bool pendingResize() const; bool isUnderCursor() const; @@ -242,8 +245,9 @@ public: virtual ~Element(); protected: - void setInitialSize(int maxWidth, int minHeight); - void setCurrentSize(int width, int height); + void paintHighlight( + Painter &p, + int geometryHeight) const; private: // This should be called only from previousInBlocksChanged() diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index 569993403..7cda2aa20 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -239,7 +239,8 @@ ListWidget::ListWidget( , _itemAverageHeight(itemMinimalHeight()) , _scrollDateCheck([this] { scrollDateCheck(); }) , _applyUpdatedScrollState([this] { applyUpdatedScrollState(); }) -, _selectEnabled(_delegate->listAllowsMultiSelect()) { +, _selectEnabled(_delegate->listAllowsMultiSelect()) +, _highlightTimer([this] { updateHighlightedMessage(); }) { setMouseTracking(true); _scrollDateHideTimer.setCallback([this] { scrollDateHideByTimer(); }); Auth().data().viewRepaintRequest( @@ -417,6 +418,32 @@ bool ListWidget::isBelowPosition(Data::MessagePosition position) const { return _items.front()->data()->position() > position; } +void ListWidget::highlightMessage(FullMsgId itemId) { + if (const auto item = App::histItemById(itemId)) { + if (const auto view = viewForItem(item)) { + _highlightStart = getms(); + _highlightedMessageId = itemId; + _highlightTimer.callEach(AnimationTimerDelta); + + repaintItem(view); + } + } +} + +void ListWidget::updateHighlightedMessage() { + if (const auto item = App::histItemById(_highlightedMessageId)) { + if (const auto view = viewForItem(item)) { + repaintItem(view); + auto duration = st::activeFadeInDuration + st::activeFadeOutDuration; + if (getms() - _highlightStart <= duration) { + return; + } + } + } + _highlightTimer.cancel(); + _highlightedMessageId = FullMsgId(); +} + void ListWidget::checkUnreadBarCreation() { if (!_unreadBarElement) { if (const auto index = _delegate->listUnreadBarView(_items)) { @@ -1071,6 +1098,16 @@ void ListWidget::elementAnimationAutoplayAsync( }); } +TimeMs ListWidget::elementHighlightTime( + not_null element) { + if (element->data()->fullId() == _highlightedMessageId) { + if (_highlightTimer.isActive()) { + return getms() - _highlightStart; + } + } + return TimeMs(0); +} + void ListWidget::saveState(not_null memento) { memento->setAroundPosition(_aroundPosition); auto state = countScrollState(); diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.h b/Telegram/SourceFiles/history/view/history_view_list_widget.h index 5aad08c92..5dcd551d8 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.h +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.h @@ -149,6 +149,7 @@ public: AnimatedScroll type); bool isAbovePosition(Data::MessagePosition position) const; bool isBelowPosition(Data::MessagePosition position) const; + void highlightMessage(FullMsgId itemId); TextWithEntities getSelectedText() const; MessageIdsList getSelectedItems() const; @@ -175,6 +176,7 @@ public: bool elementUnderCursor(not_null view) override; void elementAnimationAutoplayAsync( not_null view) override; + TimeMs elementHighlightTime(not_null element) override; ~ListWidget(); @@ -378,6 +380,8 @@ private: void applyUpdatedScrollState(); void scrollToAnimationCallback(FullMsgId attachToId); + void updateHighlightedMessage(); + // This function finds all history items that are displayed and calls template method // for each found message (in given direction) in the passed history with passed top offset. // @@ -469,6 +473,10 @@ private: QPoint _trippleClickPoint; TimeMs _trippleClickStartTime = 0; + TimeMs _highlightStart = 0; + FullMsgId _highlightedMessageId; + base::Timer _highlightTimer; + rpl::lifetime _viewerLifetime; }; diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index 586b75056..f726a52af 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -342,6 +342,26 @@ QSize Message::performCountOptimalSize() { return QSize(maxWidth, minHeight); } +int Message::marginTop() const { + auto result = 0; + if (!isHidden()) { + if (isAttachedToPrevious()) { + result += st::msgMarginTopAttached; + } else { + result += st::msgMargin.top(); + } + } + result += displayedDateHeight(); + if (const auto bar = Get()) { + result += bar->height(); + } + return result; +} + +int Message::marginBottom() const { + return isHidden() ? 0 : st::msgMargin.bottom(); +} + void Message::draw( Painter &p, QRect clip, @@ -376,25 +396,11 @@ void Message::draw( return; } - auto fullAnimMs = App::main() ? App::main()->highlightStartTime(item) : 0LL; - if (fullAnimMs > 0 && fullAnimMs <= ms) { - auto animms = ms - fullAnimMs; - if (animms < st::activeFadeInDuration + st::activeFadeOutDuration) { - auto top = marginTop(); - auto bottom = marginBottom(); - auto fill = qMin(top, bottom); - auto skiptop = top - fill; - auto fillheight = fill + g.height() + fill; + paintHighlight(p, g.height()); - auto dt = (animms > st::activeFadeInDuration) ? (1. - (animms - st::activeFadeInDuration) / float64(st::activeFadeOutDuration)) : (animms / float64(st::activeFadeInDuration)); - auto o = p.opacity(); - p.setOpacity(o * dt); - p.fillRect(0, skiptop, width(), fillheight, st::defaultTextPalette.selectOverlay); - p.setOpacity(o); - } - } - - p.setTextPalette(selected ? (outbg ? st::outTextPaletteSelected : st::inTextPaletteSelected) : (outbg ? st::outTextPalette : st::inTextPalette)); + p.setTextPalette(selected + ? (outbg ? st::outTextPaletteSelected : st::inTextPaletteSelected) + : (outbg ? st::outTextPalette : st::inTextPalette)); auto keyboard = item->inlineReplyKeyboard(); if (keyboard) { diff --git a/Telegram/SourceFiles/history/view/history_view_message.h b/Telegram/SourceFiles/history/view/history_view_message.h index 09f82592e..1190f78a5 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.h +++ b/Telegram/SourceFiles/history/view/history_view_message.h @@ -32,6 +32,8 @@ public: not_null delegate, not_null data); + int marginTop() const override; + int marginBottom() const override; void draw( Painter &p, QRect clip, diff --git a/Telegram/SourceFiles/history/view/history_view_service_message.cpp b/Telegram/SourceFiles/history/view/history_view_service_message.cpp index aa1f02f59..397953068 100644 --- a/Telegram/SourceFiles/history/view/history_view_service_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_service_message.cpp @@ -373,6 +373,14 @@ bool Service::isHidden() const { return Element::isHidden(); } +int Service::marginTop() const { + return st::msgServiceMargin.top(); +} + +int Service::marginBottom() const { + return st::msgServiceMargin.bottom(); +} + void Service::draw( Painter &p, QRect clip, @@ -410,23 +418,7 @@ void Service::draw( return; } - auto fullAnimMs = App::main() ? App::main()->highlightStartTime(item) : 0LL; - if (fullAnimMs > 0 && fullAnimMs <= ms) { - auto animms = ms - fullAnimMs; - if (animms < st::activeFadeInDuration + st::activeFadeOutDuration) { - auto top = st::msgServiceMargin.top(); - auto bottom = st::msgServiceMargin.bottom(); - auto fill = qMin(top, bottom); - auto skiptop = top - fill; - auto fillheight = fill + height + fill; - - auto dt = (animms > st::activeFadeInDuration) ? (1. - (animms - st::activeFadeInDuration) / float64(st::activeFadeOutDuration)) : (animms / float64(st::activeFadeInDuration)); - auto o = p.opacity(); - p.setOpacity(o * dt); - p.fillRect(0, skiptop, width(), fillheight, st::defaultTextPalette.selectOverlay); - p.setOpacity(o); - } - } + paintHighlight(p, height); p.setTextPalette(st::serviceTextPalette); diff --git a/Telegram/SourceFiles/history/view/history_view_service_message.h b/Telegram/SourceFiles/history/view/history_view_service_message.h index ea673310c..74776ed43 100644 --- a/Telegram/SourceFiles/history/view/history_view_service_message.h +++ b/Telegram/SourceFiles/history/view/history_view_service_message.h @@ -19,6 +19,8 @@ public: not_null delegate, not_null data); + int marginTop() const override; + int marginBottom() const override; bool isHidden() const override; void draw( Painter &p,