Added support for custom indexes of links in Ui::String::Text.
This commit is contained in:
parent
8618812fd6
commit
7e1effeeeb
3 changed files with 85 additions and 23 deletions
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 };
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue