Count correctly elided text dimensions.
This commit is contained in:
parent
08235c5e06
commit
65310f32dc
4 changed files with 61 additions and 101 deletions
116
ui/text/text.cpp
116
ui/text/text.cpp
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue