Optimize Ui::Text::String blocks vector.
This commit is contained in:
parent
30d223befe
commit
f7b4994887
4 changed files with 360 additions and 167 deletions
114
ui/text/text.cpp
114
ui/text/text.cpp
|
|
@ -461,13 +461,13 @@ void Parser::createBlock(int32 skipBack) {
|
|||
}
|
||||
_lastSkipped = false;
|
||||
if (_emoji) {
|
||||
_t->_blocks.push_back(std::make_unique<EmojiBlock>(_t->_st->font, _t->_text, _blockStart, len, _flags, _lnkIndex, _emoji));
|
||||
_t->_blocks.push_back(Block::Emoji(_t->_st->font, _t->_text, _blockStart, len, _flags, _lnkIndex, _emoji));
|
||||
_emoji = nullptr;
|
||||
_lastSkipped = true;
|
||||
} else if (newline) {
|
||||
_t->_blocks.push_back(std::make_unique<NewlineBlock>(_t->_st->font, _t->_text, _blockStart, len, _flags, _lnkIndex));
|
||||
_t->_blocks.push_back(Block::Newline(_t->_st->font, _t->_text, _blockStart, len, _flags, _lnkIndex));
|
||||
} else {
|
||||
_t->_blocks.push_back(std::make_unique<TextBlock>(_t->_st->font, _t->_text, _t->_minResizeWidth, _blockStart, len, _flags, _lnkIndex));
|
||||
_t->_blocks.push_back(Block::Text(_t->_st->font, _t->_text, _t->_minResizeWidth, _blockStart, len, _flags, _lnkIndex));
|
||||
}
|
||||
_blockStart += len;
|
||||
blockCreated();
|
||||
|
|
@ -477,7 +477,7 @@ void Parser::createBlock(int32 skipBack) {
|
|||
void Parser::createSkipBlock(int32 w, int32 h) {
|
||||
createBlock();
|
||||
_t->_text.push_back('_');
|
||||
_t->_blocks.push_back(std::make_unique<SkipBlock>(_t->_st->font, _t->_text, _blockStart++, w, h, _lnkIndex));
|
||||
_t->_blocks.push_back(Block::Skip(_t->_st->font, _t->_text, _blockStart++, w, h, _lnkIndex));
|
||||
blockCreated();
|
||||
}
|
||||
|
||||
|
|
@ -913,15 +913,14 @@ void Parser::checkForElidedSkipBlock() {
|
|||
|
||||
void Parser::finalize(const TextParseOptions &options) {
|
||||
_t->_links.resize(_maxLnkIndex);
|
||||
for (const auto &block : _t->_blocks) {
|
||||
const auto b = block.get();
|
||||
const auto shiftedIndex = b->lnkIndex();
|
||||
for (auto &block : _t->_blocks) {
|
||||
const auto shiftedIndex = block->lnkIndex();
|
||||
if (shiftedIndex <= kStringLinkIndexShift) {
|
||||
continue;
|
||||
}
|
||||
const auto realIndex = (shiftedIndex - kStringLinkIndexShift);
|
||||
const auto index = _maxLnkIndex + realIndex;
|
||||
b->setLnkIndex(index);
|
||||
block->setLnkIndex(index);
|
||||
if (_t->_links.size() >= index) {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -1165,7 +1164,7 @@ public:
|
|||
_wLeft -= _elideRemoveFromEnd;
|
||||
}
|
||||
|
||||
_parDirection = static_cast<NewlineBlock*>(b)->nextDirection();
|
||||
_parDirection = static_cast<const NewlineBlock*>(b)->nextDirection();
|
||||
if (_parDirection == Qt::LayoutDirectionAuto) _parDirection = style::LayoutDirection();
|
||||
initNextParagraph(i + 1);
|
||||
|
||||
|
|
@ -1187,7 +1186,7 @@ public:
|
|||
}
|
||||
|
||||
if (_btype == TextBlockTText) {
|
||||
auto t = static_cast<TextBlock*>(b);
|
||||
auto t = static_cast<const TextBlock*>(b);
|
||||
if (t->_words.isEmpty()) { // no words in this block, spaces only => layout this block in the same line
|
||||
_last_rPadding += b->f_rpadding();
|
||||
|
||||
|
|
@ -1678,7 +1677,7 @@ private:
|
|||
}
|
||||
Emoji::Draw(
|
||||
*_p,
|
||||
static_cast<EmojiBlock*>(currentBlock)->emoji,
|
||||
static_cast<const EmojiBlock*>(currentBlock)->_emoji,
|
||||
Emoji::GetSizeNormal(),
|
||||
(glyphX + st::emojiPadding).toInt(),
|
||||
_y + _yDelta + emojiY);
|
||||
|
|
@ -1847,7 +1846,7 @@ private:
|
|||
_p->fillRect(left, _y + _yDelta, width, _fontHeight, _textPalette->selectBg);
|
||||
}
|
||||
|
||||
void elideSaveBlock(int32 blockIndex, AbstractBlock *&_endBlock, int32 elideStart, int32 elideWidth) {
|
||||
void elideSaveBlock(int32 blockIndex, const AbstractBlock *&_endBlock, int32 elideStart, int32 elideWidth) {
|
||||
if (_elideSavedBlock) {
|
||||
restoreAfterElided();
|
||||
}
|
||||
|
|
@ -1855,7 +1854,7 @@ private:
|
|||
_elideSavedIndex = blockIndex;
|
||||
auto mutableText = const_cast<String*>(_t);
|
||||
_elideSavedBlock = std::move(mutableText->_blocks[blockIndex]);
|
||||
mutableText->_blocks[blockIndex] = std::make_unique<TextBlock>(_t->_st->font, _t->_text, QFIXED_MAX, elideStart, 0, _elideSavedBlock->flags(), _elideSavedBlock->lnkIndex());
|
||||
mutableText->_blocks[blockIndex] = Block::Text(_t->_st->font, _t->_text, QFIXED_MAX, elideStart, 0, (*_elideSavedBlock)->flags(), (*_elideSavedBlock)->lnkIndex());
|
||||
_blocksSize = blockIndex + 1;
|
||||
_endBlock = (blockIndex + 1 < _t->_blocks.size() ? _t->_blocks[blockIndex + 1].get() : nullptr);
|
||||
}
|
||||
|
|
@ -1870,7 +1869,7 @@ private:
|
|||
}
|
||||
}
|
||||
|
||||
void prepareElidedLine(QString &lineText, int32 lineStart, int32 &lineLength, AbstractBlock *&_endBlock, int repeat = 0) {
|
||||
void prepareElidedLine(QString &lineText, int32 lineStart, int32 &lineLength, const AbstractBlock *&_endBlock, int repeat = 0) {
|
||||
static const auto _Elide = QString::fromLatin1("...");
|
||||
|
||||
_f = _t->_st->font;
|
||||
|
|
@ -1980,7 +1979,7 @@ private:
|
|||
|
||||
void restoreAfterElided() {
|
||||
if (_elideSavedBlock) {
|
||||
const_cast<String*>(_t)->_blocks[_elideSavedIndex] = std::move(_elideSavedBlock);
|
||||
const_cast<String*>(_t)->_blocks[_elideSavedIndex] = std::move(*_elideSavedBlock);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2034,7 +2033,7 @@ private:
|
|||
return result;
|
||||
}
|
||||
|
||||
void eSetFont(AbstractBlock *block) {
|
||||
void eSetFont(const AbstractBlock *block) {
|
||||
const auto flags = block->flags();
|
||||
const auto usedFont = [&] {
|
||||
if (const auto index = block->lnkIndex()) {
|
||||
|
|
@ -2609,7 +2608,7 @@ private:
|
|||
}
|
||||
|
||||
private:
|
||||
void applyBlockProperties(AbstractBlock *block) {
|
||||
void applyBlockProperties(const AbstractBlock *block) {
|
||||
eSetFont(block);
|
||||
if (_p) {
|
||||
if (block->lnkIndex()) {
|
||||
|
|
@ -2660,7 +2659,7 @@ private:
|
|||
// elided hack support
|
||||
int _blocksSize = 0;
|
||||
int _elideSavedIndex = 0;
|
||||
std::unique_ptr<AbstractBlock> _elideSavedBlock;
|
||||
std::optional<Block> _elideSavedBlock;
|
||||
|
||||
int _lineStart = 0;
|
||||
int _localFrom = 0;
|
||||
|
|
@ -2679,7 +2678,8 @@ private:
|
|||
String::String(int32 minResizeWidth) : _minResizeWidth(minResizeWidth) {
|
||||
}
|
||||
|
||||
String::String(const style::TextStyle &st, const QString &text, const TextParseOptions &options, int32 minResizeWidth, bool richText) : _minResizeWidth(minResizeWidth) {
|
||||
String::String(const style::TextStyle &st, const QString &text, const TextParseOptions &options, int32 minResizeWidth, bool richText)
|
||||
: _minResizeWidth(minResizeWidth) {
|
||||
if (richText) {
|
||||
setRichText(st, text, options);
|
||||
} else {
|
||||
|
|
@ -2687,60 +2687,6 @@ String::String(const style::TextStyle &st, const QString &text, const TextParseO
|
|||
}
|
||||
}
|
||||
|
||||
String::String(const String &other)
|
||||
: _minResizeWidth(other._minResizeWidth)
|
||||
, _maxWidth(other._maxWidth)
|
||||
, _minHeight(other._minHeight)
|
||||
, _text(other._text)
|
||||
, _st(other._st)
|
||||
, _links(other._links)
|
||||
, _startDir(other._startDir) {
|
||||
_blocks.reserve(other._blocks.size());
|
||||
for (auto &block : other._blocks) {
|
||||
_blocks.push_back(block->clone());
|
||||
}
|
||||
}
|
||||
|
||||
String::String(String &&other)
|
||||
: _minResizeWidth(other._minResizeWidth)
|
||||
, _maxWidth(other._maxWidth)
|
||||
, _minHeight(other._minHeight)
|
||||
, _text(other._text)
|
||||
, _st(other._st)
|
||||
, _blocks(std::move(other._blocks))
|
||||
, _links(other._links)
|
||||
, _startDir(other._startDir) {
|
||||
other.clearFields();
|
||||
}
|
||||
|
||||
String &String::operator=(const String &other) {
|
||||
_minResizeWidth = other._minResizeWidth;
|
||||
_maxWidth = other._maxWidth;
|
||||
_minHeight = other._minHeight;
|
||||
_text = other._text;
|
||||
_st = other._st;
|
||||
_blocks = TextBlocks(other._blocks.size());
|
||||
_links = other._links;
|
||||
_startDir = other._startDir;
|
||||
for (int32 i = 0, l = _blocks.size(); i < l; ++i) {
|
||||
_blocks[i] = other._blocks.at(i)->clone();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
String &String::operator=(String &&other) {
|
||||
_minResizeWidth = other._minResizeWidth;
|
||||
_maxWidth = other._maxWidth;
|
||||
_minHeight = other._minHeight;
|
||||
_text = other._text;
|
||||
_st = other._st;
|
||||
_blocks = std::move(other._blocks);
|
||||
_links = other._links;
|
||||
_startDir = other._startDir;
|
||||
other.clearFields();
|
||||
return *this;
|
||||
}
|
||||
|
||||
void String::setText(const style::TextStyle &st, const QString &text, const TextParseOptions &options) {
|
||||
_st = &st;
|
||||
clear();
|
||||
|
|
@ -2757,8 +2703,8 @@ void String::recountNaturalSize(bool initial, Qt::LayoutDirection optionsDir) {
|
|||
int32 lineHeight = 0;
|
||||
int32 lastNewlineStart = 0;
|
||||
QFixed _width = 0, last_rBearing = 0, last_rPadding = 0;
|
||||
for (auto i = _blocks.cbegin(), e = _blocks.cend(); i != e; ++i) {
|
||||
auto b = i->get();
|
||||
for (auto &block : _blocks) {
|
||||
auto b = block.get();
|
||||
auto _btype = b->type();
|
||||
auto blockHeight = countBlockHeight(b, _st);
|
||||
if (_btype == TextBlockTNewline) {
|
||||
|
|
@ -2775,7 +2721,7 @@ void String::recountNaturalSize(bool initial, Qt::LayoutDirection optionsDir) {
|
|||
}
|
||||
}
|
||||
lastNewlineStart = b->from();
|
||||
lastNewline = static_cast<NewlineBlock*>(b);
|
||||
lastNewline = &block.unsafe<NewlineBlock>();
|
||||
|
||||
_minHeight += lineHeight;
|
||||
lineHeight = 0;
|
||||
|
|
@ -2824,19 +2770,19 @@ void String::recountNaturalSize(bool initial, Qt::LayoutDirection optionsDir) {
|
|||
}
|
||||
|
||||
int String::countMaxMonospaceWidth() const {
|
||||
NewlineBlock *lastNewline = 0;
|
||||
const NewlineBlock *lastNewline = nullptr;
|
||||
|
||||
auto result = QFixed();
|
||||
auto paragraphWidth = QFixed();
|
||||
auto lastNewlineStart = 0;
|
||||
auto fullMonospace = true;
|
||||
QFixed _width = 0, last_rBearing = 0, last_rPadding = 0;
|
||||
for (auto i = _blocks.cbegin(), e = _blocks.cend(); i != e; ++i) {
|
||||
auto b = i->get();
|
||||
for (auto &block : _blocks) {
|
||||
auto b = block.get();
|
||||
auto _btype = b->type();
|
||||
if (_btype == TextBlockTNewline) {
|
||||
lastNewlineStart = b->from();
|
||||
lastNewline = static_cast<NewlineBlock*>(b);
|
||||
lastNewline = &block.unsafe<NewlineBlock>();
|
||||
|
||||
last_rBearing = b->f_rbearing();
|
||||
last_rPadding = b->f_rpadding();
|
||||
|
|
@ -2943,7 +2889,7 @@ bool String::updateSkipBlock(int width, int height) {
|
|||
_blocks.pop_back();
|
||||
}
|
||||
_text.push_back('_');
|
||||
_blocks.push_back(std::make_unique<SkipBlock>(
|
||||
_blocks.push_back(Block::Skip(
|
||||
_st->font,
|
||||
_text,
|
||||
_text.size() - 1,
|
||||
|
|
@ -3036,7 +2982,7 @@ void String::enumerateLines(
|
|||
}
|
||||
|
||||
if (_btype == TextBlockTText) {
|
||||
auto t = static_cast<TextBlock*>(b.get());
|
||||
const auto t = &b.unsafe<TextBlock>();
|
||||
if (t->_words.isEmpty()) { // no words in this block, spaces only => layout this block in the same line
|
||||
last_rPadding += b->f_rpadding();
|
||||
|
||||
|
|
@ -3389,7 +3335,7 @@ IsolatedEmoji String::toIsolatedEmoji() const {
|
|||
if (block->lnkIndex()) {
|
||||
return IsolatedEmoji();
|
||||
} else if (type == TextBlockTEmoji) {
|
||||
result.items[index++] = static_cast<EmojiBlock*>(block.get())->emoji;
|
||||
result.items[index++] = block.unsafe<EmojiBlock>()._emoji;
|
||||
} else if (type != TextBlockTSkip) {
|
||||
return IsolatedEmoji();
|
||||
}
|
||||
|
|
@ -3409,7 +3355,5 @@ void String::clearFields() {
|
|||
_startDir = Qt::LayoutDirectionAuto;
|
||||
}
|
||||
|
||||
String::~String() = default;
|
||||
|
||||
} // namespace Text
|
||||
} // namespace Ui
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "ui/text/text_entity.h"
|
||||
#include "ui/text/text_block.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/click_handler.h"
|
||||
#include "base/flags.h"
|
||||
|
|
@ -69,7 +70,6 @@ static constexpr TextSelection AllTextSelection = { 0, 0xFFFF };
|
|||
namespace Ui {
|
||||
namespace Text {
|
||||
|
||||
class AbstractBlock;
|
||||
struct IsolatedEmoji;
|
||||
|
||||
struct StateRequest {
|
||||
|
|
@ -108,11 +108,17 @@ struct StateRequestElided : public StateRequest {
|
|||
class String {
|
||||
public:
|
||||
String(int32 minResizeWidth = QFIXED_MAX);
|
||||
String(const style::TextStyle &st, const QString &text, const TextParseOptions &options = _defaultOptions, int32 minResizeWidth = QFIXED_MAX, bool richText = false);
|
||||
String(const String &other);
|
||||
String(String &&other);
|
||||
String &operator=(const String &other);
|
||||
String &operator=(String &&other);
|
||||
String(
|
||||
const style::TextStyle &st,
|
||||
const QString &text,
|
||||
const TextParseOptions &options = _defaultOptions,
|
||||
int32 minResizeWidth = QFIXED_MAX,
|
||||
bool richText = false);
|
||||
String(const String &other) = default;
|
||||
String(String &&other) = default;
|
||||
String &operator=(const String &other) = default;
|
||||
String &operator=(String &&other) = default;
|
||||
~String() = default;
|
||||
|
||||
int countWidth(int width, bool breakEverywhere = false) const;
|
||||
int countHeight(int width, bool breakEverywhere = false) const;
|
||||
|
|
@ -168,34 +174,14 @@ public:
|
|||
TextSelection selection = AllTextSelection) const;
|
||||
IsolatedEmoji toIsolatedEmoji() const;
|
||||
|
||||
bool lastDots(int32 dots, int32 maxdots = 3) { // hack for typing animation
|
||||
if (_text.size() < maxdots) return false;
|
||||
|
||||
int32 nowDots = 0, from = _text.size() - maxdots, to = _text.size();
|
||||
for (int32 i = from; i < to; ++i) {
|
||||
if (_text.at(i) == QChar('.')) {
|
||||
++nowDots;
|
||||
}
|
||||
}
|
||||
if (nowDots == dots) return false;
|
||||
for (int32 j = from; j < from + dots; ++j) {
|
||||
_text[j] = QChar('.');
|
||||
}
|
||||
for (int32 j = from + dots; j < to; ++j) {
|
||||
_text[j] = QChar(' ');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const style::TextStyle *style() const {
|
||||
return _st;
|
||||
}
|
||||
|
||||
void clear();
|
||||
~String();
|
||||
|
||||
private:
|
||||
using TextBlocks = std::vector<std::unique_ptr<AbstractBlock>>;
|
||||
using TextBlocks = QVector<Block>;
|
||||
using TextLinks = QVector<ClickHandlerPtr>;
|
||||
|
||||
uint16 countBlockEnd(const TextBlocks::const_iterator &i, const TextBlocks::const_iterator &e) const;
|
||||
|
|
|
|||
|
|
@ -310,10 +310,20 @@ private:
|
|||
};
|
||||
|
||||
QFixed AbstractBlock::f_rbearing() const {
|
||||
return (type() == TextBlockTText) ? static_cast<const TextBlock*>(this)->real_f_rbearing() : 0;
|
||||
return (type() == TextBlockTText)
|
||||
? static_cast<const TextBlock*>(this)->real_f_rbearing()
|
||||
: 0;
|
||||
}
|
||||
|
||||
TextBlock::TextBlock(const style::font &font, const QString &str, QFixed minResizeWidth, uint16 from, uint16 length, uchar flags, uint16 lnkIndex) : AbstractBlock(font, str, from, length, flags, lnkIndex) {
|
||||
TextBlock::TextBlock(
|
||||
const style::font &font,
|
||||
const QString &str,
|
||||
QFixed minResizeWidth,
|
||||
uint16 from,
|
||||
uint16 length,
|
||||
uchar flags,
|
||||
uint16 lnkIndex)
|
||||
: AbstractBlock(font, str, from, length, flags, lnkIndex) {
|
||||
_flags |= ((TextBlockTText & 0x0F) << 8);
|
||||
if (length) {
|
||||
style::font blockFont = font;
|
||||
|
|
@ -347,11 +357,18 @@ TextBlock::TextBlock(const style::font &font, const QString &str, QFixed minResi
|
|||
}
|
||||
}
|
||||
|
||||
EmojiBlock::EmojiBlock(const style::font &font, const QString &str, uint16 from, uint16 length, uchar flags, uint16 lnkIndex, EmojiPtr emoji) : AbstractBlock(font, str, from, length, flags, lnkIndex)
|
||||
, emoji(emoji) {
|
||||
EmojiBlock::EmojiBlock(
|
||||
const style::font &font,
|
||||
const QString &str,
|
||||
uint16 from,
|
||||
uint16 length,
|
||||
uchar flags,
|
||||
uint16 lnkIndex,
|
||||
EmojiPtr emoji)
|
||||
: AbstractBlock(font, str, from, length, flags, lnkIndex)
|
||||
, _emoji(emoji) {
|
||||
_flags |= ((TextBlockTEmoji & 0x0F) << 8);
|
||||
_width = int(st::emojiSize + 2 * st::emojiPadding);
|
||||
|
||||
_rpadding = 0;
|
||||
for (auto i = length; i != 0;) {
|
||||
auto ch = str[_from + (--i)];
|
||||
|
|
@ -363,10 +380,5 @@ EmojiBlock::EmojiBlock(const style::font &font, const QString &str, uint16 from,
|
|||
}
|
||||
}
|
||||
|
||||
SkipBlock::SkipBlock(const style::font &font, const QString &str, uint16 from, int32 w, int32 h, uint16 lnkIndex) : AbstractBlock(font, str, from, 1, 0, lnkIndex), _height(h) {
|
||||
_flags |= ((TextBlockTSkip & 0x0F) << 8);
|
||||
_width = w;
|
||||
}
|
||||
|
||||
} // namespace Text
|
||||
} // namespace Ui
|
||||
|
|
|
|||
|
|
@ -34,16 +34,13 @@ enum TextBlockFlags {
|
|||
|
||||
class AbstractBlock {
|
||||
public:
|
||||
AbstractBlock(const style::font &font, const QString &str, uint16 from, uint16 length, uchar flags, uint16 lnkIndex) : _from(from), _flags((flags & 0xFF) | ((lnkIndex & 0xFFFF) << 12)) {
|
||||
}
|
||||
|
||||
uint16 from() const {
|
||||
return _from;
|
||||
}
|
||||
int32 width() const {
|
||||
int width() const {
|
||||
return _width.toInt();
|
||||
}
|
||||
int32 rpadding() const {
|
||||
int rpadding() const {
|
||||
return _rpadding.toInt();
|
||||
}
|
||||
QFixed f_width() const {
|
||||
|
|
@ -70,11 +67,18 @@ public:
|
|||
return (_flags & 0xFF);
|
||||
}
|
||||
|
||||
virtual std::unique_ptr<AbstractBlock> clone() const = 0;
|
||||
virtual ~AbstractBlock() {
|
||||
protected:
|
||||
AbstractBlock(
|
||||
const style::font &font,
|
||||
const QString &str,
|
||||
uint16 from,
|
||||
uint16 length,
|
||||
uchar flags,
|
||||
uint16 lnkIndex)
|
||||
: _from(from)
|
||||
, _flags((flags & 0xFF) | ((lnkIndex & 0xFFFF) << 12)) {
|
||||
}
|
||||
|
||||
protected:
|
||||
uint16 _from = 0;
|
||||
|
||||
uint32 _flags = 0; // 4 bits empty, 16 bits lnkIndex, 4 bits type, 8 bits flags
|
||||
|
|
@ -90,9 +94,16 @@ protected:
|
|||
|
||||
};
|
||||
|
||||
class NewlineBlock : public AbstractBlock {
|
||||
class NewlineBlock final : public AbstractBlock {
|
||||
public:
|
||||
NewlineBlock(const style::font &font, const QString &str, uint16 from, uint16 length, uchar flags, uint16 lnkIndex) : AbstractBlock(font, str, from, length, flags, lnkIndex), _nextDir(Qt::LayoutDirectionAuto) {
|
||||
NewlineBlock(
|
||||
const style::font &font,
|
||||
const QString &str,
|
||||
uint16 from,
|
||||
uint16 length,
|
||||
uchar flags,
|
||||
uint16 lnkIndex)
|
||||
: AbstractBlock(font, str, from, length, flags, lnkIndex) {
|
||||
_flags |= ((TextBlockTNewline & 0x0F) << 8);
|
||||
}
|
||||
|
||||
|
|
@ -100,12 +111,8 @@ public:
|
|||
return _nextDir;
|
||||
}
|
||||
|
||||
std::unique_ptr<AbstractBlock> clone() const override {
|
||||
return std::make_unique<NewlineBlock>(*this);
|
||||
}
|
||||
|
||||
private:
|
||||
Qt::LayoutDirection _nextDir;
|
||||
Qt::LayoutDirection _nextDir = Qt::LayoutDirectionAuto;
|
||||
|
||||
friend class String;
|
||||
friend class Parser;
|
||||
|
|
@ -113,14 +120,16 @@ private:
|
|||
|
||||
};
|
||||
|
||||
class TextWord {
|
||||
class TextWord final {
|
||||
public:
|
||||
TextWord() = default;
|
||||
TextWord(uint16 from, QFixed width, QFixed rbearing, QFixed rpadding = 0)
|
||||
: _from(from)
|
||||
, _width(width)
|
||||
, _rpadding(rpadding)
|
||||
, _rbearing(rbearing.value() > 0x7FFF ? 0x7FFF : (rbearing.value() < -0x7FFF ? -0x7FFF : rbearing.value())) {
|
||||
: _from(from)
|
||||
, _rbearing((rbearing.value() > 0x7FFF)
|
||||
? 0x7FFF
|
||||
: (rbearing.value() < -0x7FFF ? -0x7FFF : rbearing.value()))
|
||||
, _width(width)
|
||||
, _rpadding(rpadding) {
|
||||
}
|
||||
uint16 from() const {
|
||||
return _from;
|
||||
|
|
@ -140,45 +149,50 @@ public:
|
|||
|
||||
private:
|
||||
uint16 _from = 0;
|
||||
QFixed _width, _rpadding;
|
||||
int16 _rbearing = 0;
|
||||
QFixed _width, _rpadding;
|
||||
|
||||
};
|
||||
|
||||
class TextBlock : public AbstractBlock {
|
||||
class TextBlock final : public AbstractBlock {
|
||||
public:
|
||||
TextBlock(const style::font &font, const QString &str, QFixed minResizeWidth, uint16 from, uint16 length, uchar flags, uint16 lnkIndex);
|
||||
|
||||
std::unique_ptr<AbstractBlock> clone() const override {
|
||||
return std::make_unique<TextBlock>(*this);
|
||||
}
|
||||
TextBlock(
|
||||
const style::font &font,
|
||||
const QString &str,
|
||||
QFixed minResizeWidth,
|
||||
uint16 from,
|
||||
uint16 length,
|
||||
uchar flags,
|
||||
uint16 lnkIndex);
|
||||
|
||||
private:
|
||||
friend class AbstractBlock;
|
||||
QFixed real_f_rbearing() const {
|
||||
return _words.isEmpty() ? 0 : _words.back().f_rbearing();
|
||||
}
|
||||
|
||||
using TextWords = QVector<TextWord>;
|
||||
TextWords _words;
|
||||
QVector<TextWord> _words;
|
||||
|
||||
friend class String;
|
||||
friend class Parser;
|
||||
friend class Renderer;
|
||||
friend class BlockParser;
|
||||
friend class AbstractBlock;
|
||||
|
||||
};
|
||||
|
||||
class EmojiBlock : public AbstractBlock {
|
||||
class EmojiBlock final : public AbstractBlock {
|
||||
public:
|
||||
EmojiBlock(const style::font &font, const QString &str, uint16 from, uint16 length, uchar flags, uint16 lnkIndex, EmojiPtr emoji);
|
||||
|
||||
std::unique_ptr<AbstractBlock> clone() const override {
|
||||
return std::make_unique<EmojiBlock>(*this);
|
||||
}
|
||||
EmojiBlock(
|
||||
const style::font &font,
|
||||
const QString &str,
|
||||
uint16 from,
|
||||
uint16 length,
|
||||
uchar flags,
|
||||
uint16 lnkIndex,
|
||||
EmojiPtr emoji);
|
||||
|
||||
private:
|
||||
EmojiPtr emoji = nullptr;
|
||||
EmojiPtr _emoji = nullptr;
|
||||
|
||||
friend class String;
|
||||
friend class Parser;
|
||||
|
|
@ -186,20 +200,27 @@ private:
|
|||
|
||||
};
|
||||
|
||||
class SkipBlock : public AbstractBlock {
|
||||
class SkipBlock final : public AbstractBlock {
|
||||
public:
|
||||
SkipBlock(const style::font &font, const QString &str, uint16 from, int32 w, int32 h, uint16 lnkIndex);
|
||||
SkipBlock(
|
||||
const style::font &font,
|
||||
const QString &str,
|
||||
uint16 from,
|
||||
int32 w,
|
||||
int32 h,
|
||||
uint16 lnkIndex)
|
||||
: AbstractBlock(font, str, from, 1, 0, lnkIndex)
|
||||
, _height(h) {
|
||||
_flags |= ((TextBlockTSkip & 0x0F) << 8);
|
||||
_width = w;
|
||||
}
|
||||
|
||||
int32 height() const {
|
||||
int height() const {
|
||||
return _height;
|
||||
}
|
||||
|
||||
std::unique_ptr<AbstractBlock> clone() const override {
|
||||
return std::make_unique<SkipBlock>(*this);
|
||||
}
|
||||
|
||||
private:
|
||||
int32 _height;
|
||||
int _height = 0;
|
||||
|
||||
friend class String;
|
||||
friend class Parser;
|
||||
|
|
@ -207,5 +228,235 @@ private:
|
|||
|
||||
};
|
||||
|
||||
class Block final {
|
||||
public:
|
||||
Block() {
|
||||
Unexpected("Should not be called.");
|
||||
}
|
||||
Block(const Block &other) {
|
||||
switch (other->type()) {
|
||||
case TextBlockTNewline:
|
||||
emplace<NewlineBlock>(other.unsafe<NewlineBlock>());
|
||||
break;
|
||||
case TextBlockTText:
|
||||
emplace<TextBlock>(other.unsafe<TextBlock>());
|
||||
break;
|
||||
case TextBlockTEmoji:
|
||||
emplace<EmojiBlock>(other.unsafe<EmojiBlock>());
|
||||
break;
|
||||
case TextBlockTSkip:
|
||||
emplace<SkipBlock>(other.unsafe<SkipBlock>());
|
||||
break;
|
||||
default:
|
||||
Unexpected("Bad text block type in Block(const Block&).");
|
||||
}
|
||||
}
|
||||
Block(Block &&other) {
|
||||
switch (other->type()) {
|
||||
case TextBlockTNewline:
|
||||
emplace<NewlineBlock>(std::move(other.unsafe<NewlineBlock>()));
|
||||
break;
|
||||
case TextBlockTText:
|
||||
emplace<TextBlock>(std::move(other.unsafe<TextBlock>()));
|
||||
break;
|
||||
case TextBlockTEmoji:
|
||||
emplace<EmojiBlock>(std::move(other.unsafe<EmojiBlock>()));
|
||||
break;
|
||||
case TextBlockTSkip:
|
||||
emplace<SkipBlock>(std::move(other.unsafe<SkipBlock>()));
|
||||
break;
|
||||
default:
|
||||
Unexpected("Bad text block type in Block(Block&&).");
|
||||
}
|
||||
}
|
||||
Block &operator=(const Block &other) {
|
||||
if (&other == this) {
|
||||
return *this;
|
||||
}
|
||||
destroy();
|
||||
switch (other->type()) {
|
||||
case TextBlockTNewline:
|
||||
emplace<NewlineBlock>(other.unsafe<NewlineBlock>());
|
||||
break;
|
||||
case TextBlockTText:
|
||||
emplace<TextBlock>(other.unsafe<TextBlock>());
|
||||
break;
|
||||
case TextBlockTEmoji:
|
||||
emplace<EmojiBlock>(other.unsafe<EmojiBlock>());
|
||||
break;
|
||||
case TextBlockTSkip:
|
||||
emplace<SkipBlock>(other.unsafe<SkipBlock>());
|
||||
break;
|
||||
default:
|
||||
Unexpected("Bad text block type in operator=(const Block&).");
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
Block &operator=(Block &&other) {
|
||||
if (&other == this) {
|
||||
return *this;
|
||||
}
|
||||
destroy();
|
||||
switch (other->type()) {
|
||||
case TextBlockTNewline:
|
||||
emplace<NewlineBlock>(std::move(other.unsafe<NewlineBlock>()));
|
||||
break;
|
||||
case TextBlockTText:
|
||||
emplace<TextBlock>(std::move(other.unsafe<TextBlock>()));
|
||||
break;
|
||||
case TextBlockTEmoji:
|
||||
emplace<EmojiBlock>(std::move(other.unsafe<EmojiBlock>()));
|
||||
break;
|
||||
case TextBlockTSkip:
|
||||
emplace<SkipBlock>(std::move(other.unsafe<SkipBlock>()));
|
||||
break;
|
||||
default:
|
||||
Unexpected("Bad text block type in operator=(Block&&).");
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
~Block() {
|
||||
destroy();
|
||||
}
|
||||
|
||||
[[nodiscard]] static Block Newline(
|
||||
const style::font &font,
|
||||
const QString &str,
|
||||
uint16 from,
|
||||
uint16 length,
|
||||
uchar flags,
|
||||
uint16 lnkIndex) {
|
||||
return New<NewlineBlock>(font, str, from, length, flags, lnkIndex);
|
||||
}
|
||||
|
||||
[[nodiscard]] static Block Text(
|
||||
const style::font &font,
|
||||
const QString &str,
|
||||
QFixed minResizeWidth,
|
||||
uint16 from,
|
||||
uint16 length,
|
||||
uchar flags,
|
||||
uint16 lnkIndex) {
|
||||
return New<TextBlock>(
|
||||
font,
|
||||
str,
|
||||
minResizeWidth,
|
||||
from,
|
||||
length,
|
||||
flags,
|
||||
lnkIndex);
|
||||
}
|
||||
|
||||
[[nodiscard]] static Block Emoji(
|
||||
const style::font &font,
|
||||
const QString &str,
|
||||
uint16 from,
|
||||
uint16 length,
|
||||
uchar flags,
|
||||
uint16 lnkIndex,
|
||||
EmojiPtr emoji) {
|
||||
return New<EmojiBlock>(
|
||||
font,
|
||||
str,
|
||||
from,
|
||||
length,
|
||||
flags,
|
||||
lnkIndex,
|
||||
emoji);
|
||||
}
|
||||
|
||||
[[nodiscard]] static Block Skip(
|
||||
const style::font &font,
|
||||
const QString &str,
|
||||
uint16 from,
|
||||
int32 w,
|
||||
int32 h,
|
||||
uint16 lnkIndex) {
|
||||
return New<SkipBlock>(font, str, from, w, h, lnkIndex);
|
||||
}
|
||||
|
||||
template <typename FinalBlock>
|
||||
[[nodiscard]] FinalBlock &unsafe() {
|
||||
return *reinterpret_cast<FinalBlock*>(&_data);
|
||||
}
|
||||
|
||||
template <typename FinalBlock>
|
||||
[[nodiscard]] const FinalBlock &unsafe() const {
|
||||
return *reinterpret_cast<const FinalBlock*>(&_data);
|
||||
}
|
||||
|
||||
[[nodiscard]] AbstractBlock *get() {
|
||||
return &unsafe<AbstractBlock>();
|
||||
}
|
||||
|
||||
[[nodiscard]] const AbstractBlock *get() const {
|
||||
return &unsafe<AbstractBlock>();
|
||||
}
|
||||
|
||||
[[nodiscard]] AbstractBlock *operator->() {
|
||||
return get();
|
||||
}
|
||||
|
||||
[[nodiscard]] const AbstractBlock *operator->() const {
|
||||
return get();
|
||||
}
|
||||
|
||||
[[nodiscard]] AbstractBlock &operator*() {
|
||||
return *get();
|
||||
}
|
||||
|
||||
[[nodiscard]] const AbstractBlock &operator*() const {
|
||||
return *get();
|
||||
}
|
||||
|
||||
private:
|
||||
struct Tag {
|
||||
};
|
||||
|
||||
explicit Block(const Tag &) {
|
||||
}
|
||||
|
||||
template <typename FinalType, typename ...Args>
|
||||
[[nodiscard]] static Block New(Args &&...args) {
|
||||
auto result = Block(Tag{});
|
||||
result.emplace<FinalType>(std::forward<Args>(args)...);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename FinalType, typename ...Args>
|
||||
void emplace(Args &&...args) {
|
||||
new (&_data) FinalType(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
void destroy() {
|
||||
switch (get()->type()) {
|
||||
case TextBlockTNewline:
|
||||
unsafe<NewlineBlock>().~NewlineBlock();
|
||||
break;
|
||||
case TextBlockTText:
|
||||
unsafe<TextBlock>().~TextBlock();
|
||||
break;
|
||||
case TextBlockTEmoji:
|
||||
unsafe<EmojiBlock>().~EmojiBlock();
|
||||
break;
|
||||
case TextBlockTSkip:
|
||||
unsafe<SkipBlock>().~SkipBlock();
|
||||
break;
|
||||
default:
|
||||
Unexpected("Bad text block type in Block(Block&&).");
|
||||
}
|
||||
}
|
||||
|
||||
static_assert(sizeof(NewlineBlock) <= sizeof(TextBlock));
|
||||
static_assert(alignof(NewlineBlock) <= alignof(AbstractBlock));
|
||||
static_assert(sizeof(EmojiBlock) <= sizeof(TextBlock));
|
||||
static_assert(alignof(EmojiBlock) <= alignof(AbstractBlock));
|
||||
static_assert(sizeof(SkipBlock) <= sizeof(TextBlock));
|
||||
static_assert(alignof(SkipBlock) <= alignof(AbstractBlock));
|
||||
|
||||
std::aligned_storage_t<sizeof(TextBlock), alignof(AbstractBlock)> _data;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Text
|
||||
} // namespace Ui
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue