From 9eb9fcf043276bb3a73f1fc25531e4f4862d6fc9 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 13 Oct 2023 18:23:56 +0400 Subject: [PATCH] Use quote instead of paragraph for quotes. --- ui/basic.style | 12 +- ui/text/text.cpp | 179 ++++++++++++++--------------- ui/text/text.h | 41 ++++--- ui/text/text_block.h | 6 +- ui/text/text_extended_data.h | 4 +- ui/text/text_parser.cpp | 20 ++-- ui/text/text_parser.h | 6 +- ui/text/text_renderer.cpp | 213 ++++++++++++++++++++--------------- ui/text/text_renderer.h | 34 +++--- 9 files changed, 275 insertions(+), 240 deletions(-) diff --git a/ui/basic.style b/ui/basic.style index c0de76f..a2831c0 100644 --- a/ui/basic.style +++ b/ui/basic.style @@ -19,7 +19,7 @@ TextPalette { linkAlwaysActive: bool; } -ParagraphStyle { +QuoteStyle { padding: margins; verticalSkip: pixels; header: pixels; @@ -35,8 +35,8 @@ TextStyle { font: font; linkUnderline: int; lineHeight: pixels; - blockquote: ParagraphStyle; - pre: ParagraphStyle; + blockquote: QuoteStyle; + pre: QuoteStyle; } kLinkUnderlineNever: 0; @@ -65,14 +65,14 @@ defaultTextPalette: TextPalette { selectSpoilerFg: msgInDateFgSelected; selectOverlay: msgSelectOverlay; } -defaultParagraphStyle: ParagraphStyle { +defaultQuoteStyle: QuoteStyle { } defaultTextStyle: TextStyle { font: normalFont; linkUnderline: kLinkUnderlineActive; lineHeight: 0px; - blockquote: defaultParagraphStyle; - pre: defaultParagraphStyle; + blockquote: defaultQuoteStyle; + pre: defaultQuoteStyle; } semiboldTextStyle: TextStyle(defaultTextStyle) { font: semiboldFont; diff --git a/ui/text/text.cpp b/ui/text/text.cpp index 11e9b2a..a4ba32e 100644 --- a/ui/text/text.cpp +++ b/ui/text/text.cpp @@ -185,9 +185,9 @@ GeometryDescriptor SimpleGeometry( } }; -void ValidateBlockPaintCache( - BlockPaintCache &cache, - const style::ParagraphStyle &st) { +void ValidateQuotePaintCache( + QuotePaintCache &cache, + const style::QuoteStyle &st) { const auto icon = st.icon.empty() ? nullptr : &st.icon; if (!cache.corners.isNull() && cache.bgCached == cache.bg @@ -248,11 +248,11 @@ void ValidateBlockPaintCache( cache.corners = std::move(image); } -void FillBlockPaint( +void FillQuotePaint( QPainter &p, QRect rect, - const BlockPaintCache &cache, - const style::ParagraphStyle &st, + const QuotePaintCache &cache, + const style::QuoteStyle &st, SkipBlockPaintParts parts) { const auto &image = cache.corners; const auto ratio = int(image.devicePixelRatio()); @@ -431,18 +431,18 @@ void String::recountNaturalSize( } }; - auto pindex = paragraphIndex(nullptr); - auto paragraph = paragraphByIndex(pindex); - auto ppadding = paragraphPadding(paragraph); - auto pminwidth = paragraphMinWidth(paragraph); - auto pmaxwidth = QFixed(pminwidth); - auto poldheight = 0; + auto qindex = quoteIndex(nullptr); + auto quote = quoteByIndex(qindex); + auto qpadding = quotePadding(quote); + auto qminwidth = quoteMinWidth(quote); + auto qmaxwidth = QFixed(qminwidth); + auto qoldheight = 0; _maxWidth = 0; - _minHeight = ppadding.top(); + _minHeight = qpadding.top(); auto lineHeight = 0; auto maxWidth = QFixed(); - auto width = QFixed(pminwidth); + auto width = QFixed(qminwidth); auto last_rBearing = QFixed(); auto last_rPadding = QFixed(); for (auto &block : _blocks) { @@ -453,21 +453,24 @@ void String::recountNaturalSize( if (!lineHeight) { lineHeight = blockHeight; } - const auto index = paragraphIndex(b); - if (pindex != index) { - _minHeight += ppadding.bottom(); - if (paragraph) { - paragraph->maxWidth = pmaxwidth.ceil().toInt(); - paragraph->minHeight = _minHeight - poldheight; + accumulate_max(maxWidth, width); + accumulate_max(qmaxwidth, width); + + const auto index = quoteIndex(b); + if (qindex != index) { + _minHeight += qpadding.bottom(); + if (quote) { + quote->maxWidth = qmaxwidth.ceil().toInt(); + quote->minHeight = _minHeight - qoldheight; } - poldheight = _minHeight; - pindex = index; - paragraph = paragraphByIndex(pindex); - ppadding = paragraphPadding(paragraph); - pminwidth = paragraphMinWidth(paragraph); - pmaxwidth = pminwidth; - _minHeight += ppadding.top(); - ppadding.setTop(0); + qoldheight = _minHeight; + qindex = index; + quote = quoteByIndex(qindex); + qpadding = quotePadding(quote); + qminwidth = quoteMinWidth(quote); + qmaxwidth = qminwidth; + _minHeight += qpadding.top(); + qpadding.setTop(0); } if (initial) { computeParagraphDirection(b->position()); @@ -480,9 +483,7 @@ void String::recountNaturalSize( last_rBearing = 0;// b->f_rbearing(); (0 for newline) last_rPadding = 0;// b->f_rpadding(); (0 for newline) - accumulate_max(maxWidth, width); - accumulate_max(pmaxwidth, width); - width = pminwidth; + width = qminwidth; // + (b->f_width() - last_rBearing); (0 for newline) continue; } @@ -497,7 +498,7 @@ void String::recountNaturalSize( // for all the blocks to fit on their line we check each block, even the // intermediate one with a large negative right bearing. accumulate_max(maxWidth, width); - accumulate_max(pmaxwidth, width); + accumulate_max(qmaxwidth, width); width += last_rBearing + (last_rPadding + b->f_width() - b__f_rbearing); lineHeight = qMax(lineHeight, blockHeight); @@ -513,17 +514,17 @@ void String::recountNaturalSize( if (!lineHeight) { lineHeight = CountBlockHeight(_blocks.back().get(), _st); } - _minHeight += ppadding.top() + lineHeight + ppadding.bottom(); + _minHeight += qpadding.top() + lineHeight + qpadding.bottom(); accumulate_max(maxWidth, width); - accumulate_max(pmaxwidth, width); + accumulate_max(qmaxwidth, width); } _maxWidth = maxWidth.ceil().toInt(); - if (paragraph) { - paragraph->maxWidth = pmaxwidth.ceil().toInt(); - paragraph->minHeight = _minHeight - poldheight; - _endsWithParagraphDetails = true; + if (quote) { + quote->maxWidth = qmaxwidth.ceil().toInt(); + quote->minHeight = _minHeight - qoldheight; + _endsWithQuote = true; } else { - _endsWithParagraphDetails = false; + _endsWithQuote = false; } } @@ -674,7 +675,7 @@ bool String::updateSkipBlock(int width, int height) { } _text.resize(block->position()); _blocks.pop_back(); - } else if (_endsWithParagraphDetails) { + } else if (_endsWithQuote) { _text.push_back(QChar::LineFeed); _blocks.push_back(Block::Newline( _st->font, @@ -764,10 +765,10 @@ void String::enumerateLines( Callback callback) const { const auto width = QFixed(std::max(w, _minResizeWidth)); - auto pindex = paragraphIndex(nullptr); - auto paragraph = paragraphByIndex(pindex); - auto ppadding = paragraphPadding(paragraph); - auto widthLeft = width - ppadding.left() - ppadding.right(); + auto qindex = quoteIndex(nullptr); + auto quote = quoteByIndex(qindex); + auto qpadding = quotePadding(quote); + auto widthLeft = width - qpadding.left() - qpadding.right(); auto lineHeight = 0; auto last_rBearing = QFixed(); auto last_rPadding = QFixed(); @@ -780,15 +781,15 @@ void String::enumerateLines( if (!lineHeight) { lineHeight = blockHeight; } - lineHeight += ppadding.top(); - const auto index = paragraphIndex(b.get()); - if (pindex != index) { - lineHeight += ppadding.bottom(); - pindex = index; - paragraph = paragraphByIndex(pindex); - ppadding = paragraphPadding(paragraph); + lineHeight += qpadding.top(); + const auto index = quoteIndex(b.get()); + if (qindex != index) { + lineHeight += qpadding.bottom(); + qindex = index; + quote = quoteByIndex(qindex); + qpadding = quotePadding(quote); } else { - ppadding.setTop(0); + qpadding.setTop(0); } callback(width - widthLeft, lineHeight); @@ -796,7 +797,7 @@ void String::enumerateLines( lineHeight = 0; last_rBearing = 0;// b->f_rbearing(); (0 for newline) last_rPadding = 0;// b->f_rpadding(); (0 for newline) - widthLeft = width - ppadding.left() - ppadding.right(); + widthLeft = width - qpadding.left() - qpadding.right(); // - (b->f_width() - last_rBearing); (0 for newline) longWordLine = true; @@ -858,15 +859,15 @@ void String::enumerateLines( j_width = (j->f_width() >= 0) ? j->f_width() : -j->f_width(); } - callback(width - widthLeft, lineHeight + ppadding.top()); - ppadding.setTop(0); + callback(width - widthLeft, lineHeight + qpadding.top()); + qpadding.setTop(0); lineHeight = qMax(0, blockHeight); last_rBearing = j->f_rbearing(); last_rPadding = j->f_rpadding(); widthLeft = width - - ppadding.left() - - ppadding.right() + - qpadding.left() + - qpadding.right() - (j_width - last_rBearing); longWordLine = !wordEndsHere; @@ -877,15 +878,15 @@ void String::enumerateLines( continue; } - callback(width - widthLeft, lineHeight + ppadding.top()); - ppadding.setTop(0); + callback(width - widthLeft, lineHeight + qpadding.top()); + qpadding.setTop(0); lineHeight = qMax(0, blockHeight); last_rBearing = b__f_rbearing; last_rPadding = b->f_rpadding(); widthLeft = width - - ppadding.left() - - ppadding.right() + - qpadding.left() + - qpadding.right() - (b->f_width() - last_rBearing); longWordLine = true; @@ -894,7 +895,7 @@ void String::enumerateLines( if (widthLeft < width) { callback( width - widthLeft, - lineHeight + ppadding.top() + ppadding.bottom()); + lineHeight + qpadding.top() + qpadding.bottom()); } } @@ -1096,60 +1097,60 @@ uint16 String::countBlockLength( return countBlockEnd(i, e) - (*i)->position(); } -ParagraphDetails *String::paragraphByIndex(int index) const { +QuoteDetails *String::quoteByIndex(int index) const { Expects(!index - || (_extended && index <= _extended->paragraphs.size())); + || (_extended && index <= _extended->quotes.size())); - return index ? &_extended->paragraphs[index - 1] : nullptr; + return index ? &_extended->quotes[index - 1] : nullptr; } -int String::paragraphIndex(const AbstractBlock *block) const { +int String::quoteIndex(const AbstractBlock *block) const { Expects(!block || block->type() == TextBlockType::Newline); return block - ? static_cast(block)->paragraphIndex() - : _startParagraphIndex; + ? static_cast(block)->quoteIndex() + : _startQuoteIndex; } -const style::ParagraphStyle &String::paragraphStyle( - not_null info) const { - return info->pre ? _st->pre : _st->blockquote; +const style::QuoteStyle &String::quoteStyle( + not_null quote) const { + return quote->pre ? _st->pre : _st->blockquote; } -QMargins String::paragraphPadding(ParagraphDetails *info) const { - if (!info) { +QMargins String::quotePadding(QuoteDetails *quote) const { + if (!quote) { return {}; } - const auto &st = paragraphStyle(info); + const auto &st = quoteStyle(quote); const auto skip = st.verticalSkip; const auto top = st.header; return st.padding + QMargins(0, top + skip, 0, skip); } -int String::paragraphMinWidth(ParagraphDetails *info) const { - if (!info) { +int String::quoteMinWidth(QuoteDetails *quote) const { + if (!quote) { return 0; } - const auto ppadding = paragraphPadding(info); - const auto &pheader = paragraphHeaderText(info); - const auto pst = info ? ¶graphStyle(info) : nullptr; - return ppadding.left() - + (pheader.isEmpty() ? 0 : _st->font->monospace()->width(pheader)) + const auto qpadding = quotePadding(quote); + const auto &qheader = quoteHeaderText(quote); + const auto qst = quote ? "eStyle(quote) : nullptr; + return qpadding.left() + + (qheader.isEmpty() ? 0 : _st->font->monospace()->width(qheader)) + std::max( - ppadding.right(), - ((pst && !pst->icon.empty()) - ? (pst->iconPosition.x() + pst->icon.width()) + qpadding.right(), + ((qst && !qst->icon.empty()) + ? (qst->iconPosition.x() + qst->icon.width()) : 0)); } -const QString &String::paragraphHeaderText(ParagraphDetails *info) const { +const QString &String::quoteHeaderText(QuoteDetails *quote) const { static const auto kEmptyHeader = QString(); static const auto kDefaultHeader = u"code"_q; - return (!info || !info->pre) + return (!quote || !quote->pre) ? kEmptyHeader - : info->language.isEmpty() + : quote->language.isEmpty() ? kDefaultHeader - : info->language; + : quote->language; } template < @@ -1478,7 +1479,7 @@ void String::clear() { _blocks.clear(); _extended = nullptr; _maxWidth = _minHeight = 0; - _startParagraphIndex = 0; + _startQuoteIndex = 0; _startParagraphLTR = false; _startParagraphRTL = false; } diff --git a/ui/text/text.h b/ui/text/text.h index 9fe60dd..4ee53ec 100644 --- a/ui/text/text.h +++ b/ui/text/text.h @@ -24,7 +24,7 @@ enum class type : uchar; namespace style { struct TextStyle; struct TextPalette; -struct ParagraphStyle; +struct QuoteStyle; } // namespace style namespace Ui { @@ -81,7 +81,7 @@ class AbstractBlock; struct IsolatedEmoji; struct OnlyCustomEmoji; struct SpoilerData; -struct ParagraphDetails; +struct QuoteDetails; struct ExtendedData; struct Modification { @@ -165,7 +165,7 @@ struct GeometryDescriptor { bool elisionOneLine, bool elisionBreakEverywhere); -struct BlockPaintCache { +struct QuotePaintCache { QImage corners; QColor headerCached; QColor bgCached; @@ -178,19 +178,19 @@ struct BlockPaintCache { QColor icon; }; -void ValidateBlockPaintCache( - BlockPaintCache &cache, - const style::ParagraphStyle &st); +void ValidateQuotePaintCache( + QuotePaintCache &cache, + const style::QuoteStyle &st); struct SkipBlockPaintParts { bool skipTop : 1 = false; bool skipBottom : 1 = false; }; -void FillBlockPaint( +void FillQuotePaint( QPainter &p, QRect rect, - const BlockPaintCache &cache, - const style::ParagraphStyle &st, + const QuotePaintCache &cache, + const style::QuoteStyle &st, SkipBlockPaintParts parts = {}); struct PaintContext { @@ -202,8 +202,8 @@ struct PaintContext { QRect clip; const style::TextPalette *palette = nullptr; - BlockPaintCache *pre = nullptr; - BlockPaintCache *blockquote = nullptr; + QuotePaintCache *pre = nullptr; + QuotePaintCache *blockquote = nullptr; std::span colors; SpoilerMessCache *spoiler = nullptr; crl::time now = 0; @@ -375,16 +375,15 @@ private: [[nodiscard]] uint16 countBlockLength( const TextBlocks::const_iterator &i, const TextBlocks::const_iterator &e) const; - [[nodiscard]] ParagraphDetails *paragraphByIndex(int index) const; - [[nodiscard]] const style::ParagraphStyle ¶graphStyle( - not_null info) const; - [[nodiscard]] QMargins paragraphPadding(ParagraphDetails *info) const; - [[nodiscard]] int paragraphMinWidth(ParagraphDetails *info) const; - [[nodiscard]] const QString ¶graphHeaderText( - ParagraphDetails *info) const; + [[nodiscard]] QuoteDetails *quoteByIndex(int index) const; + [[nodiscard]] const style::QuoteStyle "eStyle( + not_null quote) const; + [[nodiscard]] QMargins quotePadding(QuoteDetails *quote) const; + [[nodiscard]] int quoteMinWidth(QuoteDetails *quote) const; + [[nodiscard]] const QString "eHeaderText(QuoteDetails *quote) const; // block must be either nullptr or a pointer to a NewlineBlock. - [[nodiscard]] int paragraphIndex(const AbstractBlock *block) const; + [[nodiscard]] int quoteIndex(const AbstractBlock *block) const; // Template method for originalText(), originalTextWithEntities(). template < @@ -425,7 +424,7 @@ private: int _minResizeWidth = 0; int _maxWidth = 0; int _minHeight = 0; - uint16 _startParagraphIndex = 0; + uint16 _startQuoteIndex = 0; bool _startParagraphLTR : 1 = false; bool _startParagraphRTL : 1 = false; bool _hasCustomEmoji : 1 = false; @@ -433,7 +432,7 @@ private: bool _isOnlyCustomEmoji : 1 = false; bool _hasNotEmojiAndSpaces : 1 = false; bool _skipBlockAddedNewline : 1 = false; - bool _endsWithParagraphDetails : 1 = false; + bool _endsWithQuote : 1 = false; friend class Parser; friend class Renderer; diff --git a/ui/text/text_block.h b/ui/text/text_block.h index ae7a4ce..aa51882 100644 --- a/ui/text/text_block.h +++ b/ui/text/text_block.h @@ -107,15 +107,15 @@ public: uint16 linkIndex, uint16 colorIndex); - [[nodiscard]] uint16 paragraphIndex() const { - return _paragraphIndex; + [[nodiscard]] uint16 quoteIndex() const { + return _quoteIndex; } [[nodiscard]] Qt::LayoutDirection paragraphDirection() const { return UnpackParagraphDirection(_paragraphLTR, _paragraphRTL); } private: - uint16 _paragraphIndex = 0; + uint16 _quoteIndex = 0; bool _paragraphLTR : 1 = false; bool _paragraphRTL : 1 = false; diff --git a/ui/text/text_extended_data.h b/ui/text/text_extended_data.h index f8901ca..f86177f 100644 --- a/ui/text/text_extended_data.h +++ b/ui/text/text_extended_data.h @@ -43,7 +43,7 @@ struct SpoilerData { bool revealed = false; }; -struct ParagraphDetails { +struct QuoteDetails { QString language; ClickHandlerPtr copy; int copyWidth = 0; @@ -56,7 +56,7 @@ struct ParagraphDetails { struct ExtendedData { std::vector links; - std::vector paragraphs; + std::vector quotes; std::unique_ptr spoiler; std::vector modifications; }; diff --git a/ui/text/text_parser.cpp b/ui/text/text_parser.cpp index 080b138..393c59e 100644 --- a/ui/text/text_parser.cpp +++ b/ui/text/text_parser.cpp @@ -212,7 +212,7 @@ void Parser::createBlock(int32 skipBack) { } else if (newline) { push(&Block::Newline); auto &newline = _t->_blocks.back().unsafe(); - newline._paragraphIndex = _paragraphIndex; + newline._quoteIndex = _quoteIndex; } else { push(&Block::Text, _t->_minResizeWidth); } @@ -233,7 +233,7 @@ void Parser::createNewlineBlock(bool fromOriginalText) { createBlock(); } -void Parser::ensureAtNewline(ParagraphDetails details) { +void Parser::ensureAtNewline(QuoteDetails quote) { createBlock(); const auto lastType = _t->_blocks.empty() ? TextBlockType::Newline @@ -243,15 +243,15 @@ void Parser::ensureAtNewline(ParagraphDetails details) { createNewlineBlock(false); _customEmojiData = base::take(saved); } - auto ¶graphs = _t->ensureExtended()->paragraphs; - paragraphs.push_back(std::move(details)); - const auto index = _paragraphIndex = int(paragraphs.size()); + auto "es = _t->ensureExtended()->quotes; + quotes.push_back(std::move(quote)); + const auto index = _quoteIndex = int(quotes.size()); if (_t->_blocks.empty()) { - _t->_startParagraphIndex = index; + _t->_startQuoteIndex = index; } else { auto &last = _t->_blocks.back(); Assert(last->type() == TextBlockType::Newline); - last.unsafe()._paragraphIndex = index; + last.unsafe()._quoteIndex = index; } } @@ -274,14 +274,14 @@ void Parser::finishEntities() { if ((*flags) & (TextBlockFlag::Pre | TextBlockFlag::Blockquote)) { - _paragraphIndex = 0; + _quoteIndex = 0; if (lastType != TextBlockType::Newline) { _newlineAwaited = true; } else if (_t->_blocks.empty()) { - _t->_startParagraphIndex = 0; + _t->_startQuoteIndex = 0; } else { auto &last = _t->_blocks.back(); - last.unsafe()._paragraphIndex = 0; + last.unsafe()._quoteIndex = 0; } } if (IsMono(*flags)) { diff --git a/ui/text/text_parser.h b/ui/text/text_parser.h index 20be972..c245e8f 100644 --- a/ui/text/text_parser.h +++ b/ui/text/text_parser.h @@ -11,7 +11,7 @@ namespace Ui::Text { -struct ParagraphDetails; +struct QuoteDetails; class Parser { public: @@ -60,7 +60,7 @@ private: void blockCreated(); void createBlock(int32 skipBack = 0); void createNewlineBlock(bool fromOriginalText); - void ensureAtNewline(ParagraphDetails details); + void ensureAtNewline(QuoteDetails quote); // Returns true if at least one entity was parsed in the current position. bool checkEntities(); @@ -115,7 +115,7 @@ private: uint16 _linkIndex = 0; uint16 _colorIndex = 0; uint16 _monoIndex = 0; - uint16 _paragraphIndex = 0; + uint16 _quoteIndex = 0; EmojiPtr _emoji = nullptr; // current emoji, if current word is an emoji, or zero int32 _blockStart = 0; // offset in result, from which current parsed block is started int32 _diacritics = 0; // diacritic chars skipped without good char diff --git a/ui/text/text_renderer.cpp b/ui/text/text_renderer.cpp index e7ec3c0..7e75bc0 100644 --- a/ui/text/text_renderer.cpp +++ b/ui/text/text_renderer.cpp @@ -179,8 +179,8 @@ void Renderer::draw(QPainter &p, const PaintContext &context) { ? _originalPen : _palette->selectFg->p; - _x = context.position.x(); - _y = context.position.y(); + _x = _startLeft = context.position.x(); + _y = _startTop = context.position.y(); _yFrom = context.clip.isNull() ? 0 : context.clip.y(); _yTo = context.clip.isNull() ? -1 @@ -206,8 +206,8 @@ void Renderer::draw(QPainter &p, const PaintContext &context) { ? (1. - _spoiler->revealAnimation.value( _spoiler->revealed ? 1. : 0.)) : 0.; - _preBlockCache = context.pre; - _blockquoteBlockCache = context.blockquote; + _quotePreCache = context.pre; + _quoteBlockquoteCache = context.blockquote; enumerate(); } @@ -227,13 +227,10 @@ void Renderer::enumerate() { } } - _startLeft = _x.toInt(); - _startTop = _y; - if ((*_t->_blocks.cbegin())->type() != TextBlockType::Newline) { initNextParagraph( _t->_blocks.cbegin(), - _t->_startParagraphIndex, + _t->_startQuoteIndex, UnpackParagraphDirection( _t->_startParagraphLTR, _t->_startParagraphRTL)); @@ -262,9 +259,9 @@ void Renderer::enumerate() { if (!_lineHeight) { _lineHeight = blockHeight; } - const auto pindex = static_cast(b)->paragraphIndex(); - const auto changed = (_pindex != pindex); - fillParagraphBg(changed ? _ppadding.bottom() : 0); + const auto qindex = static_cast(b)->quoteIndex(); + const auto changed = (_quoteIndex != qindex); + fillParagraphBg(changed ? _quotePadding.bottom() : 0); if (!drawLine((*i)->position(), i, e)) { return; } @@ -277,7 +274,7 @@ void Renderer::enumerate() { initNextParagraph( i + 1, - pindex, + qindex, static_cast(b)->paragraphDirection()); longWordLine = true; @@ -395,7 +392,7 @@ void Renderer::enumerate() { continue; } if (_lineStart < _t->_text.size()) { - fillParagraphBg(_ppadding.bottom()); + fillParagraphBg(_quotePadding.bottom()); if (!drawLine(_t->_text.size(), e, e)) { return; } @@ -407,30 +404,31 @@ void Renderer::enumerate() { } void Renderer::fillParagraphBg(int paddingBottom) { - const auto cache = (!_p || !_paragraph) + const auto cache = (!_p || !_quote) ? nullptr - : _paragraph->pre - ? _preBlockCache - : _paragraph->blockquote - ? _blockquoteBlockCache + : _quote->pre + ? _quotePreCache + : _quote->blockquote + ? _quoteBlockquoteCache : nullptr; if (cache) { - const auto &st = _t->paragraphStyle(_paragraph); - auto &valid = _paragraph->pre - ? _preBlockCacheValid - : _blockquoteBlockCacheValid; + const auto &st = _t->quoteStyle(_quote); + auto &valid = _quote->pre + ? _quotePreValid + : _quoteBlockquoteValid; if (!valid) { valid = true; - ValidateBlockPaintCache(*cache, st); + ValidateQuotePaintCache(*cache, st); } const auto skip = st.verticalSkip; - const auto isTop = (_y != _blockLineTop); + const auto isTop = (_y != _quoteLineTop); const auto isBottom = (paddingBottom != 0); - const auto top = _blockLineTop + (isTop ? skip : 0); + const auto left = _startLeft + _quoteShift; + const auto top = _quoteLineTop + (isTop ? skip : 0); const auto fill = _y + _lineHeight + paddingBottom - top - (isBottom ? skip : 0); - const auto rect = QRect(_startLeft, top, _startLineWidth, fill); - FillBlockPaint(*_p, rect, *cache, st, { + const auto rect = QRect(left, top, _startLineWidth, fill); + FillQuotePaint(*_p, rect, *cache, st, { .skipTop = !isTop, .skipBottom = !isBottom, }); @@ -442,10 +440,10 @@ void Renderer::fillParagraphBg(int paddingBottom) { const auto baseline = position + QPoint(0, font->ascent); _p->setFont(font); _p->setPen(_palette->monoFg->p); - _p->drawText(baseline, _t->paragraphHeaderText(_paragraph)); + _p->drawText(baseline, _t->quoteHeaderText(_quote)); } } - _blockLineTop = _y + _lineHeight + paddingBottom; + _quoteLineTop = _y + _lineHeight + paddingBottom; } StateResult Renderer::getState( @@ -484,27 +482,28 @@ void Renderer::initNextParagraph( String::TextBlocks::const_iterator i, int16 paragraphIndex, Qt::LayoutDirection direction) { - _parDirection = (direction == Qt::LayoutDirectionAuto) + _paragraphDirection = (direction == Qt::LayoutDirectionAuto) ? style::LayoutDirection() : direction; - _parStartBlock = i; + _paragraphStartBlock = i; _paragraphWidthRemaining = 0; - if (_pindex != paragraphIndex) { - _y += _ppadding.bottom(); - _pindex = paragraphIndex; - _paragraph = _t->paragraphByIndex(paragraphIndex); - _ppadding = _t->paragraphPadding(_paragraph); - _blockLineTop = _y; - _y += _ppadding.top(); - _ppadding.setTop(0); + if (_quoteIndex != paragraphIndex) { + _y += _quotePadding.bottom(); + _quoteIndex = paragraphIndex; + _quote = _t->quoteByIndex(paragraphIndex); + _quotePadding = _t->quotePadding(_quote); + _quoteLineTop = _y; + _y += _quotePadding.top(); + _quotePadding.setTop(0); + _quoteDirection = _paragraphDirection; } const auto e = _t->_blocks.cend(); if (i == e) { - _lineStart = _parStart = _t->_text.size(); + _lineStart = _paragraphStart = _t->_text.size(); _lineStartBlock = _t->_blocks.size(); - _parLength = 0; + _paragraphLength = 0; } else { - _lineStart = _parStart = (*i)->position(); + _lineStart = _paragraphStart = (*i)->position(); _lineStartBlock = i - _t->_blocks.cbegin(); auto last_rPadding = QFixed(0); @@ -520,10 +519,13 @@ void Renderer::initNextParagraph( - rBearing; last_rBearing = rBearing; } - _parLength = ((i == e) ? _t->_text.size() : (*i)->position()) - _parStart; + _paragraphLength = ((i == e) + ? _t->_text.size() + : (*i)->position()) + - _paragraphStart; } - _parAnalysis.resize(0); - _paragraphWidthRemaining += _ppadding.left() + _ppadding.right(); + _paragraphAnalysis.resize(0); + _paragraphWidthRemaining += _quotePadding.left() + _quotePadding.right(); initNextLine(); } @@ -533,31 +535,48 @@ void Renderer::initNextLine() { .top = (_y - _startTop), .width = _paragraphWidthRemaining.ceil().toInt(), }); - _blockLineTop += _startTop + line.top - _y; - _x = _startLeft + line.left + _ppadding.left(); + _quoteLineTop += _startTop + line.top - _y; + _x = _startLeft + line.left + _quotePadding.left(); _y = _startTop + line.top; _startLineWidth = line.width; - _lineWidth = _startLineWidth - _ppadding.left() - _ppadding.right(); + _quoteShift = 0; + if (_quote && _quote->maxWidth < _startLineWidth) { + const auto delta = _startLineWidth - _quote->maxWidth; + _startLineWidth = _quote->maxWidth; + + if (_align & Qt::AlignHCenter) { + _quoteShift = delta / 2; + } else if (((_align & Qt::AlignLeft) + && (_quoteDirection == Qt::RightToLeft)) + || ((_align & Qt::AlignRight) + && (_quoteDirection == Qt::LeftToRight))) { + _quoteShift = delta; + } + _x += _quoteShift; + } + _lineWidth = _startLineWidth + - _quotePadding.left() + - _quotePadding.right(); _wLeft = _lineWidth; _elidedLine = line.elided; } void Renderer::initParagraphBidi() { - if (!_parLength || !_parAnalysis.isEmpty()) { + if (!_paragraphLength || !_paragraphAnalysis.isEmpty()) { return; } - String::TextBlocks::const_iterator i = _parStartBlock, e = _t->_blocks.cend(), n = i + 1; + String::TextBlocks::const_iterator i = _paragraphStartBlock, e = _t->_blocks.cend(), n = i + 1; bool ignore = false; - bool rtl = (_parDirection == Qt::RightToLeft); + bool rtl = (_paragraphDirection == Qt::RightToLeft); if (!ignore && !rtl) { ignore = true; - const ushort *start = reinterpret_cast(_str) + _parStart; + const ushort *start = reinterpret_cast(_str) + _paragraphStart; const ushort *curr = start; - const ushort *end = start + _parLength; + const ushort *end = start + _paragraphLength; while (curr < end) { - while (n != e && (*n)->position() <= _parStart + (curr - start)) { + while (n != e && (*n)->position() <= _paragraphStart + (curr - start)) { i = n; ++n; } @@ -572,21 +591,21 @@ void Renderer::initParagraphBidi() { } } - _parAnalysis.resize(_parLength); - QScriptAnalysis *analysis = _parAnalysis.data(); + _paragraphAnalysis.resize(_paragraphLength); + QScriptAnalysis *analysis = _paragraphAnalysis.data(); BidiControl control(rtl); - _parHasBidi = false; + _paragraphHasBidi = false; if (ignore) { - memset(analysis, 0, _parLength * sizeof(QScriptAnalysis)); + memset(analysis, 0, _paragraphLength * sizeof(QScriptAnalysis)); if (rtl) { - for (int i = 0; i < _parLength; ++i) + for (int i = 0; i < _paragraphLength; ++i) analysis[i].bidiLevel = 1; - _parHasBidi = true; + _paragraphHasBidi = true; } } else { - _parHasBidi = eBidiItemize(analysis, control); + _paragraphHasBidi = eBidiItemize(analysis, control); } } @@ -651,14 +670,17 @@ bool Renderer::drawLine(uint16 _lineEnd, const String::TextBlocks::const_iterato auto x = _x; if (_align & Qt::AlignHCenter) { x += (_wLeft / 2).toInt(); - } else if (((_align & Qt::AlignLeft) && _parDirection == Qt::RightToLeft) || ((_align & Qt::AlignRight) && _parDirection == Qt::LeftToRight)) { + } else if (((_align & Qt::AlignLeft) + && (_paragraphDirection == Qt::RightToLeft)) + || ((_align & Qt::AlignRight) + && (_paragraphDirection == Qt::LeftToRight))) { x += _wLeft; } if (!_p) { if (_lookupX < x) { if (_lookupSymbol) { - if (_parDirection == Qt::RightToLeft) { + if (_paragraphDirection == Qt::RightToLeft) { _lookupResult.symbol = (_lineEnd > _lineStart) ? (_lineEnd - 1) : _lineStart; _lookupResult.afterSymbol = (_lineEnd > _lineStart) ? true : false; // _lookupResult.uponSymbol = ((_lookupX >= _x) && (_lineEnd < _t->_text.size()) && (!_endBlock || _endBlock->type() != TextBlockType::Skip)) ? true : false; @@ -674,7 +696,7 @@ bool Renderer::drawLine(uint16 _lineEnd, const String::TextBlocks::const_iterato _lookupResult.uponSymbol = false; return false; } else if (_lookupX >= x + (_lineWidth - _wLeft)) { - if (_parDirection == Qt::RightToLeft) { + if (_paragraphDirection == Qt::RightToLeft) { _lookupResult.symbol = _lineStart; _lookupResult.afterSymbol = false; // _lookupResult.uponSymbol = ((_lookupX < _x + _w) && (_lineStart > 0)) ? true : false; @@ -700,14 +722,14 @@ bool Renderer::drawLine(uint16 _lineEnd, const String::TextBlocks::const_iterato && (_selection.from <= trimmedLineEnd) && (!_endBlock || _endBlock->type() != TextBlockType::Skip); - if ((selectFromStart && _parDirection == Qt::LeftToRight) - || (selectTillEnd && _parDirection == Qt::RightToLeft)) { + if ((selectFromStart && _paragraphDirection == Qt::LeftToRight) + || (selectTillEnd && _paragraphDirection == Qt::RightToLeft)) { if (x > _x) { fillSelectRange({ _x, x }); } } - if ((selectTillEnd && _parDirection == Qt::LeftToRight) - || (selectFromStart && _parDirection == Qt::RightToLeft)) { + if ((selectTillEnd && _paragraphDirection == Qt::LeftToRight) + || (selectFromStart && _paragraphDirection == Qt::RightToLeft)) { if (x < _x + _wLeft) { fillSelectRange({ x + _lineWidth - _wLeft, _x + _lineWidth }); } @@ -723,7 +745,7 @@ bool Renderer::drawLine(uint16 _lineEnd, const String::TextBlocks::const_iterato _f = _t->_st->font; QStackTextEngine engine(lineText, _f->f); - engine.option.setTextDirection(_parDirection); + engine.option.setTextDirection(_paragraphDirection); _e = &engine; eItemize(); @@ -813,7 +835,7 @@ bool Renderer::drawLine(uint16 _lineEnd, const String::TextBlocks::const_iterato } if (_lookupSymbol) { if (_type == TextBlockType::Skip) { - if (_parDirection == Qt::RightToLeft) { + if (_paragraphDirection == Qt::RightToLeft) { _lookupResult.symbol = _lineStart; _lookupResult.afterSymbol = false; } else { @@ -1291,19 +1313,25 @@ void Renderer::elideSaveBlock(int32 blockIndex, const AbstractBlock *&_endBlock, } void Renderer::setElideBidi(int32 elideStart, int32 elideLen) { - int32 newParLength = elideStart + elideLen - _parStart; - if (newParLength > _parAnalysis.size()) { - _parAnalysis.resize(newParLength); + int32 newParLength = elideStart + elideLen - _paragraphStart; + if (newParLength > _paragraphAnalysis.size()) { + _paragraphAnalysis.resize(newParLength); } for (int32 i = elideLen; i > 0; --i) { - _parAnalysis[newParLength - i].bidiLevel = (_parDirection == Qt::RightToLeft) ? 1 : 0; + _paragraphAnalysis[newParLength - i].bidiLevel + = (_paragraphDirection == Qt::RightToLeft) ? 1 : 0; } } -void Renderer::prepareElidedLine(QString &lineText, int32 lineStart, int32 &lineLength, const AbstractBlock *&_endBlock, int repeat) { +void Renderer::prepareElidedLine( + QString &lineText, + int32 lineStart, + int32 &lineLength, + const AbstractBlock *&_endBlock, + int repeat) { _f = _t->_st->font; QStackTextEngine engine(lineText, _f->f); - engine.option.setTextDirection(_parDirection); + engine.option.setTextDirection(_paragraphDirection); _e = &engine; eItemize(); @@ -1318,7 +1346,10 @@ void Renderer::prepareElidedLine(QString &lineText, int32 lineStart, int32 &line eShapeLine(line); auto elideWidth = _f->elidew; - _wLeft = _lineWidth - _ppadding.left() - _ppadding.right() - elideWidth; + _wLeft = _lineWidth + - _quotePadding.left() + - _quotePadding.right() + - elideWidth; int firstItem = engine.findItem(line.from), lastItem = engine.findItem(line.from + line.length - 1); int nItems = (firstItem >= 0 && lastItem >= firstItem) ? (lastItem - firstItem + 1) : 0, i; @@ -1518,8 +1549,8 @@ void Renderer::eItemize() { auto currentBlock = _t->_blocks[blockIndex].get(); auto nextBlock = (++blockIndex < _blocksSize) ? _t->_blocks[blockIndex].get() : nullptr; - _e->layoutData->hasBidi = _parHasBidi; - auto analysis = _parAnalysis.data() + (_localFrom - _parStart); + _e->layoutData->hasBidi = _paragraphHasBidi; + auto analysis = _paragraphAnalysis.data() + (_localFrom - _paragraphStart); { #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) @@ -1568,7 +1599,7 @@ void Renderer::eItemize() { { auto i_string = &_e->layoutData->string; - auto i_analysis = _parAnalysis.data() + (_localFrom - _parStart); + auto i_analysis = _paragraphAnalysis.data() + (_localFrom - _paragraphStart); auto i_items = &_e->layoutData->items; blockIndex = _lineStartBlock; @@ -1623,18 +1654,18 @@ QChar::Direction Renderer::eSkipBoundryNeutrals( QChar::Direction dir = control.basicDirection(); int level = sor > 0 ? analysis[sor - 1].bidiLevel : control.level; - while (sor <= _parLength) { - while (i != _parStartBlock && (*i)->position() > _parStart + sor) { + while (sor <= _paragraphLength) { + while (i != _paragraphStartBlock && (*i)->position() > _paragraphStart + sor) { n = i; --i; } - while (n != e && (*n)->position() <= _parStart + sor) { + while (n != e && (*n)->position() <= _paragraphStart + sor) { i = n; ++n; } TextBlockType _itype = (*i)->type(); - if (eor == _parLength) + if (eor == _paragraphLength) dir = control.basicDirection(); else if (_itype == TextBlockType::Emoji || _itype == TextBlockType::CustomEmoji) @@ -1662,16 +1693,16 @@ bool Renderer::eBidiItemize(QScriptAnalysis *analysis, BidiControl &control) { int sor = 0; int eor = -1; - const ushort *unicode = reinterpret_cast(_t->_text.unicode()) + _parStart; + const ushort *unicode = reinterpret_cast(_t->_text.unicode()) + _paragraphStart; int current = 0; QChar::Direction dir = rightToLeft ? QChar::DirR : QChar::DirL; BidiStatus status; - String::TextBlocks::const_iterator i = _parStartBlock, e = _t->_blocks.cend(), n = i + 1; + String::TextBlocks::const_iterator i = _paragraphStartBlock, e = _t->_blocks.cend(), n = i + 1; QChar::Direction sdir; - TextBlockType _stype = (*_parStartBlock)->type(); + TextBlockType _stype = (*_paragraphStartBlock)->type(); if (_stype == TextBlockType::Emoji || _stype == TextBlockType::CustomEmoji) sdir = QChar::DirCS; else if (_stype == TextBlockType::Skip) @@ -1688,15 +1719,15 @@ bool Renderer::eBidiItemize(QScriptAnalysis *analysis, BidiControl &control) { status.last = status.lastStrong; status.dir = sdir; - while (current <= _parLength) { - while (n != e && (*n)->position() <= _parStart + current) { + while (current <= _paragraphLength) { + while (n != e && (*n)->position() <= _paragraphStart + current) { i = n; ++n; } QChar::Direction dirCurrent; TextBlockType _itype = (*i)->type(); - if (current == (int)_parLength) + if (current == (int)_paragraphLength) dirCurrent = control.basicDirection(); else if (_itype == TextBlockType::Emoji || _itype == TextBlockType::CustomEmoji) @@ -2020,7 +2051,7 @@ bool Renderer::eBidiItemize(QScriptAnalysis *analysis, BidiControl &control) { break; } - if (current >= (int)_parLength) break; + if (current >= (int)_paragraphLength) break; // set status.last as needed. switch (dirCurrent) { diff --git a/ui/text/text_renderer.h b/ui/text/text_renderer.h index 08bc5de..3d39c6f 100644 --- a/ui/text/text_renderer.h +++ b/ui/text/text_renderer.h @@ -151,20 +151,25 @@ private: int _indexOfElidedBlock = -1; // For spoilers. // current paragraph data - String::TextBlocks::const_iterator _parStartBlock; - Qt::LayoutDirection _parDirection = Qt::LayoutDirectionAuto; - int _parStart = 0; - int _parLength = 0; - bool _parHasBidi = false; - QVarLengthArray _parAnalysis; - ParagraphDetails *_paragraph = nullptr; - int _pindex = 0; - QMargins _ppadding; - int _blockLineTop = 0; - BlockPaintCache *_preBlockCache = nullptr; - BlockPaintCache *_blockquoteBlockCache = nullptr; - bool _preBlockCacheValid = false; - bool _blockquoteBlockCacheValid = false; + String::TextBlocks::const_iterator _paragraphStartBlock; + Qt::LayoutDirection _paragraphDirection = Qt::LayoutDirectionAuto; + int _paragraphStart = 0; + int _paragraphLength = 0; + bool _paragraphHasBidi = false; + QVarLengthArray _paragraphAnalysis; + QFixed _paragraphWidthRemaining = 0; + + // current quote data + QuoteDetails *_quote = nullptr; + Qt::LayoutDirection _quoteDirection = Qt::LayoutDirectionAuto; + int _quoteShift = 0; + int _quoteIndex = 0; + QMargins _quotePadding; + int _quoteLineTop = 0; + QuotePaintCache *_quotePreCache = nullptr; + QuotePaintCache *_quoteBlockquoteCache = nullptr; + bool _quotePreValid = false; + bool _quoteBlockquoteValid = false; // current line data QTextEngine *_e = nullptr; @@ -189,7 +194,6 @@ private: int _localFrom = 0; int _lineStartBlock = 0; QFixed _lineWidth = 0; - QFixed _paragraphWidthRemaining = 0; // link and symbol resolve QFixed _lookupX = 0;