Use quote instead of paragraph for quotes.

This commit is contained in:
John Preston 2023-10-13 18:23:56 +04:00
parent 48012d7f4a
commit 9eb9fcf043
9 changed files with 275 additions and 240 deletions

View file

@ -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;

View file

@ -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<const NewlineBlock*>(block)->paragraphIndex()
: _startParagraphIndex;
? static_cast<const NewlineBlock*>(block)->quoteIndex()
: _startQuoteIndex;
}
const style::ParagraphStyle &String::paragraphStyle(
not_null<ParagraphDetails*> info) const {
return info->pre ? _st->pre : _st->blockquote;
const style::QuoteStyle &String::quoteStyle(
not_null<QuoteDetails*> 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 ? &paragraphStyle(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 ? &quoteStyle(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;
}

View file

@ -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<SpecialColor> 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 &paragraphStyle(
not_null<ParagraphDetails*> info) const;
[[nodiscard]] QMargins paragraphPadding(ParagraphDetails *info) const;
[[nodiscard]] int paragraphMinWidth(ParagraphDetails *info) const;
[[nodiscard]] const QString &paragraphHeaderText(
ParagraphDetails *info) const;
[[nodiscard]] QuoteDetails *quoteByIndex(int index) const;
[[nodiscard]] const style::QuoteStyle &quoteStyle(
not_null<QuoteDetails*> quote) const;
[[nodiscard]] QMargins quotePadding(QuoteDetails *quote) const;
[[nodiscard]] int quoteMinWidth(QuoteDetails *quote) const;
[[nodiscard]] const QString &quoteHeaderText(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;

View file

@ -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;

View file

@ -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<ClickHandlerPtr> links;
std::vector<ParagraphDetails> paragraphs;
std::vector<QuoteDetails> quotes;
std::unique_ptr<SpoilerData> spoiler;
std::vector<Modification> modifications;
};

View file

@ -212,7 +212,7 @@ void Parser::createBlock(int32 skipBack) {
} else if (newline) {
push(&Block::Newline);
auto &newline = _t->_blocks.back().unsafe<NewlineBlock>();
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 &paragraphs = _t->ensureExtended()->paragraphs;
paragraphs.push_back(std::move(details));
const auto index = _paragraphIndex = int(paragraphs.size());
auto &quotes = _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<NewlineBlock>()._paragraphIndex = index;
last.unsafe<NewlineBlock>()._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<NewlineBlock>()._paragraphIndex = 0;
last.unsafe<NewlineBlock>()._quoteIndex = 0;
}
}
if (IsMono(*flags)) {

View file

@ -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

View file

@ -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<const NewlineBlock*>(b)->paragraphIndex();
const auto changed = (_pindex != pindex);
fillParagraphBg(changed ? _ppadding.bottom() : 0);
const auto qindex = static_cast<const NewlineBlock*>(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<const NewlineBlock*>(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<const ushort*>(_str) + _parStart;
const ushort *start = reinterpret_cast<const ushort*>(_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<const ushort*>(_t->_text.unicode()) + _parStart;
const ushort *unicode = reinterpret_cast<const ushort*>(_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) {

View file

@ -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<QScriptAnalysis, 4096> _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<QScriptAnalysis, 4096> _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;