From 7e1effeeebfc411fbdf0370754dc7d9735f33805 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Thu, 3 Feb 2022 23:08:09 +0300 Subject: [PATCH] Added support for custom indexes of links in Ui::String::Text. --- ui/text/text.cpp | 92 +++++++++++++++++++++++++++++--------- ui/text/text_utilities.cpp | 12 ++++- ui/text/text_utilities.h | 4 +- 3 files changed, 85 insertions(+), 23 deletions(-) diff --git a/ui/text/text.cpp b/ui/text/text.cpp index 30139d2..fc4c0e5 100644 --- a/ui/text/text.cpp +++ b/ui/text/text.cpp @@ -178,19 +178,21 @@ private: class StartedEntity { public: + enum class Type { + Flags, + Link, + IndexedLink, + Spoiler, + }; + explicit StartedEntity(TextBlockFlags flags); - explicit StartedEntity(uint16 index, bool isLnk = true); + explicit StartedEntity(uint16 index, Type type); std::optional flags() const; std::optional lnkIndex() const; std::optional spoilerIndex() const; private: - enum class Type { - Flags, - Link, - Spoiler, - }; const int _value = 0; const Type _type; @@ -222,6 +224,8 @@ private: bool isInvalidEntity(const EntityInText &entity) const; bool isLinkEntity(const EntityInText &entity) const; + bool processCustomIndex(uint16 index); + void parse(const TextParseOptions &options); void computeLinkText( const QString &linkData, @@ -241,6 +245,8 @@ private: const QFixed _stopAfterWidth; // summary width of all added words const bool _checkTilde = false; // do we need a special text block for tilde symbol + std::vector _linksIndexes; + std::vector _links; std::vector _spoilers; std::vector _monos; @@ -276,9 +282,9 @@ Parser::StartedEntity::StartedEntity(TextBlockFlags flags) Expects(_value >= 0 && _value < int(kStringLinkIndexShift)); } -Parser::StartedEntity::StartedEntity(uint16 index, bool isLnk) +Parser::StartedEntity::StartedEntity(uint16 index, Type type) : _value(index) -, _type(isLnk ? Type::Link : Type::Spoiler) { +, _type(type) { Expects((_type == Type::Link) ? (_value >= kStringLinkIndexShift) : (_value < kStringLinkIndexShift)); @@ -292,7 +298,8 @@ std::optional Parser::StartedEntity::flags() const { } std::optional 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 std::nullopt; @@ -515,13 +522,18 @@ bool Parser::checkEntities() { pushComplexUrl(); } + using Type = StartedEntity::Type; + if (link.type != EntityType::Invalid) { createBlock(); _links.push_back(link); - _lnkIndex = kStringLinkIndexShift + _links.size(); - - _startedEntities[entityEnd].emplace_back(_lnkIndex); + const auto tempIndex = _links.size(); + const auto useCustom = processCustomIndex(tempIndex); + _lnkIndex = tempIndex + (useCustom ? 0 : kStringLinkIndexShift); + _startedEntities[entityEnd].emplace_back( + _lnkIndex, + useCustom ? Type::IndexedLink : Type::Link); } else if (flags) { if (!(_flags & flags)) { createBlock(); @@ -538,7 +550,9 @@ bool Parser::checkEntities() { }); _spoilerIndex = _spoilers.size(); - _startedEntities[entityEnd].emplace_back(_spoilerIndex, false); + _startedEntities[entityEnd].emplace_back( + _spoilerIndex, + Type::Spoiler); } ++_waitingEntity; @@ -546,6 +560,22 @@ bool Parser::checkEntities() { 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() { while (_waitingEntity != _entitiesEnd && _start + _waitingEntity->offset() + _waitingEntity->length() <= _ptr) { @@ -737,11 +767,17 @@ void Parser::trimSourceRange() { void Parser::finalize(const TextParseOptions &options) { _t->_links.resize(_maxLnkIndex + _maxShiftedLnkIndex); + auto counterCustomIndex = uint16(0); auto currentIndex = uint16(0); // Current the latest index of _t->_links. struct { uint16 mono = 0; uint16 lnk = 0; } lastHandlerIndex; + const auto avoidIntersectionsWithCustom = [&] { + while (ranges::contains(_linksIndexes, currentIndex)) { + currentIndex++; + } + }; for (auto &block : _t->_blocks) { const auto spoilerIndex = block->spoilerIndex(); if (spoilerIndex && (_t->_spoilers.size() < spoilerIndex)) { @@ -752,6 +788,7 @@ void Parser::finalize(const TextParseOptions &options) { _t->setSpoiler(spoilerIndex, std::move(handler)); } const auto shiftedIndex = block->lnkIndex(); + auto useCustomIndex = false; if (shiftedIndex <= kStringLinkIndexShift) { if (IsMono(block->flags()) && shiftedIndex) { const auto monoIndex = shiftedIndex; @@ -762,6 +799,7 @@ void Parser::finalize(const TextParseOptions &options) { } else { currentIndex++; } + avoidIntersectionsWithCustom(); block->setLnkIndex(currentIndex); const auto handler = Integration::Instance().createLinkHandler( _monos[monoIndex - 1], @@ -771,24 +809,38 @@ void Parser::finalize(const TextParseOptions &options) { _t->setLink(currentIndex, handler); } 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) { - block->setLnkIndex(currentIndex); + block->setLnkIndex(usedIndex()); continue; // Optimization. } 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( _links[realIndex - 1], _context); if (handler) { - _t->setLink(currentIndex, handler); + _t->setLink(usedIndex(), handler); } lastHandlerIndex.lnk = realIndex; } diff --git a/ui/text/text_utilities.cpp b/ui/text/text_utilities.cpp index c93a321..c6b36a7 100644 --- a/ui/text/text_utilities.cpp +++ b/ui/text/text_utilities.cpp @@ -42,14 +42,22 @@ TextWithEntities Link(const QString &text, const QString &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) { 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.begin(), - { type, 0, int(text.text.size()), {} }); + { type, 0, int(text.text.size()), data }); return text; } diff --git a/ui/text/text_utilities.h b/ui/text/text_utilities.h index 3b2eddf..e41b9a8 100644 --- a/ui/text/text_utilities.h +++ b/ui/text/text_utilities.h @@ -30,10 +30,12 @@ inline constexpr auto Upper = details::ToUpperType{}; [[nodiscard]] TextWithEntities Link( const QString &text, const QString &url = "internal:action"); +[[nodiscard]] TextWithEntities Link(const QString &text, int index); [[nodiscard]] TextWithEntities PlainLink(const QString &text); [[nodiscard]] TextWithEntities Wrapped( TextWithEntities text, - EntityType type); + EntityType type, + const QString &data = QString()); [[nodiscard]] TextWithEntities RichLangValue(const QString &text); [[nodiscard]] inline TextWithEntities WithEntities(const QString &text) { return { text };