Added support for custom indexes of links in Ui::String::Text.

This commit is contained in:
23rd 2022-02-03 23:08:09 +03:00
parent 8618812fd6
commit 7e1effeeeb
3 changed files with 85 additions and 23 deletions

View file

@ -178,19 +178,21 @@ private:
class StartedEntity { class StartedEntity {
public: public:
enum class Type {
Flags,
Link,
IndexedLink,
Spoiler,
};
explicit StartedEntity(TextBlockFlags flags); explicit StartedEntity(TextBlockFlags flags);
explicit StartedEntity(uint16 index, bool isLnk = true); explicit StartedEntity(uint16 index, Type type);
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; std::optional<uint16> spoilerIndex() const;
private: private:
enum class Type {
Flags,
Link,
Spoiler,
};
const int _value = 0; const int _value = 0;
const Type _type; const Type _type;
@ -222,6 +224,8 @@ private:
bool isInvalidEntity(const EntityInText &entity) const; bool isInvalidEntity(const EntityInText &entity) const;
bool isLinkEntity(const EntityInText &entity) const; bool isLinkEntity(const EntityInText &entity) const;
bool processCustomIndex(uint16 index);
void parse(const TextParseOptions &options); void parse(const TextParseOptions &options);
void computeLinkText( void computeLinkText(
const QString &linkData, const QString &linkData,
@ -241,6 +245,8 @@ private:
const QFixed _stopAfterWidth; // summary width of all added words const QFixed _stopAfterWidth; // summary width of all added words
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<uint16> _linksIndexes;
std::vector<EntityLinkData> _links; std::vector<EntityLinkData> _links;
std::vector<EntityLinkData> _spoilers; std::vector<EntityLinkData> _spoilers;
std::vector<EntityLinkData> _monos; std::vector<EntityLinkData> _monos;
@ -276,9 +282,9 @@ Parser::StartedEntity::StartedEntity(TextBlockFlags flags)
Expects(_value >= 0 && _value < int(kStringLinkIndexShift)); Expects(_value >= 0 && _value < int(kStringLinkIndexShift));
} }
Parser::StartedEntity::StartedEntity(uint16 index, bool isLnk) Parser::StartedEntity::StartedEntity(uint16 index, Type type)
: _value(index) : _value(index)
, _type(isLnk ? Type::Link : Type::Spoiler) { , _type(type) {
Expects((_type == Type::Link) Expects((_type == Type::Link)
? (_value >= kStringLinkIndexShift) ? (_value >= kStringLinkIndexShift)
: (_value < kStringLinkIndexShift)); : (_value < kStringLinkIndexShift));
@ -292,7 +298,8 @@ std::optional<TextBlockFlags> Parser::StartedEntity::flags() const {
} }
std::optional<uint16> Parser::StartedEntity::lnkIndex() const { std::optional<uint16> Parser::StartedEntity::lnkIndex() const {
if (_value >= int(kStringLinkIndexShift) && (_type == Type::Link)) { if ((_value < int(kStringLinkIndexShift) && (_type == Type::IndexedLink))
|| (_value >= int(kStringLinkIndexShift) && (_type == Type::Link))) {
return uint16(_value); return uint16(_value);
} }
return std::nullopt; return std::nullopt;
@ -515,13 +522,18 @@ bool Parser::checkEntities() {
pushComplexUrl(); pushComplexUrl();
} }
using Type = StartedEntity::Type;
if (link.type != EntityType::Invalid) { if (link.type != EntityType::Invalid) {
createBlock(); createBlock();
_links.push_back(link); _links.push_back(link);
_lnkIndex = kStringLinkIndexShift + _links.size(); const auto tempIndex = _links.size();
const auto useCustom = processCustomIndex(tempIndex);
_startedEntities[entityEnd].emplace_back(_lnkIndex); _lnkIndex = tempIndex + (useCustom ? 0 : kStringLinkIndexShift);
_startedEntities[entityEnd].emplace_back(
_lnkIndex,
useCustom ? Type::IndexedLink : Type::Link);
} else if (flags) { } else if (flags) {
if (!(_flags & flags)) { if (!(_flags & flags)) {
createBlock(); createBlock();
@ -538,7 +550,9 @@ bool Parser::checkEntities() {
}); });
_spoilerIndex = _spoilers.size(); _spoilerIndex = _spoilers.size();
_startedEntities[entityEnd].emplace_back(_spoilerIndex, false); _startedEntities[entityEnd].emplace_back(
_spoilerIndex,
Type::Spoiler);
} }
++_waitingEntity; ++_waitingEntity;
@ -546,6 +560,22 @@ bool Parser::checkEntities() {
return true; return true;
} }
bool Parser::processCustomIndex(uint16 index) {
auto &url = _links[index - 1].data;
if (url.isEmpty()) {
return false;
}
if (url.startsWith("internal:index") && url.back().isDigit()) {
const auto customIndex = uint16(url.back().unicode() - '0');
// if (customIndex != index) {
url = QString();
_linksIndexes.push_back(customIndex);
return true;
// }
}
return false;
}
void Parser::skipPassedEntities() { void Parser::skipPassedEntities() {
while (_waitingEntity != _entitiesEnd while (_waitingEntity != _entitiesEnd
&& _start + _waitingEntity->offset() + _waitingEntity->length() <= _ptr) { && _start + _waitingEntity->offset() + _waitingEntity->length() <= _ptr) {
@ -737,11 +767,17 @@ void Parser::trimSourceRange() {
void Parser::finalize(const TextParseOptions &options) { void Parser::finalize(const TextParseOptions &options) {
_t->_links.resize(_maxLnkIndex + _maxShiftedLnkIndex); _t->_links.resize(_maxLnkIndex + _maxShiftedLnkIndex);
auto counterCustomIndex = uint16(0);
auto currentIndex = uint16(0); // Current the latest index of _t->_links. auto currentIndex = uint16(0); // Current the latest index of _t->_links.
struct { struct {
uint16 mono = 0; uint16 mono = 0;
uint16 lnk = 0; uint16 lnk = 0;
} lastHandlerIndex; } lastHandlerIndex;
const auto avoidIntersectionsWithCustom = [&] {
while (ranges::contains(_linksIndexes, currentIndex)) {
currentIndex++;
}
};
for (auto &block : _t->_blocks) { for (auto &block : _t->_blocks) {
const auto spoilerIndex = block->spoilerIndex(); const auto spoilerIndex = block->spoilerIndex();
if (spoilerIndex && (_t->_spoilers.size() < spoilerIndex)) { if (spoilerIndex && (_t->_spoilers.size() < spoilerIndex)) {
@ -752,6 +788,7 @@ void Parser::finalize(const TextParseOptions &options) {
_t->setSpoiler(spoilerIndex, std::move(handler)); _t->setSpoiler(spoilerIndex, std::move(handler));
} }
const auto shiftedIndex = block->lnkIndex(); const auto shiftedIndex = block->lnkIndex();
auto useCustomIndex = false;
if (shiftedIndex <= kStringLinkIndexShift) { if (shiftedIndex <= kStringLinkIndexShift) {
if (IsMono(block->flags()) && shiftedIndex) { if (IsMono(block->flags()) && shiftedIndex) {
const auto monoIndex = shiftedIndex; const auto monoIndex = shiftedIndex;
@ -762,6 +799,7 @@ void Parser::finalize(const TextParseOptions &options) {
} else { } else {
currentIndex++; currentIndex++;
} }
avoidIntersectionsWithCustom();
block->setLnkIndex(currentIndex); block->setLnkIndex(currentIndex);
const auto handler = Integration::Instance().createLinkHandler( const auto handler = Integration::Instance().createLinkHandler(
_monos[monoIndex - 1], _monos[monoIndex - 1],
@ -771,24 +809,38 @@ void Parser::finalize(const TextParseOptions &options) {
_t->setLink(currentIndex, handler); _t->setLink(currentIndex, handler);
} }
lastHandlerIndex.mono = monoIndex; lastHandlerIndex.mono = monoIndex;
continue;
} else if (shiftedIndex) {
useCustomIndex = true;
} else {
continue;
} }
continue;
} }
const auto realIndex = (shiftedIndex - kStringLinkIndexShift); const auto usedIndex = [&] {
return useCustomIndex
? _linksIndexes[counterCustomIndex - 1]
: currentIndex;
};
const auto realIndex = useCustomIndex
? shiftedIndex
: (shiftedIndex - kStringLinkIndexShift);
if (lastHandlerIndex.lnk == realIndex) { if (lastHandlerIndex.lnk == realIndex) {
block->setLnkIndex(currentIndex); block->setLnkIndex(usedIndex());
continue; // Optimization. continue; // Optimization.
} else { } else {
currentIndex++; (useCustomIndex ? counterCustomIndex : currentIndex)++;
} }
block->setLnkIndex(currentIndex); if (!useCustomIndex) {
avoidIntersectionsWithCustom();
}
block->setLnkIndex(usedIndex());
_t->_links.resize(currentIndex); _t->_links.resize(std::max(usedIndex(), uint16(_t->_links.size())));
const auto handler = Integration::Instance().createLinkHandler( const auto handler = Integration::Instance().createLinkHandler(
_links[realIndex - 1], _links[realIndex - 1],
_context); _context);
if (handler) { if (handler) {
_t->setLink(currentIndex, handler); _t->setLink(usedIndex(), handler);
} }
lastHandlerIndex.lnk = realIndex; lastHandlerIndex.lnk = realIndex;
} }

View file

@ -42,14 +42,22 @@ TextWithEntities Link(const QString &text, const QString &url) {
return WithSingleEntity(text, EntityType::CustomUrl, url); return WithSingleEntity(text, EntityType::CustomUrl, url);
} }
TextWithEntities Link(const QString &text, int index) {
Expects(index > 0 && index < 10);
return Link(text, QString("internal:index") + QChar('0' + index));
}
TextWithEntities PlainLink(const QString &text) { TextWithEntities PlainLink(const QString &text) {
return WithSingleEntity(text, EntityType::PlainLink); return WithSingleEntity(text, EntityType::PlainLink);
} }
TextWithEntities Wrapped(TextWithEntities text, EntityType type) { TextWithEntities Wrapped(
TextWithEntities text,
EntityType type,
const QString &data) {
text.entities.insert( text.entities.insert(
text.entities.begin(), text.entities.begin(),
{ type, 0, int(text.text.size()), {} }); { type, 0, int(text.text.size()), data });
return text; return text;
} }

View file

@ -30,10 +30,12 @@ inline constexpr auto Upper = details::ToUpperType{};
[[nodiscard]] TextWithEntities Link( [[nodiscard]] TextWithEntities Link(
const QString &text, const QString &text,
const QString &url = "internal:action"); const QString &url = "internal:action");
[[nodiscard]] TextWithEntities Link(const QString &text, int index);
[[nodiscard]] TextWithEntities PlainLink(const QString &text); [[nodiscard]] TextWithEntities PlainLink(const QString &text);
[[nodiscard]] TextWithEntities Wrapped( [[nodiscard]] TextWithEntities Wrapped(
TextWithEntities text, TextWithEntities text,
EntityType type); EntityType type,
const QString &data = QString());
[[nodiscard]] TextWithEntities RichLangValue(const QString &text); [[nodiscard]] TextWithEntities RichLangValue(const QString &text);
[[nodiscard]] inline TextWithEntities WithEntities(const QString &text) { [[nodiscard]] inline TextWithEntities WithEntities(const QString &text) {
return { text }; return { text };