Paint nice code blocks.
This commit is contained in:
parent
ab5057f001
commit
a38b60636a
9 changed files with 431 additions and 53 deletions
|
|
@ -23,7 +23,11 @@ TextStyle {
|
||||||
font: font;
|
font: font;
|
||||||
linkUnderline: int;
|
linkUnderline: int;
|
||||||
blockPadding: margins;
|
blockPadding: margins;
|
||||||
|
blockVerticalSkip: pixels;
|
||||||
|
blockHeader: pixels;
|
||||||
|
blockHeaderPosition: point;
|
||||||
blockOutline: pixels;
|
blockOutline: pixels;
|
||||||
|
blockRadius: pixels;
|
||||||
preScrollable: bool;
|
preScrollable: bool;
|
||||||
lineHeight: pixels;
|
lineHeight: pixels;
|
||||||
}
|
}
|
||||||
|
|
@ -57,9 +61,6 @@ defaultTextPalette: TextPalette {
|
||||||
defaultTextStyle: TextStyle {
|
defaultTextStyle: TextStyle {
|
||||||
font: normalFont;
|
font: normalFont;
|
||||||
linkUnderline: kLinkUnderlineActive;
|
linkUnderline: kLinkUnderlineActive;
|
||||||
blockPadding: margins(10px, 4px, 6px, 4px);
|
|
||||||
blockOutline: 3px;
|
|
||||||
preScrollable: true;
|
|
||||||
lineHeight: 0px;
|
lineHeight: 0px;
|
||||||
}
|
}
|
||||||
semiboldTextStyle: TextStyle(defaultTextStyle) {
|
semiboldTextStyle: TextStyle(defaultTextStyle) {
|
||||||
|
|
|
||||||
293
ui/text/text.cpp
293
ui/text/text.cpp
|
|
@ -185,6 +185,144 @@ GeometryDescriptor SimpleGeometry(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void ValidateBlockPaintCache(
|
||||||
|
BlockPaintCache &cache,
|
||||||
|
const style::TextStyle &st) {
|
||||||
|
if (!cache.corners.isNull()
|
||||||
|
&& cache.bgCached == cache.bg
|
||||||
|
&& cache.outlineCached == cache.outline
|
||||||
|
&& (!cache.withHeader || cache.headerCached == cache.header)
|
||||||
|
&& (!cache.topright || cache.iconCached == cache.icon)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cache.bgCached = cache.bg;
|
||||||
|
cache.outlineCached = cache.outline;
|
||||||
|
if (cache.withHeader) {
|
||||||
|
cache.headerCached = cache.header;
|
||||||
|
}
|
||||||
|
if (cache.topright) {
|
||||||
|
cache.iconCached = cache.icon;
|
||||||
|
}
|
||||||
|
const auto radius = st.blockRadius;
|
||||||
|
const auto header = cache.withHeader ? st.blockHeader : 0;
|
||||||
|
const auto outline = st.blockOutline;
|
||||||
|
const auto corner = std::max({ header, radius, outline });
|
||||||
|
const auto middle = st::lineWidth;
|
||||||
|
const auto side = 2 * corner + middle;
|
||||||
|
const auto full = QSize(side, side);
|
||||||
|
const auto ratio = style::DevicePixelRatio();
|
||||||
|
auto image = QImage(full * ratio, QImage::Format_ARGB32_Premultiplied);
|
||||||
|
image.fill(Qt::transparent);
|
||||||
|
image.setDevicePixelRatio(ratio);
|
||||||
|
auto p = QPainter(&image);
|
||||||
|
auto hq = PainterHighQualityEnabler(p);
|
||||||
|
p.setPen(Qt::NoPen);
|
||||||
|
|
||||||
|
auto rect = QRect(QPoint(), full);
|
||||||
|
if (header) {
|
||||||
|
p.setBrush(cache.header);
|
||||||
|
p.setClipRect(outline, 0, side - outline, header);
|
||||||
|
p.drawRoundedRect(0, 0, side, corner + radius, radius, radius);
|
||||||
|
}
|
||||||
|
if (outline) {
|
||||||
|
p.setBrush(cache.outline);
|
||||||
|
p.setClipRect(0, 0, outline, side);
|
||||||
|
p.drawRoundedRect(0, 0, outline + radius * 2, side, radius, radius);
|
||||||
|
}
|
||||||
|
p.setBrush(cache.bg);
|
||||||
|
p.setClipRect(outline, header, side - outline, side - header);
|
||||||
|
p.drawRoundedRect(0, 0, side, side, radius, radius);
|
||||||
|
|
||||||
|
p.end();
|
||||||
|
cache.corners = std::move(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FillBlockPaint(
|
||||||
|
QPainter &p,
|
||||||
|
QRect rect,
|
||||||
|
const BlockPaintCache &cache,
|
||||||
|
const style::TextStyle &st,
|
||||||
|
SkipBlockPaintParts parts) {
|
||||||
|
const auto &image = cache.corners;
|
||||||
|
const auto ratio = int(image.devicePixelRatio());
|
||||||
|
const auto iwidth = image.width() / ratio;
|
||||||
|
const auto iheight = image.height() / ratio;
|
||||||
|
const auto imiddle = st::lineWidth;
|
||||||
|
const auto ihalf = (iheight - imiddle) / 2;
|
||||||
|
const auto x = rect.left();
|
||||||
|
const auto width = rect.width();
|
||||||
|
auto y = rect.top();
|
||||||
|
auto height = rect.height();
|
||||||
|
if (!parts.skipTop) {
|
||||||
|
const auto top = std::min(height, ihalf);
|
||||||
|
p.drawImage(
|
||||||
|
QRect(x, y, ihalf, top),
|
||||||
|
image,
|
||||||
|
QRect(0, 0, ihalf * ratio, top * ratio));
|
||||||
|
p.drawImage(
|
||||||
|
QRect(x + width - ihalf, y, ihalf, top),
|
||||||
|
image,
|
||||||
|
QRect((iwidth - ihalf) * ratio, 0, ihalf * ratio, top * ratio));
|
||||||
|
if (const auto middle = width - 2 * ihalf) {
|
||||||
|
const auto header = cache.withHeader ? st.blockHeader : 0;
|
||||||
|
const auto fillHeader = std::min(header, top);
|
||||||
|
if (fillHeader) {
|
||||||
|
p.fillRect(x + ihalf, y, middle, fillHeader, cache.header);
|
||||||
|
}
|
||||||
|
if (const auto fillBody = top - fillHeader) {
|
||||||
|
p.fillRect(
|
||||||
|
QRect(x + ihalf, y + fillHeader, middle, fillBody),
|
||||||
|
cache.bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
height -= top;
|
||||||
|
if (!height) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
y += top;
|
||||||
|
rect.setTop(y);
|
||||||
|
}
|
||||||
|
if (!parts.skipBottom) {
|
||||||
|
const auto bottom = std::min(height, ihalf);
|
||||||
|
p.drawImage(
|
||||||
|
QRect(x, y + height - bottom, ihalf, bottom),
|
||||||
|
image,
|
||||||
|
QRect(
|
||||||
|
0,
|
||||||
|
(iheight - bottom) * ratio,
|
||||||
|
ihalf * ratio,
|
||||||
|
bottom * ratio));
|
||||||
|
p.drawImage(
|
||||||
|
QRect(
|
||||||
|
x + width - ihalf,
|
||||||
|
y + height - bottom,
|
||||||
|
ihalf,
|
||||||
|
bottom),
|
||||||
|
image,
|
||||||
|
QRect(
|
||||||
|
(iwidth - ihalf) * ratio,
|
||||||
|
(iheight - bottom) * ratio,
|
||||||
|
ihalf * ratio,
|
||||||
|
bottom * ratio));
|
||||||
|
if (const auto middle = width - 2 * ihalf) {
|
||||||
|
p.fillRect(
|
||||||
|
QRect(x + ihalf, y + height - bottom, middle, bottom),
|
||||||
|
cache.bg);
|
||||||
|
}
|
||||||
|
height -= bottom;
|
||||||
|
if (!height) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
rect.setHeight(height);
|
||||||
|
}
|
||||||
|
const auto outline = st.blockOutline;
|
||||||
|
if (outline) {
|
||||||
|
p.fillRect(x, y, outline, height, cache.outline);
|
||||||
|
}
|
||||||
|
p.fillRect(x + outline, y, width - outline, height, cache.bg);
|
||||||
|
}
|
||||||
|
|
||||||
String::ExtendedWrap::ExtendedWrap() noexcept = default;
|
String::ExtendedWrap::ExtendedWrap() noexcept = default;
|
||||||
|
|
||||||
String::ExtendedWrap::ExtendedWrap(ExtendedWrap &&other) noexcept
|
String::ExtendedWrap::ExtendedWrap(ExtendedWrap &&other) noexcept
|
||||||
|
|
@ -283,18 +421,34 @@ void String::recountNaturalSize(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
_maxWidth = _minHeight = 0;
|
auto pindex = paragraphIndex(nullptr);
|
||||||
int32 lineHeight = 0;
|
auto paragraph = paragraphByIndex(pindex);
|
||||||
QFixed maxWidth = 0;
|
auto ppadding = paragraphPadding(paragraph);
|
||||||
QFixed width = 0, last_rBearing = 0, last_rPadding = 0;
|
|
||||||
|
_maxWidth = 0;
|
||||||
|
_minHeight = ppadding.top();
|
||||||
|
auto lineHeight = 0;
|
||||||
|
auto maxWidth = QFixed();
|
||||||
|
auto width = QFixed(ppadding.left() + ppadding.right());
|
||||||
|
auto last_rBearing = QFixed();
|
||||||
|
auto last_rPadding = QFixed();
|
||||||
for (auto &block : _blocks) {
|
for (auto &block : _blocks) {
|
||||||
auto b = block.get();
|
const auto b = block.get();
|
||||||
auto _btype = b->type();
|
const auto _btype = b->type();
|
||||||
auto blockHeight = CountBlockHeight(b, _st);
|
const auto blockHeight = CountBlockHeight(b, _st);
|
||||||
if (_btype == TextBlockType::Newline) {
|
if (_btype == TextBlockType::Newline) {
|
||||||
if (!lineHeight) {
|
if (!lineHeight) {
|
||||||
lineHeight = blockHeight;
|
lineHeight = blockHeight;
|
||||||
}
|
}
|
||||||
|
const auto index = paragraphIndex(b);
|
||||||
|
if (pindex != index) {
|
||||||
|
_minHeight += ppadding.bottom();
|
||||||
|
pindex = index;
|
||||||
|
paragraph = paragraphByIndex(pindex);
|
||||||
|
ppadding = paragraphPadding(paragraph);
|
||||||
|
_minHeight += ppadding.top();
|
||||||
|
ppadding.setTop(0);
|
||||||
|
}
|
||||||
if (initial) {
|
if (initial) {
|
||||||
computeParagraphDirection(b->position());
|
computeParagraphDirection(b->position());
|
||||||
}
|
}
|
||||||
|
|
@ -303,11 +457,12 @@ void String::recountNaturalSize(
|
||||||
|
|
||||||
_minHeight += lineHeight;
|
_minHeight += lineHeight;
|
||||||
lineHeight = 0;
|
lineHeight = 0;
|
||||||
last_rBearing = b->f_rbearing();
|
last_rBearing = 0;// b->f_rbearing(); (0 for newline)
|
||||||
last_rPadding = b->f_rpadding();
|
last_rPadding = 0;// b->f_rpadding(); (0 for newline)
|
||||||
|
|
||||||
accumulate_max(maxWidth, width);
|
accumulate_max(maxWidth, width);
|
||||||
width = (b->f_width() - last_rBearing);
|
width = ppadding.left() + ppadding.right();
|
||||||
|
// + (b->f_width() - last_rBearing); (0 for newline)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -333,11 +488,14 @@ void String::recountNaturalSize(
|
||||||
computeParagraphDirection(_text.size());
|
computeParagraphDirection(_text.size());
|
||||||
}
|
}
|
||||||
if (width > 0) {
|
if (width > 0) {
|
||||||
if (!lineHeight) lineHeight = CountBlockHeight(_blocks.back().get(), _st);
|
if (!lineHeight) {
|
||||||
_minHeight += lineHeight;
|
lineHeight = CountBlockHeight(_blocks.back().get(), _st);
|
||||||
|
}
|
||||||
|
_minHeight += ppadding.top() + lineHeight + ppadding.bottom();
|
||||||
accumulate_max(maxWidth, width);
|
accumulate_max(maxWidth, width);
|
||||||
}
|
}
|
||||||
_maxWidth = maxWidth.ceil().toInt();
|
_maxWidth = maxWidth.ceil().toInt();
|
||||||
|
_endsWithParagraphDetails = (pindex != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int String::countMaxMonospaceWidth() const {
|
int String::countMaxMonospaceWidth() const {
|
||||||
|
|
@ -487,6 +645,17 @@ bool String::updateSkipBlock(int width, int height) {
|
||||||
}
|
}
|
||||||
_text.resize(block->position());
|
_text.resize(block->position());
|
||||||
_blocks.pop_back();
|
_blocks.pop_back();
|
||||||
|
} else if (_endsWithParagraphDetails) {
|
||||||
|
_text.push_back(QChar::LineFeed);
|
||||||
|
_blocks.push_back(Block::Newline(
|
||||||
|
_st->font,
|
||||||
|
_text,
|
||||||
|
_text.size() - 1,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0));
|
||||||
|
_skipBlockAddedNewline = true;
|
||||||
}
|
}
|
||||||
_text.push_back('_');
|
_text.push_back('_');
|
||||||
_blocks.push_back(Block::Skip(
|
_blocks.push_back(Block::Skip(
|
||||||
|
|
@ -504,9 +673,15 @@ bool String::updateSkipBlock(int width, int height) {
|
||||||
bool String::removeSkipBlock() {
|
bool String::removeSkipBlock() {
|
||||||
if (_blocks.empty() || _blocks.back()->type() != TextBlockType::Skip) {
|
if (_blocks.empty() || _blocks.back()->type() != TextBlockType::Skip) {
|
||||||
return false;
|
return false;
|
||||||
}
|
} else if (_skipBlockAddedNewline) {
|
||||||
|
_text.resize(_blocks.back()->position() - 1);
|
||||||
|
_blocks.pop_back();
|
||||||
|
_blocks.pop_back();
|
||||||
|
_skipBlockAddedNewline = false;
|
||||||
|
} else {
|
||||||
_text.resize(_blocks.back()->position());
|
_text.resize(_blocks.back()->position());
|
||||||
_blocks.pop_back();
|
_blocks.pop_back();
|
||||||
|
}
|
||||||
recountNaturalSize(false);
|
recountNaturalSize(false);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -558,24 +733,42 @@ void String::enumerateLines(
|
||||||
int w,
|
int w,
|
||||||
bool breakEverywhere,
|
bool breakEverywhere,
|
||||||
Callback callback) const {
|
Callback callback) const {
|
||||||
QFixed width = w;
|
const auto width = QFixed(std::max(w, _minResizeWidth));
|
||||||
if (width < _minResizeWidth) width = _minResizeWidth;
|
|
||||||
|
|
||||||
int lineHeight = 0;
|
auto pindex = paragraphIndex(nullptr);
|
||||||
QFixed widthLeft = width, last_rBearing = 0, last_rPadding = 0;
|
auto paragraph = paragraphByIndex(pindex);
|
||||||
|
auto ppadding = paragraphPadding(paragraph);
|
||||||
|
auto widthLeft = width - ppadding.left() - ppadding.right();
|
||||||
|
auto lineHeight = 0;
|
||||||
|
auto last_rBearing = QFixed();
|
||||||
|
auto last_rPadding = QFixed();
|
||||||
bool longWordLine = true;
|
bool longWordLine = true;
|
||||||
for (auto &b : _blocks) {
|
for (auto &b : _blocks) {
|
||||||
auto _btype = b->type();
|
auto _btype = b->type();
|
||||||
int blockHeight = CountBlockHeight(b.get(), _st);
|
const auto blockHeight = CountBlockHeight(b.get(), _st);
|
||||||
|
|
||||||
if (_btype == TextBlockType::Newline) {
|
if (_btype == TextBlockType::Newline) {
|
||||||
if (!lineHeight) lineHeight = blockHeight;
|
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);
|
||||||
|
} else {
|
||||||
|
ppadding.setTop(0);
|
||||||
|
}
|
||||||
|
|
||||||
callback(width - widthLeft, lineHeight);
|
callback(width - widthLeft, lineHeight);
|
||||||
|
|
||||||
lineHeight = 0;
|
lineHeight = 0;
|
||||||
last_rBearing = b->f_rbearing();
|
last_rBearing = 0;// b->f_rbearing(); (0 for newline)
|
||||||
last_rPadding = b->f_rpadding();
|
last_rPadding = 0;// b->f_rpadding(); (0 for newline)
|
||||||
widthLeft = width - (b->f_width() - last_rBearing);
|
widthLeft = width - ppadding.left() - ppadding.right();
|
||||||
|
// - (b->f_width() - last_rBearing); (0 for newline)
|
||||||
|
|
||||||
longWordLine = true;
|
longWordLine = true;
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -636,12 +829,16 @@ void String::enumerateLines(
|
||||||
j_width = (j->f_width() >= 0) ? j->f_width() : -j->f_width();
|
j_width = (j->f_width() >= 0) ? j->f_width() : -j->f_width();
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(width - widthLeft, lineHeight);
|
callback(width - widthLeft, lineHeight + ppadding.top());
|
||||||
|
ppadding.setTop(0);
|
||||||
|
|
||||||
lineHeight = qMax(0, blockHeight);
|
lineHeight = qMax(0, blockHeight);
|
||||||
last_rBearing = j->f_rbearing();
|
last_rBearing = j->f_rbearing();
|
||||||
last_rPadding = j->f_rpadding();
|
last_rPadding = j->f_rpadding();
|
||||||
widthLeft = width - (j_width - last_rBearing);
|
widthLeft = width
|
||||||
|
- ppadding.left()
|
||||||
|
- ppadding.right()
|
||||||
|
- (j_width - last_rBearing);
|
||||||
|
|
||||||
longWordLine = !wordEndsHere;
|
longWordLine = !wordEndsHere;
|
||||||
f = j + 1;
|
f = j + 1;
|
||||||
|
|
@ -651,18 +848,24 @@ void String::enumerateLines(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(width - widthLeft, lineHeight);
|
callback(width - widthLeft, lineHeight + ppadding.top());
|
||||||
|
ppadding.setTop(0);
|
||||||
|
|
||||||
lineHeight = qMax(0, blockHeight);
|
lineHeight = qMax(0, blockHeight);
|
||||||
last_rBearing = b__f_rbearing;
|
last_rBearing = b__f_rbearing;
|
||||||
last_rPadding = b->f_rpadding();
|
last_rPadding = b->f_rpadding();
|
||||||
widthLeft = width - (b->f_width() - last_rBearing);
|
widthLeft = width
|
||||||
|
- ppadding.left()
|
||||||
|
- ppadding.right()
|
||||||
|
- (b->f_width() - last_rBearing);
|
||||||
|
|
||||||
longWordLine = true;
|
longWordLine = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (widthLeft < width) {
|
if (widthLeft < width) {
|
||||||
callback(width - widthLeft, lineHeight);
|
callback(
|
||||||
|
width - widthLeft,
|
||||||
|
lineHeight + ppadding.top() + ppadding.bottom());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -852,14 +1055,42 @@ not_null<ExtendedData*> String::ensureExtended() {
|
||||||
return _extended.get();
|
return _extended.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16 String::countBlockEnd(const TextBlocks::const_iterator &i, const TextBlocks::const_iterator &e) const {
|
uint16 String::countBlockEnd(
|
||||||
|
const TextBlocks::const_iterator &i,
|
||||||
|
const TextBlocks::const_iterator &e) const {
|
||||||
return (i + 1 == e) ? _text.size() : (*(i + 1))->position();
|
return (i + 1 == e) ? _text.size() : (*(i + 1))->position();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16 String::countBlockLength(const String::TextBlocks::const_iterator &i, const String::TextBlocks::const_iterator &e) const {
|
uint16 String::countBlockLength(
|
||||||
|
const TextBlocks::const_iterator &i,
|
||||||
|
const TextBlocks::const_iterator &e) const {
|
||||||
return countBlockEnd(i, e) - (*i)->position();
|
return countBlockEnd(i, e) - (*i)->position();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ParagraphDetails *String::paragraphByIndex(int index) const {
|
||||||
|
Expects(!index
|
||||||
|
|| (_extended && index <= _extended->paragraphs.size()));
|
||||||
|
|
||||||
|
return index ? &_extended->paragraphs[index - 1] : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
int String::paragraphIndex(const AbstractBlock *block) const {
|
||||||
|
Expects(!block || block->type() == TextBlockType::Newline);
|
||||||
|
|
||||||
|
return block
|
||||||
|
? static_cast<const NewlineBlock*>(block)->paragraphIndex()
|
||||||
|
: _startParagraphIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMargins String::paragraphPadding(ParagraphDetails *info) const {
|
||||||
|
if (!info) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
const auto skip = _st->blockVerticalSkip;
|
||||||
|
const auto top = info->pre ? _st->blockHeader : 0;
|
||||||
|
return _st->blockPadding + QMargins(0, top + skip, 0, skip);
|
||||||
|
}
|
||||||
|
|
||||||
template <
|
template <
|
||||||
typename AppendPartCallback,
|
typename AppendPartCallback,
|
||||||
typename ClickHandlerStartCallback,
|
typename ClickHandlerStartCallback,
|
||||||
|
|
@ -994,8 +1225,8 @@ bool String::hasNotEmojiAndSpaces() const {
|
||||||
return _hasNotEmojiAndSpaces;
|
return _hasNotEmojiAndSpaces;
|
||||||
}
|
}
|
||||||
|
|
||||||
const base::flat_map<int, int> &String::modifications() const {
|
const base::flat_map<int, Deltas> &String::modifications() const {
|
||||||
static const auto kEmpty = base::flat_map<int, int>();
|
static const auto kEmpty = base::flat_map<int, Deltas>();
|
||||||
return _extended ? _extended->modifications : kEmpty;
|
return _extended ? _extended->modifications : kEmpty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -76,12 +76,18 @@ static constexpr TextSelection AllTextSelection = { 0, 0xFFFF };
|
||||||
namespace Ui::Text {
|
namespace Ui::Text {
|
||||||
|
|
||||||
struct Block;
|
struct Block;
|
||||||
|
class AbstractBlock;
|
||||||
struct IsolatedEmoji;
|
struct IsolatedEmoji;
|
||||||
struct OnlyCustomEmoji;
|
struct OnlyCustomEmoji;
|
||||||
struct SpoilerData;
|
struct SpoilerData;
|
||||||
struct ParagraphDetails;
|
struct ParagraphDetails;
|
||||||
struct ExtendedData;
|
struct ExtendedData;
|
||||||
|
|
||||||
|
struct Deltas {
|
||||||
|
uint16 added = 0;
|
||||||
|
uint16 removed = 0;
|
||||||
|
};
|
||||||
|
|
||||||
struct StateRequest {
|
struct StateRequest {
|
||||||
enum class Flag {
|
enum class Flag {
|
||||||
BreakEverywhere = (1 << 0),
|
BreakEverywhere = (1 << 0),
|
||||||
|
|
@ -157,6 +163,38 @@ struct GeometryDescriptor {
|
||||||
bool elisionOneLine,
|
bool elisionOneLine,
|
||||||
bool elisionBreakEverywhere);
|
bool elisionBreakEverywhere);
|
||||||
|
|
||||||
|
struct BlockPaintCache {
|
||||||
|
QImage corners;
|
||||||
|
QColor headerCached;
|
||||||
|
QColor bgCached;
|
||||||
|
QColor outlineCached;
|
||||||
|
QColor iconCached;
|
||||||
|
|
||||||
|
QColor header;
|
||||||
|
QColor bg;
|
||||||
|
QColor outline;
|
||||||
|
QColor icon;
|
||||||
|
|
||||||
|
const style::icon *topright = nullptr;
|
||||||
|
QPoint toprightPosition;
|
||||||
|
bool withHeader = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ValidateBlockPaintCache(
|
||||||
|
BlockPaintCache &cache,
|
||||||
|
const style::TextStyle &st);
|
||||||
|
|
||||||
|
struct SkipBlockPaintParts {
|
||||||
|
bool skipTop : 1 = false;
|
||||||
|
bool skipBottom : 1 = false;
|
||||||
|
};
|
||||||
|
void FillBlockPaint(
|
||||||
|
QPainter &p,
|
||||||
|
QRect rect,
|
||||||
|
const BlockPaintCache &cache,
|
||||||
|
const style::TextStyle &st,
|
||||||
|
SkipBlockPaintParts parts = {});
|
||||||
|
|
||||||
struct PaintContext {
|
struct PaintContext {
|
||||||
QPoint position;
|
QPoint position;
|
||||||
int outerWidth = 0; // For automatic RTL Ui inversion.
|
int outerWidth = 0; // For automatic RTL Ui inversion.
|
||||||
|
|
@ -166,6 +204,8 @@ struct PaintContext {
|
||||||
QRect clip;
|
QRect clip;
|
||||||
|
|
||||||
const style::TextPalette *palette = nullptr;
|
const style::TextPalette *palette = nullptr;
|
||||||
|
BlockPaintCache *pre = nullptr;
|
||||||
|
BlockPaintCache *blockquote = nullptr;
|
||||||
std::span<SpecialColor> colors;
|
std::span<SpecialColor> colors;
|
||||||
SpoilerMessCache *spoiler = nullptr;
|
SpoilerMessCache *spoiler = nullptr;
|
||||||
crl::time now = 0;
|
crl::time now = 0;
|
||||||
|
|
@ -301,7 +341,7 @@ public:
|
||||||
[[nodiscard]] OnlyCustomEmoji toOnlyCustomEmoji() const;
|
[[nodiscard]] OnlyCustomEmoji toOnlyCustomEmoji() const;
|
||||||
|
|
||||||
[[nodiscard]] bool hasNotEmojiAndSpaces() const;
|
[[nodiscard]] bool hasNotEmojiAndSpaces() const;
|
||||||
[[nodiscard]] const base::flat_map<int, int> &modifications() const;
|
[[nodiscard]] const base::flat_map<int, Deltas> &modifications() const;
|
||||||
|
|
||||||
[[nodiscard]] const style::TextStyle *style() const {
|
[[nodiscard]] const style::TextStyle *style() const {
|
||||||
return _st;
|
return _st;
|
||||||
|
|
@ -337,6 +377,11 @@ private:
|
||||||
[[nodiscard]] uint16 countBlockLength(
|
[[nodiscard]] uint16 countBlockLength(
|
||||||
const TextBlocks::const_iterator &i,
|
const TextBlocks::const_iterator &i,
|
||||||
const TextBlocks::const_iterator &e) const;
|
const TextBlocks::const_iterator &e) const;
|
||||||
|
[[nodiscard]] ParagraphDetails *paragraphByIndex(int index) const;
|
||||||
|
[[nodiscard]] QMargins paragraphPadding(ParagraphDetails *info) const;
|
||||||
|
|
||||||
|
// block must be either nullptr or a pointer to a NewlineBlock.
|
||||||
|
[[nodiscard]] int paragraphIndex(const AbstractBlock *block) const;
|
||||||
|
|
||||||
// Template method for originalText(), originalTextWithEntities().
|
// Template method for originalText(), originalTextWithEntities().
|
||||||
template <
|
template <
|
||||||
|
|
@ -377,13 +422,15 @@ private:
|
||||||
int _minResizeWidth = 0;
|
int _minResizeWidth = 0;
|
||||||
int _maxWidth = 0;
|
int _maxWidth = 0;
|
||||||
int _minHeight = 0;
|
int _minHeight = 0;
|
||||||
int16 _startParagraphIndex = 0;
|
uint16 _startParagraphIndex = 0;
|
||||||
bool _startParagraphLTR : 1 = false;
|
bool _startParagraphLTR : 1 = false;
|
||||||
bool _startParagraphRTL : 1 = false;
|
bool _startParagraphRTL : 1 = false;
|
||||||
bool _hasCustomEmoji : 1 = false;
|
bool _hasCustomEmoji : 1 = false;
|
||||||
bool _isIsolatedEmoji : 1 = false;
|
bool _isIsolatedEmoji : 1 = false;
|
||||||
bool _isOnlyCustomEmoji : 1 = false;
|
bool _isOnlyCustomEmoji : 1 = false;
|
||||||
bool _hasNotEmojiAndSpaces : 1 = false;
|
bool _hasNotEmojiAndSpaces : 1 = false;
|
||||||
|
bool _skipBlockAddedNewline : 1 = false;
|
||||||
|
bool _endsWithParagraphDetails : 1 = false;
|
||||||
|
|
||||||
friend class Parser;
|
friend class Parser;
|
||||||
friend class Renderer;
|
friend class Renderer;
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,7 @@ public:
|
||||||
uint16 linkIndex,
|
uint16 linkIndex,
|
||||||
uint16 colorIndex);
|
uint16 colorIndex);
|
||||||
|
|
||||||
[[nodiscard]] int16 paragraphIndex() const {
|
[[nodiscard]] uint16 paragraphIndex() const {
|
||||||
return _paragraphIndex;
|
return _paragraphIndex;
|
||||||
}
|
}
|
||||||
[[nodiscard]] Qt::LayoutDirection paragraphDirection() const {
|
[[nodiscard]] Qt::LayoutDirection paragraphDirection() const {
|
||||||
|
|
@ -115,7 +115,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int16 _paragraphIndex = 0;
|
uint16 _paragraphIndex = 0;
|
||||||
bool _paragraphLTR : 1 = false;
|
bool _paragraphLTR : 1 = false;
|
||||||
bool _paragraphRTL : 1 = false;
|
bool _paragraphRTL : 1 = false;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@
|
||||||
|
|
||||||
namespace Ui::Text {
|
namespace Ui::Text {
|
||||||
|
|
||||||
|
struct Deltas;
|
||||||
|
|
||||||
class String;
|
class String;
|
||||||
|
|
||||||
class SpoilerClickHandler final : public ClickHandler {
|
class SpoilerClickHandler final : public ClickHandler {
|
||||||
|
|
@ -57,7 +59,7 @@ struct ExtendedData {
|
||||||
std::vector<ClickHandlerPtr> links;
|
std::vector<ClickHandlerPtr> links;
|
||||||
std::vector<ParagraphDetails> paragraphs;
|
std::vector<ParagraphDetails> paragraphs;
|
||||||
std::unique_ptr<SpoilerData> spoiler;
|
std::unique_ptr<SpoilerData> spoiler;
|
||||||
base::flat_map<int, int> modifications;
|
base::flat_map<int, Deltas> modifications;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Ui::Text
|
} // namespace Ui::Text
|
||||||
|
|
|
||||||
|
|
@ -211,6 +211,8 @@ void Parser::createBlock(int32 skipBack) {
|
||||||
push(&Block::Emoji, _emoji);
|
push(&Block::Emoji, _emoji);
|
||||||
} else if (newline) {
|
} else if (newline) {
|
||||||
push(&Block::Newline);
|
push(&Block::Newline);
|
||||||
|
auto &newline = _t->_blocks.back().unsafe<NewlineBlock>();
|
||||||
|
newline._paragraphIndex = _paragraphIndex;
|
||||||
} else {
|
} else {
|
||||||
push(&Block::Text, _t->_minResizeWidth);
|
push(&Block::Text, _t->_minResizeWidth);
|
||||||
}
|
}
|
||||||
|
|
@ -231,7 +233,7 @@ void Parser::createNewlineBlock(bool fromOriginalText) {
|
||||||
createBlock();
|
createBlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Parser::ensureAtNewline() {
|
void Parser::ensureAtNewline(ParagraphDetails details) {
|
||||||
createBlock();
|
createBlock();
|
||||||
const auto lastType = _t->_blocks.empty()
|
const auto lastType = _t->_blocks.empty()
|
||||||
? TextBlockType::Newline
|
? TextBlockType::Newline
|
||||||
|
|
@ -241,6 +243,16 @@ void Parser::ensureAtNewline() {
|
||||||
createNewlineBlock(false);
|
createNewlineBlock(false);
|
||||||
_customEmojiData = base::take(saved);
|
_customEmojiData = base::take(saved);
|
||||||
}
|
}
|
||||||
|
auto ¶graphs = _t->ensureExtended()->paragraphs;
|
||||||
|
paragraphs.push_back(std::move(details));
|
||||||
|
const auto index = _paragraphIndex = int(paragraphs.size());
|
||||||
|
if (_t->_blocks.empty()) {
|
||||||
|
_t->_startParagraphIndex = index;
|
||||||
|
} else {
|
||||||
|
auto &last = _t->_blocks.back();
|
||||||
|
Assert(last->type() == TextBlockType::Newline);
|
||||||
|
last.unsafe<NewlineBlock>()._paragraphIndex = index;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Parser::finishEntities() {
|
void Parser::finishEntities() {
|
||||||
|
|
@ -259,11 +271,18 @@ void Parser::finishEntities() {
|
||||||
const auto lastType = _t->_blocks.empty()
|
const auto lastType = _t->_blocks.empty()
|
||||||
? TextBlockType::Newline
|
? TextBlockType::Newline
|
||||||
: _t->_blocks.back()->type();
|
: _t->_blocks.back()->type();
|
||||||
if ((lastType != TextBlockType::Newline)
|
if ((*flags)
|
||||||
&& ((*flags)
|
|
||||||
& (TextBlockFlag::Pre
|
& (TextBlockFlag::Pre
|
||||||
| TextBlockFlag::Blockquote))) {
|
| TextBlockFlag::Blockquote)) {
|
||||||
|
_paragraphIndex = 0;
|
||||||
|
if (lastType != TextBlockType::Newline) {
|
||||||
_newlineAwaited = true;
|
_newlineAwaited = true;
|
||||||
|
} else if (_t->_blocks.empty()) {
|
||||||
|
_t->_startParagraphIndex = 0;
|
||||||
|
} else {
|
||||||
|
auto &last = _t->_blocks.back();
|
||||||
|
last.unsafe<NewlineBlock>()._paragraphIndex = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (IsMono(*flags)) {
|
if (IsMono(*flags)) {
|
||||||
_monoIndex = 0;
|
_monoIndex = 0;
|
||||||
|
|
@ -340,7 +359,10 @@ bool Parser::checkEntities() {
|
||||||
flags = TextBlockFlag::Code;
|
flags = TextBlockFlag::Code;
|
||||||
} else {
|
} else {
|
||||||
flags = TextBlockFlag::Pre;
|
flags = TextBlockFlag::Pre;
|
||||||
ensureAtNewline();
|
ensureAtNewline({
|
||||||
|
.language = _waitingEntity->data(),
|
||||||
|
.pre = true,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
const auto text = QString(entityBegin, entityLength);
|
const auto text = QString(entityBegin, entityLength);
|
||||||
|
|
||||||
|
|
@ -356,7 +378,7 @@ bool Parser::checkEntities() {
|
||||||
}
|
}
|
||||||
} else if (entityType == EntityType::Blockquote) {
|
} else if (entityType == EntityType::Blockquote) {
|
||||||
flags = TextBlockFlag::Blockquote;
|
flags = TextBlockFlag::Blockquote;
|
||||||
ensureAtNewline();
|
ensureAtNewline({ .blockquote = true });
|
||||||
} else if (entityType == EntityType::Url
|
} else if (entityType == EntityType::Url
|
||||||
|| entityType == EntityType::Email
|
|| entityType == EntityType::Email
|
||||||
|| entityType == EntityType::Mention
|
|| entityType == EntityType::Mention
|
||||||
|
|
@ -581,7 +603,12 @@ bool Parser::isLinkEntity(const EntityInText &entity) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Parser::updateModifications(int index, int delta) {
|
void Parser::updateModifications(int index, int delta) {
|
||||||
_t->ensureExtended()->modifications[index] += delta;
|
auto &deltas = _t->ensureExtended()->modifications[index];
|
||||||
|
if (delta > 0) {
|
||||||
|
deltas.added += delta;
|
||||||
|
} else {
|
||||||
|
deltas.removed -= delta;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Parser::parse(const TextParseOptions &options) {
|
void Parser::parse(const TextParseOptions &options) {
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@
|
||||||
|
|
||||||
namespace Ui::Text {
|
namespace Ui::Text {
|
||||||
|
|
||||||
|
struct ParagraphDetails;
|
||||||
|
|
||||||
class Parser {
|
class Parser {
|
||||||
public:
|
public:
|
||||||
Parser(
|
Parser(
|
||||||
|
|
@ -58,7 +60,7 @@ private:
|
||||||
void blockCreated();
|
void blockCreated();
|
||||||
void createBlock(int32 skipBack = 0);
|
void createBlock(int32 skipBack = 0);
|
||||||
void createNewlineBlock(bool fromOriginalText);
|
void createNewlineBlock(bool fromOriginalText);
|
||||||
void ensureAtNewline();
|
void ensureAtNewline(ParagraphDetails details);
|
||||||
|
|
||||||
// Returns true if at least one entity was parsed in the current position.
|
// Returns true if at least one entity was parsed in the current position.
|
||||||
bool checkEntities();
|
bool checkEntities();
|
||||||
|
|
@ -113,6 +115,7 @@ private:
|
||||||
uint16 _linkIndex = 0;
|
uint16 _linkIndex = 0;
|
||||||
uint16 _colorIndex = 0;
|
uint16 _colorIndex = 0;
|
||||||
uint16 _monoIndex = 0;
|
uint16 _monoIndex = 0;
|
||||||
|
uint16 _paragraphIndex = 0;
|
||||||
EmojiPtr _emoji = nullptr; // current emoji, if current word is an emoji, or zero
|
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 _blockStart = 0; // offset in result, from which current parsed block is started
|
||||||
int32 _diacritics = 0; // diacritic chars skipped without good char
|
int32 _diacritics = 0; // diacritic chars skipped without good char
|
||||||
|
|
|
||||||
|
|
@ -206,6 +206,8 @@ void Renderer::draw(QPainter &p, const PaintContext &context) {
|
||||||
? (1. - _spoiler->revealAnimation.value(
|
? (1. - _spoiler->revealAnimation.value(
|
||||||
_spoiler->revealed ? 1. : 0.))
|
_spoiler->revealed ? 1. : 0.))
|
||||||
: 0.;
|
: 0.;
|
||||||
|
_preBlockCache = context.pre;
|
||||||
|
_blockquoteBlockCache = context.blockquote;
|
||||||
enumerate();
|
enumerate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -231,6 +233,7 @@ void Renderer::enumerate() {
|
||||||
if ((*_t->_blocks.cbegin())->type() != TextBlockType::Newline) {
|
if ((*_t->_blocks.cbegin())->type() != TextBlockType::Newline) {
|
||||||
initNextParagraph(
|
initNextParagraph(
|
||||||
_t->_blocks.cbegin(),
|
_t->_blocks.cbegin(),
|
||||||
|
_t->_startParagraphIndex,
|
||||||
UnpackParagraphDirection(
|
UnpackParagraphDirection(
|
||||||
_t->_startParagraphLTR,
|
_t->_startParagraphLTR,
|
||||||
_t->_startParagraphRTL));
|
_t->_startParagraphRTL));
|
||||||
|
|
@ -259,6 +262,9 @@ void Renderer::enumerate() {
|
||||||
if (!_lineHeight) {
|
if (!_lineHeight) {
|
||||||
_lineHeight = blockHeight;
|
_lineHeight = blockHeight;
|
||||||
}
|
}
|
||||||
|
const auto pindex = static_cast<const NewlineBlock*>(b)->paragraphIndex();
|
||||||
|
const auto changed = (_pindex != pindex);
|
||||||
|
fillParagraphBg(changed ? _ppadding.bottom() : 0);
|
||||||
if (!drawLine((*i)->position(), i, e)) {
|
if (!drawLine((*i)->position(), i, e)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -271,6 +277,7 @@ void Renderer::enumerate() {
|
||||||
|
|
||||||
initNextParagraph(
|
initNextParagraph(
|
||||||
i + 1,
|
i + 1,
|
||||||
|
pindex,
|
||||||
static_cast<const NewlineBlock*>(b)->paragraphDirection());
|
static_cast<const NewlineBlock*>(b)->paragraphDirection());
|
||||||
|
|
||||||
longWordLine = true;
|
longWordLine = true;
|
||||||
|
|
@ -340,6 +347,7 @@ void Renderer::enumerate() {
|
||||||
: (j + 1 != en)
|
: (j + 1 != en)
|
||||||
? (j + 1)->position()
|
? (j + 1)->position()
|
||||||
: _t->countBlockEnd(i, e);
|
: _t->countBlockEnd(i, e);
|
||||||
|
fillParagraphBg(0);
|
||||||
if (!drawLine(lineEnd, i, e)) {
|
if (!drawLine(lineEnd, i, e)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -368,6 +376,7 @@ void Renderer::enumerate() {
|
||||||
const auto lineEnd = !_elidedLine
|
const auto lineEnd = !_elidedLine
|
||||||
? b->position()
|
? b->position()
|
||||||
: _t->countBlockEnd(i, e);
|
: _t->countBlockEnd(i, e);
|
||||||
|
fillParagraphBg(0);
|
||||||
if (!drawLine(lineEnd, i, e)) {
|
if (!drawLine(lineEnd, i, e)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -386,6 +395,7 @@ void Renderer::enumerate() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (_lineStart < _t->_text.size()) {
|
if (_lineStart < _t->_text.size()) {
|
||||||
|
fillParagraphBg(_ppadding.bottom());
|
||||||
if (!drawLine(_t->_text.size(), e, e)) {
|
if (!drawLine(_t->_text.size(), e, e)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -396,6 +406,37 @@ void Renderer::enumerate() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Renderer::fillParagraphBg(int paddingBottom) {
|
||||||
|
const auto cache = (!_p || !_paragraph)
|
||||||
|
? nullptr
|
||||||
|
: _paragraph->pre
|
||||||
|
? _preBlockCache
|
||||||
|
: _paragraph->blockquote
|
||||||
|
? _blockquoteBlockCache
|
||||||
|
: nullptr;
|
||||||
|
if (cache) {
|
||||||
|
auto &valid = _paragraph->pre
|
||||||
|
? _preBlockCacheValid
|
||||||
|
: _blockquoteBlockCacheValid;
|
||||||
|
if (!valid) {
|
||||||
|
valid = true;
|
||||||
|
ValidateBlockPaintCache(*cache, *_t->_st);
|
||||||
|
}
|
||||||
|
const auto skip = _t->_st->blockVerticalSkip;
|
||||||
|
const auto isTop = (_y != _blockLineTop);
|
||||||
|
const auto isBottom = (paddingBottom != 0);
|
||||||
|
const auto top = _blockLineTop + (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, *_t->_st, {
|
||||||
|
.skipTop = !isTop,
|
||||||
|
.skipBottom = !isBottom,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_blockLineTop = _y + _lineHeight + paddingBottom;
|
||||||
|
}
|
||||||
|
|
||||||
StateResult Renderer::getState(
|
StateResult Renderer::getState(
|
||||||
QPoint point,
|
QPoint point,
|
||||||
GeometryDescriptor geometry,
|
GeometryDescriptor geometry,
|
||||||
|
|
@ -430,12 +471,22 @@ crl::time Renderer::now() const {
|
||||||
|
|
||||||
void Renderer::initNextParagraph(
|
void Renderer::initNextParagraph(
|
||||||
String::TextBlocks::const_iterator i,
|
String::TextBlocks::const_iterator i,
|
||||||
|
int16 paragraphIndex,
|
||||||
Qt::LayoutDirection direction) {
|
Qt::LayoutDirection direction) {
|
||||||
_parDirection = (direction == Qt::LayoutDirectionAuto)
|
_parDirection = (direction == Qt::LayoutDirectionAuto)
|
||||||
? style::LayoutDirection()
|
? style::LayoutDirection()
|
||||||
: direction;
|
: direction;
|
||||||
_parStartBlock = i;
|
_parStartBlock = i;
|
||||||
_paragraphWidthRemaining = 0;
|
_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);
|
||||||
|
}
|
||||||
const auto e = _t->_blocks.cend();
|
const auto e = _t->_blocks.cend();
|
||||||
if (i == e) {
|
if (i == e) {
|
||||||
_lineStart = _parStart = _t->_text.size();
|
_lineStart = _parStart = _t->_text.size();
|
||||||
|
|
@ -461,6 +512,7 @@ void Renderer::initNextParagraph(
|
||||||
_parLength = ((i == e) ? _t->_text.size() : (*i)->position()) - _parStart;
|
_parLength = ((i == e) ? _t->_text.size() : (*i)->position()) - _parStart;
|
||||||
}
|
}
|
||||||
_parAnalysis.resize(0);
|
_parAnalysis.resize(0);
|
||||||
|
_paragraphWidthRemaining += _ppadding.left() + _ppadding.right();
|
||||||
initNextLine();
|
initNextLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -470,9 +522,12 @@ void Renderer::initNextLine() {
|
||||||
.top = (_y - _startTop),
|
.top = (_y - _startTop),
|
||||||
.width = _paragraphWidthRemaining.ceil().toInt(),
|
.width = _paragraphWidthRemaining.ceil().toInt(),
|
||||||
});
|
});
|
||||||
_x = _startLeft + line.left;
|
_blockLineTop += _startTop + line.top - _y;
|
||||||
|
_x = _startLeft + line.left + _ppadding.left();
|
||||||
_y = _startTop + line.top;
|
_y = _startTop + line.top;
|
||||||
_lineWidth = _wLeft = line.width;
|
_startLineWidth = line.width;
|
||||||
|
_lineWidth = _startLineWidth - _ppadding.left() - _ppadding.right();
|
||||||
|
_wLeft = _lineWidth;
|
||||||
_elidedLine = line.elided;
|
_elidedLine = line.elided;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1252,7 +1307,7 @@ void Renderer::prepareElidedLine(QString &lineText, int32 lineStart, int32 &line
|
||||||
eShapeLine(line);
|
eShapeLine(line);
|
||||||
|
|
||||||
auto elideWidth = _f->elidew;
|
auto elideWidth = _f->elidew;
|
||||||
_wLeft = _lineWidth - elideWidth;
|
_wLeft = _lineWidth - _ppadding.left() - _ppadding.right() - elideWidth;
|
||||||
|
|
||||||
int firstItem = engine.findItem(line.from), lastItem = engine.findItem(line.from + line.length - 1);
|
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;
|
int nItems = (firstItem >= 0 && lastItem >= firstItem) ? (lastItem - firstItem + 1) : 0, i;
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ private:
|
||||||
[[nodiscard]] crl::time now() const;
|
[[nodiscard]] crl::time now() const;
|
||||||
void initNextParagraph(
|
void initNextParagraph(
|
||||||
String::TextBlocks::const_iterator i,
|
String::TextBlocks::const_iterator i,
|
||||||
|
int16 paragraphIndex,
|
||||||
Qt::LayoutDirection direction);
|
Qt::LayoutDirection direction);
|
||||||
void initNextLine();
|
void initNextLine();
|
||||||
void initParagraphBidi();
|
void initParagraphBidi();
|
||||||
|
|
@ -89,6 +90,8 @@ private:
|
||||||
int repeat = 0);
|
int repeat = 0);
|
||||||
void restoreAfterElided();
|
void restoreAfterElided();
|
||||||
|
|
||||||
|
void fillParagraphBg(int paddingBottom);
|
||||||
|
|
||||||
// COPIED FROM qtextengine.cpp AND MODIFIED
|
// COPIED FROM qtextengine.cpp AND MODIFIED
|
||||||
static void eAppendItems(
|
static void eAppendItems(
|
||||||
QScriptAnalysis *analysis,
|
QScriptAnalysis *analysis,
|
||||||
|
|
@ -154,12 +157,21 @@ private:
|
||||||
int _parLength = 0;
|
int _parLength = 0;
|
||||||
bool _parHasBidi = false;
|
bool _parHasBidi = false;
|
||||||
QVarLengthArray<QScriptAnalysis, 4096> _parAnalysis;
|
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;
|
||||||
|
|
||||||
// current line data
|
// current line data
|
||||||
QTextEngine *_e = nullptr;
|
QTextEngine *_e = nullptr;
|
||||||
style::font _f;
|
style::font _f;
|
||||||
int _startLeft = 0;
|
int _startLeft = 0;
|
||||||
int _startTop = 0;
|
int _startTop = 0;
|
||||||
|
int _startLineWidth = 0;
|
||||||
QFixed _x, _wLeft, _last_rPadding;
|
QFixed _x, _wLeft, _last_rPadding;
|
||||||
int _y = 0;
|
int _y = 0;
|
||||||
int _yDelta = 0;
|
int _yDelta = 0;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue