mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2026-08-07 20:26:25 +00:00
Optimize reveal animation of rich messages.
This commit is contained in:
@@ -2782,6 +2782,8 @@ private:
|
||||
int _width = -1;
|
||||
int _laidOutWidth = 0;
|
||||
int _height = 0;
|
||||
int _layoutGeneration = 0;
|
||||
MarkdownArticleRevealLineCountsCache _revealLineCounts;
|
||||
CachedTextLeafPool _cachedTextLeafs;
|
||||
std::vector<LaidOutBlock> _blocks;
|
||||
std::vector<LaidOutBlock> _retainedBlocks;
|
||||
@@ -3100,6 +3102,15 @@ void MarkdownArticle::Impl::paint(
|
||||
auto markBg = MarkBgColorForStyle(paintSt);
|
||||
const auto ownedMarkBg = style::internal::OwnedColor(markBg);
|
||||
textPalette.markBg = ownedMarkBg.color();
|
||||
if (local.reveal) {
|
||||
if (_revealLineCounts.layoutGeneration != _layoutGeneration) {
|
||||
_revealLineCounts.layoutGeneration = _layoutGeneration;
|
||||
_revealLineCounts.counts.clear();
|
||||
}
|
||||
local.reveal->lineCounts = &_revealLineCounts;
|
||||
} else if (!_revealLineCounts.counts.empty()) {
|
||||
_revealLineCounts = {};
|
||||
}
|
||||
const auto &previousTextPalette = p.textPalette();
|
||||
p.setTextPalette(textPalette);
|
||||
PaintBlocks(
|
||||
@@ -4354,7 +4365,7 @@ bool MarkdownArticle::Impl::setScrollLeft(
|
||||
_capturedScrollLefts.erase(identity);
|
||||
}
|
||||
refreshScrolledGeometry(block);
|
||||
RefreshScrollableSegmentRects(_blocks, &_segments);
|
||||
RefreshScrollableSegmentRects(block, &_segments);
|
||||
if (_textRepaintRect) {
|
||||
_textRepaintRect(block.outer);
|
||||
} else if (_textRepaint) {
|
||||
@@ -4516,6 +4527,7 @@ void MarkdownArticle::Impl::endHorizontalScroll() {
|
||||
// message bubbles to the minimum width in release Linux builds.
|
||||
void MarkdownArticle::Impl::finalizeRelayout(int heightBottom) {
|
||||
const auto &page = layoutStyle().pagePadding;
|
||||
++_layoutGeneration;
|
||||
restoreScrollState();
|
||||
refreshScrolledGeometry(_blocks);
|
||||
_laidOutWidth = std::min(
|
||||
|
||||
@@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "iv/markdown/iv_markdown_media_block.h"
|
||||
#include "iv/markdown/iv_markdown_prepare.h"
|
||||
|
||||
#include "base/flat_map.h"
|
||||
#include "spellcheck/spellcheck_highlight_syntax.h"
|
||||
#include "ui/chat/chat_style.h"
|
||||
#include "ui/effects/radial_animation.h"
|
||||
@@ -156,10 +157,21 @@ struct MarkdownArticleRevealPostprocess {
|
||||
not_null<QImage*> cache;
|
||||
};
|
||||
|
||||
// Caches per-block / per-leaf reveal line counts while the reveal
|
||||
// animation is active, so that painting doesn't re-run line breaking
|
||||
// for all the blocks above the clip on every frame. Owned by the
|
||||
// article, valid for a single layout generation, freed on the first
|
||||
// paint after the reveal finishes.
|
||||
struct MarkdownArticleRevealLineCountsCache {
|
||||
int layoutGeneration = -1;
|
||||
base::flat_map<const void*, int> counts;
|
||||
};
|
||||
|
||||
struct MarkdownArticleRevealPaintState {
|
||||
int activeLine = -1;
|
||||
int nextLine = 0;
|
||||
const MarkdownArticleRevealPostprocess *postprocess = nullptr;
|
||||
MarkdownArticleRevealLineCountsCache *lineCounts = nullptr;
|
||||
};
|
||||
|
||||
struct MarkdownArticlePaintContext final : Ui::ChatPaintContext {
|
||||
|
||||
@@ -2632,6 +2632,8 @@ void UpdateLaidOutLeafContent(
|
||||
cell->rowspan = std::max(prepared.rowspan, 1);
|
||||
cell->placeholderText = QString();
|
||||
cell->placeholderLeaf = Ui::Text::String();
|
||||
cell->cachedPreferredWidth = -1;
|
||||
cell->cachedPreferredHeight = 0;
|
||||
BuildOrReuseCachedTextLeaf(
|
||||
&cell->leaf,
|
||||
nullptr,
|
||||
@@ -3919,11 +3921,15 @@ LaidOutBlock LayoutGroupedMediaBlock(
|
||||
cellData.usePlaceholder = usePlaceholder;
|
||||
cellData.minimumWidth = LeafMinimumWidth(displayLeaf);
|
||||
cellData.preferredWidth = displayLeaf.maxWidth();
|
||||
cellData.preferredHeight = std::max(
|
||||
displayLeaf.countHeight(
|
||||
std::max(cellData.preferredWidth, 1),
|
||||
true),
|
||||
TextLineHeight(textStyle));
|
||||
if (cell.cachedPreferredWidth != cellData.preferredWidth) {
|
||||
cell.cachedPreferredWidth = cellData.preferredWidth;
|
||||
cell.cachedPreferredHeight = std::max(
|
||||
displayLeaf.countHeight(
|
||||
std::max(cellData.preferredWidth, 1),
|
||||
true),
|
||||
TextLineHeight(textStyle));
|
||||
}
|
||||
cellData.preferredHeight = cell.cachedPreferredHeight;
|
||||
row.cells.push_back(std::move(cellData));
|
||||
}
|
||||
rows.push_back(std::move(row));
|
||||
|
||||
@@ -48,6 +48,11 @@ struct LaidOutTableCell {
|
||||
QRect outer;
|
||||
QRect textRect;
|
||||
int textWidth = 0;
|
||||
|
||||
// Content-dependent, survives geometry resets, recomputed when
|
||||
// the displayed leaf max width changes (so on content changes).
|
||||
int cachedPreferredWidth = -1;
|
||||
int cachedPreferredHeight = 0;
|
||||
bool header = false;
|
||||
PreparedTableCellVerticalAlignment verticalAlignment
|
||||
= PreparedTableCellVerticalAlignment::Top;
|
||||
|
||||
@@ -329,12 +329,33 @@ void PaintSelectableTextLeaf(
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename CountCallback>
|
||||
[[nodiscard]] int CachedRevealLineCount(
|
||||
const MarkdownArticlePaintContext &context,
|
||||
const void *key,
|
||||
CountCallback &&count) {
|
||||
const auto cache = context.reveal ? context.reveal->lineCounts : nullptr;
|
||||
if (!cache) {
|
||||
return count();
|
||||
}
|
||||
const auto i = cache->counts.find(key);
|
||||
if (i != end(cache->counts)) {
|
||||
return i->second;
|
||||
}
|
||||
const auto result = count();
|
||||
cache->counts.emplace(key, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
void AdvanceRevealLinesForBlock(
|
||||
const MarkdownArticlePaintContext &context,
|
||||
const LaidOutBlock &block,
|
||||
const style::Markdown &st) {
|
||||
if (context.reveal) {
|
||||
context.reveal->nextLine += CountRevealLinesForBlock(block, st);
|
||||
context.reveal->nextLine += CachedRevealLineCount(
|
||||
context,
|
||||
&block,
|
||||
[&] { return CountRevealLinesForBlock(block, st); });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -697,9 +718,16 @@ void PaintTextLeaf(
|
||||
const auto availableWidth = std::max(width, 1);
|
||||
auto linePostprocess = std::optional<Ui::Text::LinePostprocess>();
|
||||
if (context.reveal && !elisionLines) {
|
||||
const auto lines = leaf.countLinesGeometry(availableWidth, true);
|
||||
const auto lineCount = CachedRevealLineCount(
|
||||
context,
|
||||
&leaf,
|
||||
[&] {
|
||||
return int(leaf.countLinesGeometry(
|
||||
availableWidth,
|
||||
true).size());
|
||||
});
|
||||
const auto baseLine = context.reveal->nextLine;
|
||||
context.reveal->nextLine += int(lines.size());
|
||||
context.reveal->nextLine += lineCount;
|
||||
if (const auto articlePostprocess = context.reveal->postprocess) {
|
||||
const auto activeLine = context.reveal->activeLine;
|
||||
linePostprocess.emplace(Ui::Text::LinePostprocess{
|
||||
@@ -756,7 +784,10 @@ void PaintSelectableTextLeaf(
|
||||
&& ((context.hiddenTextSegmentIndex == segmentIndex)
|
||||
|| (context.hiddenSegmentIndex == segmentIndex))) {
|
||||
if (context.reveal && !elisionLines) {
|
||||
context.reveal->nextLine += CountTextRevealLines(leaf, rect, width);
|
||||
context.reveal->nextLine += CachedRevealLineCount(
|
||||
context,
|
||||
&leaf,
|
||||
[&] { return CountTextRevealLines(leaf, rect, width); });
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -882,32 +882,41 @@ void RefreshScrollableSegmentRects(
|
||||
return;
|
||||
}
|
||||
for (const auto &block : blocks) {
|
||||
const auto refreshIndex = [&](int index) {
|
||||
if (index < 0 || index >= int(segments->size())) {
|
||||
return;
|
||||
}
|
||||
RefreshBlockSegmentRect(block, &(*segments)[index]);
|
||||
};
|
||||
refreshIndex(block.segmentIndex);
|
||||
refreshIndex(block.secondarySegmentIndex);
|
||||
refreshIndex(block.tertiarySegmentIndex);
|
||||
if (block.kind == PreparedBlockKind::Table) {
|
||||
for (const auto &row : block.tableRows) {
|
||||
for (const auto &cell : row.cells) {
|
||||
if (cell.segmentIndex < 0
|
||||
|| cell.segmentIndex >= int(segments->size())) {
|
||||
continue;
|
||||
}
|
||||
auto &segment = (*segments)[cell.segmentIndex];
|
||||
segment.outerRect = cell.outer;
|
||||
segment.textRect = VisibleTextRect(
|
||||
cell.textRect,
|
||||
cell.outer);
|
||||
RefreshScrollableSegmentRects(block, segments);
|
||||
}
|
||||
}
|
||||
|
||||
void RefreshScrollableSegmentRects(
|
||||
const LaidOutBlock &block,
|
||||
std::vector<SelectableSegment> *segments) {
|
||||
if (!segments) {
|
||||
return;
|
||||
}
|
||||
const auto refreshIndex = [&](int index) {
|
||||
if (index < 0 || index >= int(segments->size())) {
|
||||
return;
|
||||
}
|
||||
RefreshBlockSegmentRect(block, &(*segments)[index]);
|
||||
};
|
||||
refreshIndex(block.segmentIndex);
|
||||
refreshIndex(block.secondarySegmentIndex);
|
||||
refreshIndex(block.tertiarySegmentIndex);
|
||||
if (block.kind == PreparedBlockKind::Table) {
|
||||
for (const auto &row : block.tableRows) {
|
||||
for (const auto &cell : row.cells) {
|
||||
if (cell.segmentIndex < 0
|
||||
|| cell.segmentIndex >= int(segments->size())) {
|
||||
continue;
|
||||
}
|
||||
auto &segment = (*segments)[cell.segmentIndex];
|
||||
segment.outerRect = cell.outer;
|
||||
segment.textRect = VisibleTextRect(
|
||||
cell.textRect,
|
||||
cell.outer);
|
||||
}
|
||||
}
|
||||
RefreshScrollableSegmentRects(block.children, segments);
|
||||
}
|
||||
RefreshScrollableSegmentRects(block.children, segments);
|
||||
}
|
||||
|
||||
void CollectAnchors(
|
||||
|
||||
@@ -60,6 +60,9 @@ void CollectSelectableSegments(
|
||||
void RefreshScrollableSegmentRects(
|
||||
const std::vector<LaidOutBlock> &blocks,
|
||||
std::vector<SelectableSegment> *segments);
|
||||
void RefreshScrollableSegmentRects(
|
||||
const LaidOutBlock &block,
|
||||
std::vector<SelectableSegment> *segments);
|
||||
void CollectAnchors(
|
||||
const std::vector<LaidOutBlock> &blocks,
|
||||
std::vector<std::pair<QString, int>> *anchors);
|
||||
|
||||
Reference in New Issue
Block a user