Added initial spoiler support to Ui::Text::String.
This commit is contained in:
parent
e19a4c6544
commit
1819e6e3a5
3 changed files with 192 additions and 22 deletions
208
ui/text/text.cpp
208
ui/text/text.cpp
|
|
@ -253,6 +253,30 @@ const TextParseOptions _textPlainOptions = {
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
namespace Text {
|
namespace Text {
|
||||||
|
|
||||||
|
class String::SpoilerClickHandler final : public ClickHandler {
|
||||||
|
public:
|
||||||
|
SpoilerClickHandler() = default;
|
||||||
|
|
||||||
|
TextEntity getTextEntity() const override {
|
||||||
|
return { EntityType::Spoiler };
|
||||||
|
}
|
||||||
|
|
||||||
|
void onClick(ClickContext context) const override {
|
||||||
|
if (!_shown) {
|
||||||
|
const auto nonconst = const_cast<SpoilerClickHandler*>(this);
|
||||||
|
nonconst->_shown = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool shown() const {
|
||||||
|
return _shown;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool _shown = false;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
class Parser {
|
class Parser {
|
||||||
public:
|
public:
|
||||||
Parser(
|
Parser(
|
||||||
|
|
@ -273,13 +297,20 @@ private:
|
||||||
class StartedEntity {
|
class StartedEntity {
|
||||||
public:
|
public:
|
||||||
explicit StartedEntity(TextBlockFlags flags);
|
explicit StartedEntity(TextBlockFlags flags);
|
||||||
explicit StartedEntity(uint16 lnkIndex);
|
explicit StartedEntity(uint16 index, bool isLnk = true);
|
||||||
|
|
||||||
std::optional<TextBlockFlags> flags() const;
|
std::optional<TextBlockFlags> flags() const;
|
||||||
std::optional<uint16> lnkIndex() const;
|
std::optional<uint16> lnkIndex() const;
|
||||||
|
std::optional<uint16> spoilerIndex() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int _value = 0;
|
enum class Type {
|
||||||
|
Flags,
|
||||||
|
Link,
|
||||||
|
Spoiler,
|
||||||
|
};
|
||||||
|
const int _value = 0;
|
||||||
|
const Type _type;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -334,15 +365,18 @@ private:
|
||||||
const bool _checkTilde = false; // do we need a special text block for tilde symbol
|
const bool _checkTilde = false; // do we need a special text block for tilde symbol
|
||||||
|
|
||||||
std::vector<EntityLinkData> _links;
|
std::vector<EntityLinkData> _links;
|
||||||
|
std::vector<EntityLinkData> _spoilers;
|
||||||
base::flat_map<
|
base::flat_map<
|
||||||
const QChar*,
|
const QChar*,
|
||||||
std::vector<StartedEntity>> _startedEntities;
|
std::vector<StartedEntity>> _startedEntities;
|
||||||
|
|
||||||
uint16 _maxLnkIndex = 0;
|
uint16 _maxLnkIndex = 0;
|
||||||
|
uint16 _maxSpoilerIndex = 0;
|
||||||
|
|
||||||
// current state
|
// current state
|
||||||
int32 _flags = 0;
|
int32 _flags = 0;
|
||||||
uint16 _lnkIndex = 0;
|
uint16 _lnkIndex = 0;
|
||||||
|
uint16 _spoilerIndex = 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 _diacs = 0; // diac chars skipped without good char
|
int32 _diacs = 0; // diac chars skipped without good char
|
||||||
|
|
@ -357,23 +391,36 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Parser::StartedEntity::StartedEntity(TextBlockFlags flags) : _value(flags) {
|
Parser::StartedEntity::StartedEntity(TextBlockFlags flags)
|
||||||
|
: _value(flags)
|
||||||
|
, _type(Type::Flags) {
|
||||||
Expects(_value >= 0 && _value < int(kStringLinkIndexShift));
|
Expects(_value >= 0 && _value < int(kStringLinkIndexShift));
|
||||||
}
|
}
|
||||||
|
|
||||||
Parser::StartedEntity::StartedEntity(uint16 lnkIndex) : _value(lnkIndex) {
|
Parser::StartedEntity::StartedEntity(uint16 index, bool isLnk)
|
||||||
Expects(_value >= kStringLinkIndexShift);
|
: _value(index)
|
||||||
|
, _type(isLnk ? Type::Link : Type::Spoiler) {
|
||||||
|
Expects((_type == Type::Link)
|
||||||
|
? (_value >= kStringLinkIndexShift)
|
||||||
|
: (_value < kStringLinkIndexShift));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<TextBlockFlags> Parser::StartedEntity::flags() const {
|
std::optional<TextBlockFlags> Parser::StartedEntity::flags() const {
|
||||||
if (_value < int(kStringLinkIndexShift)) {
|
if (_value < int(kStringLinkIndexShift) && (_type == Type::Flags)) {
|
||||||
return TextBlockFlags(_value);
|
return TextBlockFlags(_value);
|
||||||
}
|
}
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<uint16> Parser::StartedEntity::lnkIndex() const {
|
std::optional<uint16> Parser::StartedEntity::lnkIndex() const {
|
||||||
if (_value >= int(kStringLinkIndexShift)) {
|
if (_value >= int(kStringLinkIndexShift) && (_type == Type::Link)) {
|
||||||
|
return uint16(_value);
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<uint16> Parser::StartedEntity::spoilerIndex() const {
|
||||||
|
if (_value < int(kStringLinkIndexShift) && (_type == Type::Spoiler)) {
|
||||||
return uint16(_value);
|
return uint16(_value);
|
||||||
}
|
}
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
@ -437,6 +484,9 @@ void Parser::createBlock(int32 skipBack) {
|
||||||
if (_lnkIndex < kStringLinkIndexShift && _lnkIndex > _maxLnkIndex) {
|
if (_lnkIndex < kStringLinkIndexShift && _lnkIndex > _maxLnkIndex) {
|
||||||
_maxLnkIndex = _lnkIndex;
|
_maxLnkIndex = _lnkIndex;
|
||||||
}
|
}
|
||||||
|
if (_spoilerIndex > _maxSpoilerIndex) {
|
||||||
|
_maxSpoilerIndex = _spoilerIndex;
|
||||||
|
}
|
||||||
|
|
||||||
int32 len = int32(_t->_text.size()) + skipBack - _blockStart;
|
int32 len = int32(_t->_text.size()) + skipBack - _blockStart;
|
||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
|
|
@ -450,13 +500,13 @@ void Parser::createBlock(int32 skipBack) {
|
||||||
}
|
}
|
||||||
_lastSkipped = false;
|
_lastSkipped = false;
|
||||||
if (_emoji) {
|
if (_emoji) {
|
||||||
_t->_blocks.push_back(Block::Emoji(_t->_st->font, _t->_text, _blockStart, len, _flags, _lnkIndex, 0/*spoilerIndex*/, _emoji));
|
_t->_blocks.push_back(Block::Emoji(_t->_st->font, _t->_text, _blockStart, len, _flags, _lnkIndex, _spoilerIndex, _emoji));
|
||||||
_emoji = nullptr;
|
_emoji = nullptr;
|
||||||
_lastSkipped = true;
|
_lastSkipped = true;
|
||||||
} else if (newline) {
|
} else if (newline) {
|
||||||
_t->_blocks.push_back(Block::Newline(_t->_st->font, _t->_text, _blockStart, len, _flags, _lnkIndex, 0/*spoilerIndex*/));
|
_t->_blocks.push_back(Block::Newline(_t->_st->font, _t->_text, _blockStart, len, _flags, _lnkIndex, _spoilerIndex));
|
||||||
} else {
|
} else {
|
||||||
_t->_blocks.push_back(Block::Text(_t->_st->font, _t->_text, _t->_minResizeWidth, _blockStart, len, _flags, _lnkIndex, 0/*spoilerIndex*/));
|
_t->_blocks.push_back(Block::Text(_t->_st->font, _t->_text, _t->_minResizeWidth, _blockStart, len, _flags, _lnkIndex, _spoilerIndex));
|
||||||
}
|
}
|
||||||
_blockStart += len;
|
_blockStart += len;
|
||||||
blockCreated();
|
blockCreated();
|
||||||
|
|
@ -466,7 +516,7 @@ void Parser::createBlock(int32 skipBack) {
|
||||||
void Parser::createSkipBlock(int32 w, int32 h) {
|
void Parser::createSkipBlock(int32 w, int32 h) {
|
||||||
createBlock();
|
createBlock();
|
||||||
_t->_text.push_back('_');
|
_t->_text.push_back('_');
|
||||||
_t->_blocks.push_back(Block::Skip(_t->_st->font, _t->_text, _blockStart++, w, h, _lnkIndex, 0/*spoilerIndex*/));
|
_t->_blocks.push_back(Block::Skip(_t->_st->font, _t->_text, _blockStart++, w, h, _lnkIndex, _spoilerIndex));
|
||||||
blockCreated();
|
blockCreated();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -509,6 +559,11 @@ void Parser::finishEntities() {
|
||||||
createBlock();
|
createBlock();
|
||||||
_lnkIndex = 0;
|
_lnkIndex = 0;
|
||||||
}
|
}
|
||||||
|
} else if (const auto spoilerIndex = list.back().spoilerIndex()) {
|
||||||
|
if (_spoilerIndex == *spoilerIndex && (_spoilerIndex != 0)) {
|
||||||
|
createBlock();
|
||||||
|
_spoilerIndex = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
list.pop_back();
|
list.pop_back();
|
||||||
}
|
}
|
||||||
|
|
@ -594,6 +649,16 @@ bool Parser::checkEntities() {
|
||||||
_flags |= flags;
|
_flags |= flags;
|
||||||
_startedEntities[entityEnd].emplace_back(flags);
|
_startedEntities[entityEnd].emplace_back(flags);
|
||||||
}
|
}
|
||||||
|
} else if (entityType == EntityType::Spoiler) {
|
||||||
|
createBlock();
|
||||||
|
|
||||||
|
_spoilers.push_back(EntityLinkData{
|
||||||
|
.data = QString::number(_spoilers.size() + 1),
|
||||||
|
.type = entityType,
|
||||||
|
});
|
||||||
|
_spoilerIndex = _spoilers.size();
|
||||||
|
|
||||||
|
_startedEntities[entityEnd].emplace_back(_spoilerIndex, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
++_waitingEntity;
|
++_waitingEntity;
|
||||||
|
|
@ -918,7 +983,16 @@ void Parser::checkForElidedSkipBlock() {
|
||||||
|
|
||||||
void Parser::finalize(const TextParseOptions &options) {
|
void Parser::finalize(const TextParseOptions &options) {
|
||||||
_t->_links.resize(_maxLnkIndex);
|
_t->_links.resize(_maxLnkIndex);
|
||||||
|
_t->_spoilers.resize(_maxSpoilerIndex);
|
||||||
for (auto &block : _t->_blocks) {
|
for (auto &block : _t->_blocks) {
|
||||||
|
const auto spoilerIndex = block->spoilerIndex();
|
||||||
|
if (spoilerIndex) {
|
||||||
|
_t->_spoilers.resize(spoilerIndex);
|
||||||
|
const auto handler = (options.flags & TextParseLinks)
|
||||||
|
? std::make_shared<String::SpoilerClickHandler>()
|
||||||
|
: nullptr;
|
||||||
|
_t->_spoilers[spoilerIndex - 1] = std::move(handler);
|
||||||
|
}
|
||||||
const auto shiftedIndex = block->lnkIndex();
|
const auto shiftedIndex = block->lnkIndex();
|
||||||
if (shiftedIndex <= kStringLinkIndexShift) {
|
if (shiftedIndex <= kStringLinkIndexShift) {
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -939,6 +1013,7 @@ void Parser::finalize(const TextParseOptions &options) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_t->_links.squeeze();
|
_t->_links.squeeze();
|
||||||
|
_t->_spoilers.squeeze();
|
||||||
_t->_blocks.squeeze();
|
_t->_blocks.squeeze();
|
||||||
_t->_text.squeeze();
|
_t->_text.squeeze();
|
||||||
}
|
}
|
||||||
|
|
@ -1591,8 +1666,14 @@ private:
|
||||||
TextBlockType _type = currentBlock->type();
|
TextBlockType _type = currentBlock->type();
|
||||||
if (!_p && _lookupX >= x && _lookupX < x + si.width) { // _lookupRequest
|
if (!_p && _lookupX >= x && _lookupX < x + si.width) { // _lookupRequest
|
||||||
if (_lookupLink) {
|
if (_lookupLink) {
|
||||||
if (currentBlock->lnkIndex() && _lookupY >= _y + _yDelta && _lookupY < _y + _yDelta + _fontHeight) {
|
if (_lookupY >= _y + _yDelta && _lookupY < _y + _yDelta + _fontHeight) {
|
||||||
_lookupResult.link = _t->_links.at(currentBlock->lnkIndex() - 1);
|
const auto spoilerLink = _t->spoilerLink(currentBlock->spoilerIndex());
|
||||||
|
const auto resultLink = (spoilerLink || !currentBlock->lnkIndex())
|
||||||
|
? spoilerLink
|
||||||
|
: _t->_links.at(currentBlock->lnkIndex() - 1);
|
||||||
|
if (resultLink) {
|
||||||
|
_lookupResult.link = resultLink;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (_type != TextBlockTSkip) {
|
if (_type != TextBlockTSkip) {
|
||||||
|
|
@ -1664,12 +1745,35 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Emoji::Draw(
|
if (_background.brush) {
|
||||||
*_p,
|
const auto from = currentBlock->from();
|
||||||
static_cast<const EmojiBlock*>(currentBlock)->_emoji,
|
const auto to = currentBlock->from() + currentBlock->width();
|
||||||
Emoji::GetSizeNormal(),
|
if (_localFrom + si.position < to) {
|
||||||
(glyphX + st::emojiPadding).toInt(),
|
auto chFrom = _str + currentBlock->from();
|
||||||
_y + _yDelta + emojiY);
|
auto chTo = chFrom + ((nextBlock ? nextBlock->from() : _t->_text.size()) - currentBlock->from());
|
||||||
|
if (_localFrom + si.position >= from) { // could be without space
|
||||||
|
if (chTo == chFrom || (chTo - 1)->unicode() != QChar::Space || to >= (chTo - _str)) {
|
||||||
|
fillSpoilerRange(x, si.width, blockIndex);
|
||||||
|
} else { // or with space
|
||||||
|
fillSpoilerRange(glyphX, currentBlock->f_width(), blockIndex);
|
||||||
|
}
|
||||||
|
} else if (chTo > chFrom && (chTo - 1)->unicode() == QChar::Space && (chTo - 1 - _str) >= from) {
|
||||||
|
if (rtl) { // rtl space only
|
||||||
|
fillSpoilerRange(x, glyphX - x, blockIndex);
|
||||||
|
} else { // ltr space only
|
||||||
|
fillSpoilerRange(x + currentBlock->f_width(), si.width, blockIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!_background.inFront) {
|
||||||
|
Emoji::Draw(
|
||||||
|
*_p,
|
||||||
|
static_cast<const EmojiBlock*>(currentBlock)->_emoji,
|
||||||
|
Emoji::GetSizeNormal(),
|
||||||
|
(glyphX + st::emojiPadding).toInt(),
|
||||||
|
_y + _yDelta + emojiY);
|
||||||
|
}
|
||||||
// } else if (_p && currentBlock->type() == TextBlockSkip) { // debug
|
// } else if (_p && currentBlock->type() == TextBlockSkip) { // debug
|
||||||
// _p->fillRect(QRect(x.toInt(), _y, currentBlock->width(), static_cast<SkipBlock*>(currentBlock)->height()), QColor(0, 0, 0, 32));
|
// _p->fillRect(QRect(x.toInt(), _y, currentBlock->width(), static_cast<SkipBlock*>(currentBlock)->height()), QColor(0, 0, 0, 32));
|
||||||
}
|
}
|
||||||
|
|
@ -1697,8 +1801,14 @@ private:
|
||||||
|
|
||||||
if (!_p && _lookupX >= x && _lookupX < x + itemWidth) { // _lookupRequest
|
if (!_p && _lookupX >= x && _lookupX < x + itemWidth) { // _lookupRequest
|
||||||
if (_lookupLink) {
|
if (_lookupLink) {
|
||||||
if (currentBlock->lnkIndex() && _lookupY >= _y + _yDelta && _lookupY < _y + _yDelta + _fontHeight) {
|
if (_lookupY >= _y + _yDelta && _lookupY < _y + _yDelta + _fontHeight) {
|
||||||
_lookupResult.link = _t->_links.at(currentBlock->lnkIndex() - 1);
|
const auto spoilerLink = _t->spoilerLink(currentBlock->spoilerIndex());
|
||||||
|
const auto resultLink = (spoilerLink || !currentBlock->lnkIndex())
|
||||||
|
? spoilerLink
|
||||||
|
: _t->_links.at(currentBlock->lnkIndex() - 1);
|
||||||
|
if (resultLink) {
|
||||||
|
_lookupResult.link = resultLink;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_lookupResult.uponSymbol = true;
|
_lookupResult.uponSymbol = true;
|
||||||
|
|
@ -1752,6 +1862,10 @@ private:
|
||||||
gf.justified = false;
|
gf.justified = false;
|
||||||
gf.initWithScriptItem(si);
|
gf.initWithScriptItem(si);
|
||||||
|
|
||||||
|
if (!_background.inFront) {
|
||||||
|
fillSpoilerRange(x, si.width, blockIndex);
|
||||||
|
}
|
||||||
|
|
||||||
auto hasSelected = false;
|
auto hasSelected = false;
|
||||||
auto hasNotSelected = true;
|
auto hasNotSelected = true;
|
||||||
auto selectedRect = QRect();
|
auto selectedRect = QRect();
|
||||||
|
|
@ -1823,6 +1937,10 @@ private:
|
||||||
_p->setPen(*_currentPen);
|
_p->setPen(*_currentPen);
|
||||||
_p->drawTextItem(QPointF(x.toReal(), textY), gf);
|
_p->drawTextItem(QPointF(x.toReal(), textY), gf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_background.inFront) {
|
||||||
|
fillSpoilerRange(x, si.width, blockIndex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
x += itemWidth;
|
x += itemWidth;
|
||||||
|
|
@ -1835,6 +1953,22 @@ private:
|
||||||
_p->fillRect(left, _y + _yDelta, width, _fontHeight, _textPalette->selectBg);
|
_p->fillRect(left, _y + _yDelta, width, _fontHeight, _textPalette->selectBg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void fillSpoilerRange(QFixed x, QFixed width, int currentBlockIndex) {
|
||||||
|
if (_background.brush) {
|
||||||
|
const auto elideOffset =
|
||||||
|
(_indexOfElidedBlock == currentBlockIndex)
|
||||||
|
? (_elideRemoveFromEnd + _f->elidew)
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
_p->fillRect(
|
||||||
|
x.toInt(),
|
||||||
|
_y + _yDelta,
|
||||||
|
width.toInt() - elideOffset,
|
||||||
|
_fontHeight,
|
||||||
|
*_background.brush);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void elideSaveBlock(int32 blockIndex, const AbstractBlock *&_endBlock, int32 elideStart, int32 elideWidth) {
|
void elideSaveBlock(int32 blockIndex, const AbstractBlock *&_endBlock, int32 elideStart, int32 elideWidth) {
|
||||||
if (_elideSavedBlock) {
|
if (_elideSavedBlock) {
|
||||||
restoreAfterElided();
|
restoreAfterElided();
|
||||||
|
|
@ -1900,6 +2034,7 @@ private:
|
||||||
lineText = lineText.mid(0, currentBlock->from() - _localFrom) + _Elide;
|
lineText = lineText.mid(0, currentBlock->from() - _localFrom) + _Elide;
|
||||||
lineLength = currentBlock->from() + _Elide.size() - _lineStart;
|
lineLength = currentBlock->from() + _Elide.size() - _lineStart;
|
||||||
_selection.to = qMin(_selection.to, currentBlock->from());
|
_selection.to = qMin(_selection.to, currentBlock->from());
|
||||||
|
_indexOfElidedBlock = blockIndex + (nextBlock ? 1 : 0);
|
||||||
setElideBidi(currentBlock->from(), _Elide.size());
|
setElideBidi(currentBlock->from(), _Elide.size());
|
||||||
elideSaveBlock(blockIndex - 1, _endBlock, currentBlock->from(), elideWidth);
|
elideSaveBlock(blockIndex - 1, _endBlock, currentBlock->from(), elideWidth);
|
||||||
return;
|
return;
|
||||||
|
|
@ -1932,6 +2067,7 @@ private:
|
||||||
lineText += _Elide;
|
lineText += _Elide;
|
||||||
lineLength = _localFrom + pos + _Elide.size() - _lineStart;
|
lineLength = _localFrom + pos + _Elide.size() - _lineStart;
|
||||||
_selection.to = qMin(_selection.to, uint16(_localFrom + pos));
|
_selection.to = qMin(_selection.to, uint16(_localFrom + pos));
|
||||||
|
_indexOfElidedBlock = blockIndex + (nextBlock ? 1 : 0);
|
||||||
setElideBidi(_localFrom + pos, _Elide.size());
|
setElideBidi(_localFrom + pos, _Elide.size());
|
||||||
_blocksSize = blockIndex;
|
_blocksSize = blockIndex;
|
||||||
_endBlock = nextBlock;
|
_endBlock = nextBlock;
|
||||||
|
|
@ -1952,6 +2088,7 @@ private:
|
||||||
|
|
||||||
int32 elideStart = _localFrom + lineText.size();
|
int32 elideStart = _localFrom + lineText.size();
|
||||||
_selection.to = qMin(_selection.to, uint16(elideStart));
|
_selection.to = qMin(_selection.to, uint16(elideStart));
|
||||||
|
_indexOfElidedBlock = blockIndex + (nextBlock ? 1 : 0);
|
||||||
setElideBidi(elideStart, _Elide.size());
|
setElideBidi(elideStart, _Elide.size());
|
||||||
|
|
||||||
lineText += _Elide;
|
lineText += _Elide;
|
||||||
|
|
@ -2611,6 +2748,18 @@ private:
|
||||||
void applyBlockProperties(const AbstractBlock *block) {
|
void applyBlockProperties(const AbstractBlock *block) {
|
||||||
eSetFont(block);
|
eSetFont(block);
|
||||||
if (_p) {
|
if (_p) {
|
||||||
|
const auto handler = !block->spoilerIndex()
|
||||||
|
? nullptr
|
||||||
|
: _t->_spoilers.at(block->spoilerIndex() - 1);
|
||||||
|
if (!block->spoilerIndex()) {
|
||||||
|
_background = {};
|
||||||
|
} else {
|
||||||
|
const auto inBack = (handler && handler->shown());
|
||||||
|
_background.inFront = !inBack;
|
||||||
|
_background.brush = inBack
|
||||||
|
? &_textPalette->spoilerActiveBg->b
|
||||||
|
: &_textPalette->spoilerBg->b;
|
||||||
|
}
|
||||||
if (block->lnkIndex()) {
|
if (block->lnkIndex()) {
|
||||||
_currentPen = &_textPalette->linkFg->p;
|
_currentPen = &_textPalette->linkFg->p;
|
||||||
_currentPenSelected = &_textPalette->selectLinkFg->p;
|
_currentPenSelected = &_textPalette->selectLinkFg->p;
|
||||||
|
|
@ -2635,6 +2784,10 @@ private:
|
||||||
QPen _originalPenSelected;
|
QPen _originalPenSelected;
|
||||||
const QPen *_currentPen = nullptr;
|
const QPen *_currentPen = nullptr;
|
||||||
const QPen *_currentPenSelected = nullptr;
|
const QPen *_currentPenSelected = nullptr;
|
||||||
|
struct {
|
||||||
|
const QBrush *brush = nullptr;
|
||||||
|
bool inFront = false;
|
||||||
|
} _background;
|
||||||
int _yFrom = 0;
|
int _yFrom = 0;
|
||||||
int _yTo = 0;
|
int _yTo = 0;
|
||||||
int _yToElide = 0;
|
int _yToElide = 0;
|
||||||
|
|
@ -2642,6 +2795,8 @@ private:
|
||||||
bool _fullWidthSelection = true;
|
bool _fullWidthSelection = true;
|
||||||
const QChar *_str = nullptr;
|
const QChar *_str = nullptr;
|
||||||
|
|
||||||
|
int _indexOfElidedBlock = -1; // For spoilers.
|
||||||
|
|
||||||
// current paragraph data
|
// current paragraph data
|
||||||
String::TextBlocks::const_iterator _parStartBlock;
|
String::TextBlocks::const_iterator _parStartBlock;
|
||||||
Qt::LayoutDirection _parDirection;
|
Qt::LayoutDirection _parDirection;
|
||||||
|
|
@ -3248,7 +3403,7 @@ TextForMimeData String::toText(
|
||||||
{ TextBlockFUnderline, EntityType::Underline },
|
{ TextBlockFUnderline, EntityType::Underline },
|
||||||
{ TextBlockFStrikeOut, EntityType::StrikeOut },
|
{ TextBlockFStrikeOut, EntityType::StrikeOut },
|
||||||
{ TextBlockFCode, EntityType::Code }, // #TODO entities
|
{ TextBlockFCode, EntityType::Code }, // #TODO entities
|
||||||
{ TextBlockFPre, EntityType::Pre }
|
{ TextBlockFPre, EntityType::Pre },
|
||||||
} : std::vector<MarkdownTagTracker>();
|
} : std::vector<MarkdownTagTracker>();
|
||||||
const auto flagsChangeCallback = [&](int32 oldFlags, int32 newFlags) {
|
const auto flagsChangeCallback = [&](int32 oldFlags, int32 newFlags) {
|
||||||
if (!composeEntities) {
|
if (!composeEntities) {
|
||||||
|
|
@ -3368,10 +3523,19 @@ void String::clear() {
|
||||||
void String::clearFields() {
|
void String::clearFields() {
|
||||||
_blocks.clear();
|
_blocks.clear();
|
||||||
_links.clear();
|
_links.clear();
|
||||||
|
_spoilers.clear();
|
||||||
_maxWidth = _minHeight = 0;
|
_maxWidth = _minHeight = 0;
|
||||||
_startDir = Qt::LayoutDirectionAuto;
|
_startDir = Qt::LayoutDirectionAuto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ClickHandlerPtr String::spoilerLink(uint16 spoilerIndex) const {
|
||||||
|
if (spoilerIndex) {
|
||||||
|
const auto &handler = _spoilers.at(spoilerIndex - 1);
|
||||||
|
return (handler && !handler->shown()) ? handler : nullptr;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
bool IsWordSeparator(QChar ch) {
|
bool IsWordSeparator(QChar ch) {
|
||||||
switch (ch.unicode()) {
|
switch (ch.unicode()) {
|
||||||
case QChar::Space:
|
case QChar::Space:
|
||||||
|
|
|
||||||
|
|
@ -205,6 +205,8 @@ private:
|
||||||
// it is also called from move constructor / assignment operator
|
// it is also called from move constructor / assignment operator
|
||||||
void clearFields();
|
void clearFields();
|
||||||
|
|
||||||
|
ClickHandlerPtr spoilerLink(uint16 spoilerIndex) const;
|
||||||
|
|
||||||
TextForMimeData toText(
|
TextForMimeData toText(
|
||||||
TextSelection selection,
|
TextSelection selection,
|
||||||
bool composeExpanded,
|
bool composeExpanded,
|
||||||
|
|
@ -220,6 +222,9 @@ private:
|
||||||
TextBlocks _blocks;
|
TextBlocks _blocks;
|
||||||
TextLinks _links;
|
TextLinks _links;
|
||||||
|
|
||||||
|
class SpoilerClickHandler;
|
||||||
|
QVector<std::shared_ptr<SpoilerClickHandler>> _spoilers;
|
||||||
|
|
||||||
Qt::LayoutDirection _startDir = Qt::LayoutDirectionAuto;
|
Qt::LayoutDirection _startDir = Qt::LayoutDirectionAuto;
|
||||||
|
|
||||||
friend class Parser;
|
friend class Parser;
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ enum class EntityType : uchar {
|
||||||
StrikeOut,
|
StrikeOut,
|
||||||
Code, // inline
|
Code, // inline
|
||||||
Pre, // block
|
Pre, // block
|
||||||
|
Spoiler,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class EntityLinkShown : uchar {
|
enum class EntityLinkShown : uchar {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue