Count correctly elided text dimensions.

This commit is contained in:
John Preston 2023-11-06 11:28:27 +04:00
parent 08235c5e06
commit 65310f32dc
4 changed files with 61 additions and 101 deletions

View file

@ -142,47 +142,36 @@ not_null<SpoilerMessCache*> DefaultSpoilerCache() {
GeometryDescriptor SimpleGeometry( GeometryDescriptor SimpleGeometry(
int availableWidth, int availableWidth,
int fontHeight, int elisionLines,
int elisionHeight,
int elisionRemoveFromEnd, int elisionRemoveFromEnd,
bool elisionOneLine,
bool elisionBreakEverywhere) { bool elisionBreakEverywhere) {
constexpr auto wrap = []( constexpr auto wrap = [](
Fn<LineGeometry(LineGeometry)> layout, Fn<LineGeometry(int line)> layout,
bool breakEverywhere = false) { bool breakEverywhere = false) {
return GeometryDescriptor{ std::move(layout), breakEverywhere }; return GeometryDescriptor{ std::move(layout), breakEverywhere };
}; };
// Try to minimize captured values (to minimize Fn allocations). // Try to minimize captured values (to minimize Fn allocations).
if (!elisionOneLine && !elisionHeight) { if (!elisionLines) {
return wrap([=](LineGeometry line) { return wrap([=](int line) {
line.width = availableWidth; return LineGeometry{ .width = availableWidth };
return line;
}); });
} else if (elisionOneLine) {
return wrap([=](LineGeometry line) {
line.elided = true;
line.width = availableWidth - elisionRemoveFromEnd;
return line;
}, elisionBreakEverywhere);
} else if (!elisionRemoveFromEnd) { } else if (!elisionRemoveFromEnd) {
return wrap([=](LineGeometry line) { return wrap([=](int line) {
if (line.top + fontHeight * 2 > elisionHeight) { return LineGeometry{
line.elided = true; .width = availableWidth,
} .elided = (line + 1 >= elisionLines),
line.width = availableWidth; };
return line; }, elisionBreakEverywhere);
});
} else { } else {
return wrap([=](LineGeometry line) { return wrap([=](int line) {
if (line.top + fontHeight * 2 > elisionHeight) { const auto elided = (line + 1 >= elisionLines);
line.elided = true; const auto removeFromEnd = (elided ? elisionRemoveFromEnd : 0);
line.width = availableWidth - elisionRemoveFromEnd; return LineGeometry{
} else { .width = availableWidth - removeFromEnd,
line.width = availableWidth; .elided = elided,
} };
return line; }, elisionBreakEverywhere);
});
} }
}; };
@ -913,7 +902,7 @@ void String::enumerateLines(
return; return;
} }
const auto width = std::max(w, _minResizeWidth); const auto width = std::max(w, _minResizeWidth);
auto g = SimpleGeometry(width, _st->font->height, 0, 0, false, false); auto g = SimpleGeometry(width, 0, 0, false);
g.breakEverywhere = breakEverywhere; g.breakEverywhere = breakEverywhere;
enumerateLines(g, std::forward<Callback>(callback)); enumerateLines(g, std::forward<Callback>(callback));
} }
@ -925,6 +914,13 @@ void String::enumerateLines(
if (isEmpty()) { if (isEmpty()) {
return; return;
} }
const auto withElided = [&](bool elided) {
if (geometry.outElided) {
*geometry.outElided = elided;
}
};
auto qindex = 0; auto qindex = 0;
auto quote = (QuoteDetails*)nullptr; auto quote = (QuoteDetails*)nullptr;
auto qpadding = QMargins(); auto qpadding = QMargins();
@ -932,16 +928,14 @@ void String::enumerateLines(
auto top = 0; auto top = 0;
auto lineLeft = 0; auto lineLeft = 0;
auto lineWidth = 0; auto lineWidth = 0;
auto lineElided = false;
auto widthLeft = QFixed(0); auto widthLeft = QFixed(0);
auto paragraphWidthRemaining = QFixed(); auto lineIndex = 0;
const auto initNextLine = [&] { const auto initNextLine = [&] {
const auto line = geometry.layout({ const auto line = geometry.layout(lineIndex++);
.left = 0,
.top = top,
.width = paragraphWidthRemaining.ceil().toInt(),
});
lineLeft = line.left; lineLeft = line.left;
lineWidth = line.width; lineWidth = line.width;
lineElided = line.elided;
if (quote && quote->maxWidth < lineWidth) { if (quote && quote->maxWidth < lineWidth) {
const auto delta = lineWidth - quote->maxWidth; const auto delta = lineWidth - quote->maxWidth;
lineWidth = quote->maxWidth; lineWidth = quote->maxWidth;
@ -951,7 +945,6 @@ void String::enumerateLines(
const auto initNextParagraph = [&]( const auto initNextParagraph = [&](
TextBlocks::const_iterator i, TextBlocks::const_iterator i,
int16 paragraphIndex) { int16 paragraphIndex) {
paragraphWidthRemaining = 0;
if (qindex != paragraphIndex) { if (qindex != paragraphIndex) {
top += qpadding.bottom(); top += qpadding.bottom();
qindex = paragraphIndex; qindex = paragraphIndex;
@ -960,23 +953,6 @@ void String::enumerateLines(
top += qpadding.top(); top += qpadding.top();
qpadding.setTop(0); qpadding.setTop(0);
} }
const auto e = _blocks.cend();
if (i != e) {
auto last_rPadding = QFixed(0);
auto last_rBearing = QFixed(0);
for (; i != e; ++i) {
if ((*i)->type() == TextBlockType::Newline) {
break;
}
const auto rBearing = (*i)->f_rbearing();
paragraphWidthRemaining += last_rBearing
+ last_rPadding
+ (*i)->f_width()
- rBearing;
last_rBearing = rBearing;
}
}
paragraphWidthRemaining += qpadding.left() + qpadding.right();
initNextLine(); initNextLine();
}; };
@ -1004,6 +980,9 @@ void String::enumerateLines(
} }
callback(lineLeft + lineWidth - widthLeft, top += lineHeight); callback(lineLeft + lineWidth - widthLeft, top += lineHeight);
if (lineElided) {
return withElided(true);
}
lineHeight = 0; lineHeight = 0;
initNextParagraph(i + 1, index); initNextParagraph(i + 1, index);
@ -1059,7 +1038,9 @@ void String::enumerateLines(
continue; continue;
} }
if (f != j && !geometry.breakEverywhere) { if (lineElided) {
lineHeight = qMax(lineHeight, blockHeight);
} else if (f != j && !geometry.breakEverywhere) {
j = f; j = f;
widthLeft = f_wLeft; widthLeft = f_wLeft;
lineHeight = f_lineHeight; lineHeight = f_lineHeight;
@ -1067,10 +1048,12 @@ void String::enumerateLines(
} }
callback(lineLeft + lineWidth - widthLeft, top += lineHeight); callback(lineLeft + lineWidth - widthLeft, top += lineHeight);
if (lineElided) {
return withElided(true);
}
lineHeight = qMax(0, blockHeight); lineHeight = qMax(0, blockHeight);
paragraphWidthRemaining -= (lineWidth - widthLeft) - last_rPadding + last_rBearing;
initNextLine(); initNextLine();
last_rBearing = j->f_rbearing(); last_rBearing = j->f_rbearing();
@ -1085,10 +1068,15 @@ void String::enumerateLines(
continue; continue;
} }
if (lineElided) {
lineHeight = qMax(lineHeight, blockHeight);
}
callback(lineLeft + lineWidth - widthLeft, top += lineHeight); callback(lineLeft + lineWidth - widthLeft, top += lineHeight);
if (lineElided) {
return withElided(true);
}
lineHeight = qMax(0, blockHeight); lineHeight = qMax(0, blockHeight);
paragraphWidthRemaining -= (lineWidth - widthLeft) - last_rPadding + last_rBearing;
initNextLine(); initNextLine();
last_rBearing = b__f_rbearing; last_rBearing = b__f_rbearing;
@ -1103,6 +1091,7 @@ void String::enumerateLines(
lineLeft + lineWidth - widthLeft, lineLeft + lineWidth - widthLeft,
top + lineHeight + qpadding.bottom()); top + lineHeight + qpadding.bottom());
} }
return withElided(false);
} }
void String::draw(QPainter &p, const PaintContext &context) const { void String::draw(QPainter &p, const PaintContext &context) const {
@ -1144,11 +1133,8 @@ void String::drawElided(Painter &p, int32 left, int32 top, int32 w, int32 lines,
.palette = &p.textPalette(), .palette = &p.textPalette(),
.paused = p.inactive(), .paused = p.inactive(),
.selection = selection, .selection = selection,
.elisionHeight = ((!isEmpty() && lines > 1) .elisionLines = lines,
? (lines * _st->font->height)
: 0),
.elisionRemoveFromEnd = removeFromEnd, .elisionRemoveFromEnd = removeFromEnd,
.elisionOneLine = (lines == 1),
}); });
} }
@ -1185,7 +1171,7 @@ StateResult String::getState(QPoint point, int width, StateRequest request) cons
} }
return Renderer(*this).getState( return Renderer(*this).getState(
point, point,
SimpleGeometry(width, _st->font->height, 0, 0, false, false), SimpleGeometry(width, 0, 0, false),
request); request);
} }
@ -1199,10 +1185,8 @@ StateResult String::getStateElided(QPoint point, int width, StateRequestElided r
} }
return Renderer(*this).getState(point, SimpleGeometry( return Renderer(*this).getState(point, SimpleGeometry(
width, width,
_st->font->height, request.lines,
(request.lines > 1) ? (request.lines * _st->font->height) : 0,
request.removeFromEnd, request.removeFromEnd,
(request.lines == 1),
request.flags & StateRequest::Flag::BreakEverywhere request.flags & StateRequest::Flag::BreakEverywhere
), static_cast<StateRequest>(request)); ), static_cast<StateRequest>(request));
} }

View file

@ -146,23 +146,21 @@ struct SpecialColor {
struct LineGeometry { struct LineGeometry {
int left = 0; int left = 0;
int top = 0;
int width = 0; int width = 0;
bool elided = false; bool elided = false;
}; };
struct GeometryDescriptor { struct GeometryDescriptor {
Fn<LineGeometry(LineGeometry line)> layout; Fn<LineGeometry(int line)> layout;
bool breakEverywhere = false; bool breakEverywhere = false;
bool *outElided = nullptr;
}; };
[[nodiscard]] not_null<SpoilerMessCache*> DefaultSpoilerCache(); [[nodiscard]] not_null<SpoilerMessCache*> DefaultSpoilerCache();
[[nodiscard]] GeometryDescriptor SimpleGeometry( [[nodiscard]] GeometryDescriptor SimpleGeometry(
int availableWidth, int availableWidth,
int fontHeight, int elisionLines,
int elisionHeight,
int elisionRemoveFromEnd, int elisionRemoveFromEnd,
bool elisionOneLine,
bool elisionBreakEverywhere); bool elisionBreakEverywhere);
constexpr auto kMaxQuoteOutlines = 3; constexpr auto kMaxQuoteOutlines = 3;
@ -230,8 +228,8 @@ struct PaintContext {
HighlightInfoRequest *highlight = nullptr; HighlightInfoRequest *highlight = nullptr;
int elisionHeight = 0; int elisionHeight = 0;
int elisionLines = 0;
int elisionRemoveFromEnd = 0; int elisionRemoveFromEnd = 0;
bool elisionOneLine = false;
bool elisionBreakEverywhere = false; bool elisionBreakEverywhere = false;
}; };

View file

@ -189,10 +189,10 @@ void Renderer::draw(QPainter &p, const PaintContext &context) {
? context.geometry ? context.geometry
: SimpleGeometry( : SimpleGeometry(
context.availableWidth, context.availableWidth,
_t->_st->font->height, (context.elisionLines
context.elisionHeight, ? context.elisionLines
: (context.elisionHeight / _t->_st->font->height)),
context.elisionRemoveFromEnd, context.elisionRemoveFromEnd,
context.elisionOneLine,
context.elisionBreakEverywhere); context.elisionBreakEverywhere);
_breakEverywhere = _geometry.breakEverywhere; _breakEverywhere = _geometry.breakEverywhere;
_spoilerCache = context.spoiler; _spoilerCache = context.spoiler;
@ -213,6 +213,8 @@ void Renderer::draw(QPainter &p, const PaintContext &context) {
} }
void Renderer::enumerate() { void Renderer::enumerate() {
Expects(!_geometry.outElided);
_blocksSize = _t->_blocks.size(); _blocksSize = _t->_blocks.size();
_str = _t->_text.unicode(); _str = _t->_text.unicode();
@ -356,7 +358,6 @@ void Renderer::enumerate() {
_lineHeight = qMax(0, blockHeight); _lineHeight = qMax(0, blockHeight);
_lineStart = j->position(); _lineStart = j->position();
_lineStartBlock = blockIndex; _lineStartBlock = blockIndex;
_paragraphWidthRemaining -= (_lineWidth - _wLeft) - _last_rPadding + last_rBearing;
initNextLine(); initNextLine();
last_rBearing = j->f_rbearing(); last_rBearing = j->f_rbearing();
@ -385,7 +386,6 @@ void Renderer::enumerate() {
_lineHeight = qMax(0, blockHeight); _lineHeight = qMax(0, blockHeight);
_lineStart = b->position(); _lineStart = b->position();
_lineStartBlock = blockIndex; _lineStartBlock = blockIndex;
_paragraphWidthRemaining -= (_lineWidth - _wLeft) - _last_rPadding + last_rBearing;
initNextLine(); initNextLine();
last_rBearing = b__f_rbearing; last_rBearing = b__f_rbearing;
@ -506,7 +506,6 @@ void Renderer::initNextParagraph(
? style::LayoutDirection() ? style::LayoutDirection()
: direction; : direction;
_paragraphStartBlock = i; _paragraphStartBlock = i;
_paragraphWidthRemaining = 0;
if (_quoteIndex != paragraphIndex) { if (_quoteIndex != paragraphIndex) {
_y += _quotePadding.bottom(); _y += _quotePadding.bottom();
_quoteIndex = paragraphIndex; _quoteIndex = paragraphIndex;
@ -525,39 +524,18 @@ void Renderer::initNextParagraph(
} else { } else {
_lineStart = _paragraphStart = (*i)->position(); _lineStart = _paragraphStart = (*i)->position();
_lineStartBlock = i - _t->_blocks.cbegin(); _lineStartBlock = i - _t->_blocks.cbegin();
auto last_rPadding = QFixed(0);
auto last_rBearing = QFixed(0);
for (; i != e; ++i) {
if ((*i)->type() == TextBlockType::Newline) {
break;
}
const auto rBearing = (*i)->f_rbearing();
_paragraphWidthRemaining += last_rBearing
+ last_rPadding
+ (*i)->f_width()
- rBearing;
last_rBearing = rBearing;
}
_paragraphLength = ((i == e) _paragraphLength = ((i == e)
? _t->_text.size() ? _t->_text.size()
: (*i)->position()) : (*i)->position())
- _paragraphStart; - _paragraphStart;
} }
_paragraphAnalysis.resize(0); _paragraphAnalysis.resize(0);
_paragraphWidthRemaining += _quotePadding.left() + _quotePadding.right();
initNextLine(); initNextLine();
} }
void Renderer::initNextLine() { void Renderer::initNextLine() {
const auto line = _geometry.layout({ const auto line = _geometry.layout(_lineIndex++);
.left = 0,
.top = (_y - _startTop),
.width = _paragraphWidthRemaining.ceil().toInt(),
});
_quoteLineTop += _startTop + line.top - _y;
_x = _startLeft + line.left + _quotePadding.left(); _x = _startLeft + line.left + _quotePadding.left();
_y = _startTop + line.top;
_startLineWidth = line.width; _startLineWidth = line.width;
_quoteShift = 0; _quoteShift = 0;
if (_quote && _quote->maxWidth < _startLineWidth) { if (_quote && _quote->maxWidth < _startLineWidth) {

View file

@ -179,7 +179,6 @@ private:
int _paragraphLength = 0; int _paragraphLength = 0;
bool _paragraphHasBidi = false; bool _paragraphHasBidi = false;
QVarLengthArray<QScriptAnalysis, 4096> _paragraphAnalysis; QVarLengthArray<QScriptAnalysis, 4096> _paragraphAnalysis;
QFixed _paragraphWidthRemaining = 0;
// current quote data // current quote data
QuoteDetails *_quote = nullptr; QuoteDetails *_quote = nullptr;
@ -203,6 +202,7 @@ private:
QFixed _x, _wLeft, _last_rPadding; QFixed _x, _wLeft, _last_rPadding;
int _y = 0; int _y = 0;
int _yDelta = 0; int _yDelta = 0;
int _lineIndex = 0;
int _lineHeight = 0; int _lineHeight = 0;
int _fontHeight = 0; int _fontHeight = 0;
bool _breakEverywhere = false; bool _breakEverywhere = false;