Highlight album part that had a reply clicked.

This commit is contained in:
John Preston 2020-12-30 12:55:11 +04:00
parent 39f9147790
commit 3a34881488
19 changed files with 154 additions and 63 deletions

View file

@ -556,7 +556,7 @@ bool InnerWidget::elementUnderCursor(
} }
crl::time InnerWidget::elementHighlightTime( crl::time InnerWidget::elementHighlightTime(
not_null<const HistoryView::Element*> element) { not_null<const HistoryItem*> item) {
return crl::time(0); return crl::time(0);
} }

View file

@ -97,7 +97,7 @@ public:
bool elementUnderCursor( bool elementUnderCursor(
not_null<const HistoryView::Element*> view) override; not_null<const HistoryView::Element*> view) override;
crl::time elementHighlightTime( crl::time elementHighlightTime(
not_null<const HistoryView::Element*> element) override; not_null<const HistoryItem*> item) override;
bool elementInSelectionMode() override; bool elementInSelectionMode() override;
bool elementIntersectsRange( bool elementIntersectsRange(
not_null<const HistoryView::Element*> view, not_null<const HistoryView::Element*> view,

View file

@ -2520,9 +2520,9 @@ void HistoryInner::elementStartStickerLoop(
_animatedStickersPlayed.emplace(view->data()); _animatedStickersPlayed.emplace(view->data());
} }
crl::time HistoryInner::elementHighlightTime(not_null<const Element*> view) { crl::time HistoryInner::elementHighlightTime(
const auto fullAnimMs = _controller->content()->highlightStartTime( not_null<const HistoryItem*> item) {
view->data()); const auto fullAnimMs = _controller->content()->highlightStartTime(item);
if (fullAnimMs > 0) { if (fullAnimMs > 0) {
const auto now = crl::now(); const auto now = crl::now();
if (fullAnimMs < now) { if (fullAnimMs < now) {
@ -3421,8 +3421,8 @@ not_null<HistoryView::ElementDelegate*> HistoryInner::ElementDelegate() {
return (App::hoveredItem() == view); return (App::hoveredItem() == view);
} }
crl::time elementHighlightTime( crl::time elementHighlightTime(
not_null<const Element*> view) override { not_null<const HistoryItem*> item) override {
return Instance ? Instance->elementHighlightTime(view) : 0; return Instance ? Instance->elementHighlightTime(item) : 0;
} }
bool elementInSelectionMode() override { bool elementInSelectionMode() override {
return Instance ? Instance->inSelectionMode() : false; return Instance ? Instance->inSelectionMode() : false;

View file

@ -84,7 +84,7 @@ public:
int till) const; int till) const;
void elementStartStickerLoop(not_null<const Element*> view); void elementStartStickerLoop(not_null<const Element*> view);
[[nodiscard]] crl::time elementHighlightTime( [[nodiscard]] crl::time elementHighlightTime(
not_null<const Element*> view); not_null<const HistoryItem*> item);
void elementShowPollResults( void elementShowPollResults(
not_null<PollData*> poll, not_null<PollData*> poll,
FullMsgId context); FullMsgId context);

View file

@ -1045,11 +1045,6 @@ void HistoryWidget::scrollToAnimationCallback(
void HistoryWidget::enqueueMessageHighlight( void HistoryWidget::enqueueMessageHighlight(
not_null<HistoryView::Element*> view) { not_null<HistoryView::Element*> view) {
if (const auto group = session().data().groups().find(view->data())) {
if (const auto leader = group->items.front()->mainView()) {
view = leader;
}
}
auto enqueueMessageId = [this](MsgId universalId) { auto enqueueMessageId = [this](MsgId universalId) {
if (_highlightQueue.empty() && !_highlightTimer.isActive()) { if (_highlightQueue.empty() && !_highlightTimer.isActive()) {
highlightMessage(universalId); highlightMessage(universalId);
@ -1096,7 +1091,7 @@ void HistoryWidget::checkNextHighlight() {
void HistoryWidget::updateHighlightedMessage() { void HistoryWidget::updateHighlightedMessage() {
const auto item = getItemFromHistoryOrMigrated(_highlightedMessageId); const auto item = getItemFromHistoryOrMigrated(_highlightedMessageId);
const auto view = item ? item->mainView() : nullptr; auto view = item ? item->mainView() : nullptr;
if (!view) { if (!view) {
return stopMessageHighlight(); return stopMessageHighlight();
} }
@ -1105,6 +1100,11 @@ void HistoryWidget::updateHighlightedMessage() {
return stopMessageHighlight(); return stopMessageHighlight();
} }
if (const auto group = session().data().groups().find(view->data())) {
if (const auto leader = group->items.front()->mainView()) {
view = leader;
}
}
session().data().requestViewRepaint(view); session().data().requestViewRepaint(view);
} }

View file

@ -79,7 +79,7 @@ bool SimpleElementDelegate::elementUnderCursor(
} }
crl::time SimpleElementDelegate::elementHighlightTime( crl::time SimpleElementDelegate::elementHighlightTime(
not_null<const Element*> element) { not_null<const HistoryItem*> item) {
return crl::time(0); return crl::time(0);
} }
@ -280,29 +280,44 @@ void Element::refreshDataIdHook() {
void Element::paintHighlight( void Element::paintHighlight(
Painter &p, Painter &p,
int geometryHeight) const { int geometryHeight) const {
const auto animms = delegate()->elementHighlightTime(this);
if (!animms
|| animms >= st::activeFadeInDuration + st::activeFadeOutDuration) {
return;
}
const auto top = marginTop(); const auto top = marginTop();
const auto bottom = marginBottom(); const auto bottom = marginBottom();
const auto fill = qMin(top, bottom); const auto fill = qMin(top, bottom);
const auto skiptop = top - fill; const auto skiptop = top - fill;
const auto fillheight = fill + geometryHeight + fill; const auto fillheight = fill + geometryHeight + fill;
const auto dt = (animms > st::activeFadeInDuration) paintCustomHighlight(p, skiptop, fillheight, data());
}
float64 Element::highlightOpacity(not_null<const HistoryItem*> item) const {
const auto animms = delegate()->elementHighlightTime(item);
if (!animms
|| animms >= st::activeFadeInDuration + st::activeFadeOutDuration) {
return 0.;
}
return (animms > st::activeFadeInDuration)
? (1. - (animms - st::activeFadeInDuration) ? (1. - (animms - st::activeFadeInDuration)
/ float64(st::activeFadeOutDuration)) / float64(st::activeFadeOutDuration))
: (animms / float64(st::activeFadeInDuration)); : (animms / float64(st::activeFadeInDuration));
}
void Element::paintCustomHighlight(
Painter &p,
int y,
int height,
not_null<const HistoryItem*> item) const {
const auto opacity = highlightOpacity(item);
if (opacity == 0.) {
return;
}
const auto o = p.opacity(); const auto o = p.opacity();
p.setOpacity(o * dt); p.setOpacity(o * opacity);
p.fillRect( p.fillRect(
0, 0,
skiptop, y,
width(), width(),
fillheight, height,
st::defaultTextPalette.selectOverlay); st::defaultTextPalette.selectOverlay);
p.setOpacity(o); p.setOpacity(o);
} }

View file

@ -51,7 +51,7 @@ public:
Element *replacing = nullptr) = 0; Element *replacing = nullptr) = 0;
virtual bool elementUnderCursor(not_null<const Element*> view) = 0; virtual bool elementUnderCursor(not_null<const Element*> view) = 0;
virtual crl::time elementHighlightTime( virtual crl::time elementHighlightTime(
not_null<const Element*> element) = 0; not_null<const HistoryItem*> item) = 0;
virtual bool elementInSelectionMode() = 0; virtual bool elementInSelectionMode() = 0;
virtual bool elementIntersectsRange( virtual bool elementIntersectsRange(
not_null<const Element*> view, not_null<const Element*> view,
@ -87,7 +87,7 @@ public:
Element *replacing = nullptr) override; Element *replacing = nullptr) override;
bool elementUnderCursor(not_null<const Element*> view) override; bool elementUnderCursor(not_null<const Element*> view) override;
crl::time elementHighlightTime( crl::time elementHighlightTime(
not_null<const Element*> element) override; not_null<const HistoryItem*> item) override;
bool elementInSelectionMode() override; bool elementInSelectionMode() override;
bool elementIntersectsRange( bool elementIntersectsRange(
not_null<const Element*> view, not_null<const Element*> view,
@ -301,6 +301,13 @@ public:
virtual void unloadHeavyPart(); virtual void unloadHeavyPart();
void checkHeavyPart(); void checkHeavyPart();
void paintCustomHighlight(
Painter &p,
int y,
int height,
not_null<const HistoryItem*> item) const;
float64 highlightOpacity(not_null<const HistoryItem*> item) const;
// Legacy blocks structure. // Legacy blocks structure.
HistoryBlock *block(); HistoryBlock *block();
const HistoryBlock *block() const; const HistoryBlock *block() const;

View file

@ -482,7 +482,7 @@ void ListWidget::highlightMessage(FullMsgId itemId) {
_highlightedMessageId = itemId; _highlightedMessageId = itemId;
_highlightTimer.callEach(AnimationTimerDelta); _highlightTimer.callEach(AnimationTimerDelta);
repaintItem(view); repaintHighlightedItem(view);
} }
} }
} }
@ -496,10 +496,24 @@ void ListWidget::showAroundPosition(
refreshViewer(); refreshViewer();
} }
void ListWidget::repaintHighlightedItem(not_null<const Element*> view) {
if (view->isHiddenByGroup()) {
if (const auto group = session().data().groups().find(view->data())) {
if (const auto leader = viewForItem(group->items.front())) {
if (!leader->isHiddenByGroup()) {
repaintItem(leader);
return;
}
}
}
}
repaintItem(view);
}
void ListWidget::updateHighlightedMessage() { void ListWidget::updateHighlightedMessage() {
if (const auto item = session().data().message(_highlightedMessageId)) { if (const auto item = session().data().message(_highlightedMessageId)) {
if (const auto view = viewForItem(item)) { if (const auto view = viewForItem(item)) {
repaintItem(view); repaintHighlightedItem(view);
auto duration = st::activeFadeInDuration + st::activeFadeOutDuration; auto duration = st::activeFadeInDuration + st::activeFadeOutDuration;
if (crl::now() - _highlightStart <= duration) { if (crl::now() - _highlightStart <= duration) {
return; return;
@ -1244,8 +1258,8 @@ bool ListWidget::elementUnderCursor(
} }
crl::time ListWidget::elementHighlightTime( crl::time ListWidget::elementHighlightTime(
not_null<const HistoryView::Element*> element) { not_null<const HistoryItem*> item) {
if (element->data()->fullId() == _highlightedMessageId) { if (item->fullId() == _highlightedMessageId) {
if (_highlightTimer.isActive()) { if (_highlightTimer.isActive()) {
return crl::now() - _highlightStart; return crl::now() - _highlightStart;
} }

View file

@ -221,7 +221,7 @@ public:
Element *replacing = nullptr) override; Element *replacing = nullptr) override;
bool elementUnderCursor(not_null<const Element*> view) override; bool elementUnderCursor(not_null<const Element*> view) override;
crl::time elementHighlightTime( crl::time elementHighlightTime(
not_null<const Element*> element) override; not_null<const HistoryItem*> item) override;
bool elementInSelectionMode() override; bool elementInSelectionMode() override;
bool elementIntersectsRange( bool elementIntersectsRange(
not_null<const Element*> view, not_null<const Element*> view,
@ -340,6 +340,7 @@ private:
int itemTop(not_null<const Element*> view) const; int itemTop(not_null<const Element*> view) const;
void repaintItem(FullMsgId itemId); void repaintItem(FullMsgId itemId);
void repaintItem(const Element *view); void repaintItem(const Element *view);
void repaintHighlightedItem(not_null<const Element*> view);
void resizeItem(not_null<Element*> view); void resizeItem(not_null<Element*> view);
void refreshItem(not_null<const Element*> view); void refreshItem(not_null<const Element*> view);
void itemRemoved(not_null<const HistoryItem*> item); void itemRemoved(not_null<const HistoryItem*> item);

View file

@ -570,7 +570,40 @@ void Message::draw(
return; return;
} }
paintHighlight(p, g.height()); auto entry = logEntryOriginal();
auto mediaDisplayed = media && media->isDisplayed();
// Entry page is always a bubble bottom.
auto mediaOnBottom = (mediaDisplayed && media->isBubbleBottom()) || (entry/* && entry->isBubbleBottom()*/);
auto mediaOnTop = (mediaDisplayed && media->isBubbleTop()) || (entry && entry->isBubbleTop());
auto mediaSelectionIntervals = (!selected && mediaDisplayed)
? media->getBubbleSelectionIntervals(selection)
: std::vector<BubbleSelectionInterval>();
auto localMediaTop = 0;
const auto customHighlight = mediaDisplayed && media->customHighlight();
if (!mediaSelectionIntervals.empty() || customHighlight) {
auto localMediaBottom = g.top() + g.height();
if (data()->repliesAreComments() || data()->externalReply()) {
localMediaBottom -= st::historyCommentsButtonHeight;
}
if (!mediaOnBottom) {
localMediaBottom -= st::msgPadding.bottom();
}
if (entry) {
localMediaBottom -= entry->height();
}
localMediaTop = localMediaBottom - media->height();
for (auto &[top, height] : mediaSelectionIntervals) {
top += localMediaTop;
}
}
if (customHighlight) {
media->drawHighlight(p, localMediaTop);
} else {
paintHighlight(p, g.height());
}
const auto roll = media ? media->bubbleRoll() : Media::BubbleRoll(); const auto roll = media ? media->bubbleRoll() : Media::BubbleRoll();
if (roll) { if (roll) {
@ -602,34 +635,6 @@ void Message::draw(
fromNameUpdated(g.width()); fromNameUpdated(g.width());
} }
auto entry = logEntryOriginal();
auto mediaDisplayed = media && media->isDisplayed();
// Entry page is always a bubble bottom.
auto mediaOnBottom = (mediaDisplayed && media->isBubbleBottom()) || (entry/* && entry->isBubbleBottom()*/);
auto mediaOnTop = (mediaDisplayed && media->isBubbleTop()) || (entry && entry->isBubbleTop());
auto mediaSelectionIntervals = (!selected && mediaDisplayed)
? media->getBubbleSelectionIntervals(selection)
: std::vector<BubbleSelectionInterval>();
if (!mediaSelectionIntervals.empty()) {
auto localMediaBottom = g.top() + g.height();
if (data()->repliesAreComments() || data()->externalReply()) {
localMediaBottom -= st::historyCommentsButtonHeight;
}
if (!mediaOnBottom) {
localMediaBottom -= st::msgPadding.bottom();
}
if (entry) {
localMediaBottom -= entry->height();
}
const auto localMediaTop = localMediaBottom - media->height();
for (auto &[top, height] : mediaSelectionIntervals) {
top += localMediaTop;
}
}
auto skipTail = isAttachedToNext() auto skipTail = isAttachedToNext()
|| (media && media->skipBubbleTail()) || (media && media->skipBubbleTail())
|| (keyboard != nullptr) || (keyboard != nullptr)

View file

@ -951,6 +951,7 @@ void Document::drawGrouped(
const QRect &geometry, const QRect &geometry,
RectParts sides, RectParts sides,
RectParts corners, RectParts corners,
float64 highlightOpacity,
not_null<uint64*> cacheKey, not_null<uint64*> cacheKey,
not_null<QPixmap*> cache) const { not_null<QPixmap*> cache) const {
p.translate(geometry.topLeft()); p.translate(geometry.topLeft());

View file

@ -72,6 +72,7 @@ public:
const QRect &geometry, const QRect &geometry,
RectParts sides, RectParts sides,
RectParts corners, RectParts corners,
float64 highlightOpacity,
not_null<uint64*> cacheKey, not_null<uint64*> cacheKey,
not_null<QPixmap*> cache) const override; not_null<QPixmap*> cache) const override;
TextState getStateGrouped( TextState getStateGrouped(

View file

@ -901,6 +901,7 @@ void Gif::drawGrouped(
const QRect &geometry, const QRect &geometry,
RectParts sides, RectParts sides,
RectParts corners, RectParts corners,
float64 highlightOpacity,
not_null<uint64*> cacheKey, not_null<uint64*> cacheKey,
not_null<QPixmap*> cache) const { not_null<QPixmap*> cache) const {
ensureDataMediaCreated(); ensureDataMediaCreated();
@ -989,8 +990,16 @@ void Gif::drawGrouped(
p.drawPixmap(geometry, *cache); p.drawPixmap(geometry, *cache);
} }
if (selected) { const auto overlayOpacity = selected
? (1. - highlightOpacity)
: highlightOpacity;
if (overlayOpacity > 0.) {
p.setOpacity(overlayOpacity);
Ui::FillComplexOverlayRect(p, geometry, roundRadius, corners); Ui::FillComplexOverlayRect(p, geometry, roundRadius, corners);
if (!selected) {
Ui::FillComplexOverlayRect(p, geometry, roundRadius, corners);
}
p.setOpacity(1.);
} }
if (radial if (radial

View file

@ -79,6 +79,7 @@ public:
const QRect &geometry, const QRect &geometry,
RectParts sides, RectParts sides,
RectParts corners, RectParts corners,
float64 highlightOpacity,
not_null<uint64*> cacheKey, not_null<uint64*> cacheKey,
not_null<QPixmap*> cache) const override; not_null<QPixmap*> cache) const override;
TextState getStateGrouped( TextState getStateGrouped(

View file

@ -84,6 +84,8 @@ public:
} }
virtual void refreshParentId(not_null<HistoryItem*> realParent) { virtual void refreshParentId(not_null<HistoryItem*> realParent) {
} }
virtual void drawHighlight(Painter &p, int top) const {
}
virtual void draw( virtual void draw(
Painter &p, Painter &p,
const QRect &r, const QRect &r,
@ -177,6 +179,7 @@ public:
const QRect &geometry, const QRect &geometry,
RectParts sides, RectParts sides,
RectParts corners, RectParts corners,
float64 highlightOpacity,
not_null<uint64*> cacheKey, not_null<uint64*> cacheKey,
not_null<QPixmap*> cache) const { not_null<QPixmap*> cache) const {
Unexpected("Grouping method call."); Unexpected("Grouping method call.");
@ -274,6 +277,9 @@ public:
const QRect &bubble, const QRect &bubble,
crl::time ms) const { crl::time ms) const {
} }
[[nodiscard]] virtual bool customHighlight() const {
return false;
}
virtual bool hasHeavyPart() const { virtual bool hasHeavyPart() const {
return false; return false;

View file

@ -268,6 +268,18 @@ QMargins GroupedMedia::groupedPadding() const {
(normal.bottom() - grouped.bottom()) + addToBottom); (normal.bottom() - grouped.bottom()) + addToBottom);
} }
void GroupedMedia::drawHighlight(Painter &p, int top) const {
if (_mode != Mode::Column) {
return;
}
const auto skip = top + groupedPadding().top();
for (auto i = 0, count = int(_parts.size()); i != count; ++i) {
const auto &part = _parts[i];
const auto rect = part.geometry.translated(0, skip);
_parent->paintCustomHighlight(p, rect.y(), rect.height(), part.item);
}
}
void GroupedMedia::draw( void GroupedMedia::draw(
Painter &p, Painter &p,
const QRect &clip, const QRect &clip,
@ -290,6 +302,9 @@ void GroupedMedia::draw(
if (textSelection) { if (textSelection) {
selection = part.content->skipSelection(selection); selection = part.content->skipSelection(selection);
} }
const auto highlightOpacity = (_mode == Mode::Grid)
? _parent->highlightOpacity(part.item)
: 0.;
part.content->drawGrouped( part.content->drawGrouped(
p, p,
clip, clip,
@ -298,6 +313,7 @@ void GroupedMedia::draw(
part.geometry.translated(0, groupPadding.top()), part.geometry.translated(0, groupPadding.top()),
part.sides, part.sides,
cornersFromSides(part.sides), cornersFromSides(part.sides),
highlightOpacity,
&part.cacheKey, &part.cacheKey,
&part.cache); &part.cache);
} }

View file

@ -31,6 +31,7 @@ public:
void refreshParentId(not_null<HistoryItem*> realParent) override; void refreshParentId(not_null<HistoryItem*> realParent) override;
void drawHighlight(Painter &p, int top) const override;
void draw( void draw(
Painter &p, Painter &p,
const QRect &clip, const QRect &clip,
@ -87,6 +88,9 @@ public:
bool allowsFastShare() const override { bool allowsFastShare() const override {
return true; return true;
} }
bool customHighlight() const override {
return true;
}
void stopAnimation() override; void stopAnimation() override;
void checkAnimation() override; void checkAnimation() override;

View file

@ -485,6 +485,7 @@ void Photo::drawGrouped(
const QRect &geometry, const QRect &geometry,
RectParts sides, RectParts sides,
RectParts corners, RectParts corners,
float64 highlightOpacity,
not_null<uint64*> cacheKey, not_null<uint64*> cacheKey,
not_null<QPixmap*> cache) const { not_null<QPixmap*> cache) const {
ensureDataMediaCreated(); ensureDataMediaCreated();
@ -509,9 +510,18 @@ void Photo::drawGrouped(
// App::roundShadow(p, 0, 0, paintw, painth, selected ? st::msgInShadowSelected : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners); // App::roundShadow(p, 0, 0, paintw, painth, selected ? st::msgInShadowSelected : st::msgInShadow, selected ? InSelectedShadowCorners : InShadowCorners);
} }
p.drawPixmap(geometry.topLeft(), *cache); p.drawPixmap(geometry.topLeft(), *cache);
if (selected) {
const auto overlayOpacity = selected
? (1. - highlightOpacity)
: highlightOpacity;
if (overlayOpacity > 0.) {
p.setOpacity(overlayOpacity);
const auto roundRadius = ImageRoundRadius::Large; const auto roundRadius = ImageRoundRadius::Large;
Ui::FillComplexOverlayRect(p, geometry, roundRadius, corners); Ui::FillComplexOverlayRect(p, geometry, roundRadius, corners);
if (!selected) {
Ui::FillComplexOverlayRect(p, geometry, roundRadius, corners);
}
p.setOpacity(1.);
} }
const auto displayState = radial const auto displayState = radial

View file

@ -68,6 +68,7 @@ public:
const QRect &geometry, const QRect &geometry,
RectParts sides, RectParts sides,
RectParts corners, RectParts corners,
float64 highlightOpacity,
not_null<uint64*> cacheKey, not_null<uint64*> cacheKey,
not_null<QPixmap*> cache) const override; not_null<QPixmap*> cache) const override;
TextState getStateGrouped( TextState getStateGrouped(