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(
not_null<const HistoryView::Element*> element) {
not_null<const HistoryItem*> item) {
return crl::time(0);
}

View File

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

View File

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

View File

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

View File

@ -1045,11 +1045,6 @@ void HistoryWidget::scrollToAnimationCallback(
void HistoryWidget::enqueueMessageHighlight(
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) {
if (_highlightQueue.empty() && !_highlightTimer.isActive()) {
highlightMessage(universalId);
@ -1096,7 +1091,7 @@ void HistoryWidget::checkNextHighlight() {
void HistoryWidget::updateHighlightedMessage() {
const auto item = getItemFromHistoryOrMigrated(_highlightedMessageId);
const auto view = item ? item->mainView() : nullptr;
auto view = item ? item->mainView() : nullptr;
if (!view) {
return stopMessageHighlight();
}
@ -1105,6 +1100,11 @@ void HistoryWidget::updateHighlightedMessage() {
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);
}

View File

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

View File

@ -51,7 +51,7 @@ public:
Element *replacing = nullptr) = 0;
virtual bool elementUnderCursor(not_null<const Element*> view) = 0;
virtual crl::time elementHighlightTime(
not_null<const Element*> element) = 0;
not_null<const HistoryItem*> item) = 0;
virtual bool elementInSelectionMode() = 0;
virtual bool elementIntersectsRange(
not_null<const Element*> view,
@ -87,7 +87,7 @@ public:
Element *replacing = nullptr) override;
bool elementUnderCursor(not_null<const Element*> view) override;
crl::time elementHighlightTime(
not_null<const Element*> element) override;
not_null<const HistoryItem*> item) override;
bool elementInSelectionMode() override;
bool elementIntersectsRange(
not_null<const Element*> view,
@ -301,6 +301,13 @@ public:
virtual void unloadHeavyPart();
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.
HistoryBlock *block();
const HistoryBlock *block() const;

View File

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

View File

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

View File

@ -570,7 +570,40 @@ void Message::draw(
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();
if (roll) {
@ -602,34 +635,6 @@ void Message::draw(
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()
|| (media && media->skipBubbleTail())
|| (keyboard != nullptr)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -268,6 +268,18 @@ QMargins GroupedMedia::groupedPadding() const {
(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(
Painter &p,
const QRect &clip,
@ -290,6 +302,9 @@ void GroupedMedia::draw(
if (textSelection) {
selection = part.content->skipSelection(selection);
}
const auto highlightOpacity = (_mode == Mode::Grid)
? _parent->highlightOpacity(part.item)
: 0.;
part.content->drawGrouped(
p,
clip,
@ -298,6 +313,7 @@ void GroupedMedia::draw(
part.geometry.translated(0, groupPadding.top()),
part.sides,
cornersFromSides(part.sides),
highlightOpacity,
&part.cacheKey,
&part.cache);
}

View File

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

View File

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

View File

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