diff --git a/ui/click_handler.cpp b/ui/click_handler.cpp index a40dc6e..c96abe9 100644 --- a/ui/click_handler.cpp +++ b/ui/click_handler.cpp @@ -158,8 +158,10 @@ void ActivateClickHandler( not_null guard, ClickHandlerPtr handler, ClickContext context) { - crl::on_main(guard, [=] { - handler->onClick(context); + crl::on_main(guard, [=, weak = std::weak_ptr(handler)] { + if (const auto strong = weak.lock()) { + strong->onClick(context); + } }); } diff --git a/ui/click_handler.h b/ui/click_handler.h index bc54071..5f87046 100644 --- a/ui/click_handler.h +++ b/ui/click_handler.h @@ -29,7 +29,7 @@ protected: }; -enum class EntityType; +enum class EntityType : uchar; class ClickHandler { public: virtual ~ClickHandler() { diff --git a/ui/integration.cpp b/ui/integration.cpp index 68db7c1..b2bb3bf 100644 --- a/ui/integration.cpp +++ b/ui/integration.cpp @@ -43,14 +43,19 @@ void Integration::startFontsEnd() { } std::shared_ptr Integration::createLinkHandler( - EntityType type, - const QString &text, - const QString &data, - const TextParseOptions &options) { - switch (type) { + const EntityLinkData &data, + const std::any &context) { + switch (data.type) { case EntityType::CustomUrl: - return !data.isEmpty() - ? std::make_shared(data, false) + return !data.data.isEmpty() + ? std::make_shared(data.data, false) + : nullptr; + case EntityType::Email: + case EntityType::Url: + return !data.data.isEmpty() + ? std::make_shared( + data.data, + data.shown == EntityLinkShown::Full) : nullptr; } return nullptr; diff --git a/ui/integration.h b/ui/integration.h index c01cba5..67fc358 100644 --- a/ui/integration.h +++ b/ui/integration.h @@ -8,6 +8,8 @@ #include "base/basic_types.h" +#include + // Methods that must be implemented outside lib_ui. class QString; @@ -16,7 +18,7 @@ class QVariant; struct TextParseOptions; class ClickHandler; -enum class EntityType; +struct EntityLinkData; namespace Ui { namespace Emoji { @@ -43,10 +45,8 @@ public: virtual void startFontsEnd(); [[nodiscard]] virtual std::shared_ptr createLinkHandler( - EntityType type, - const QString &text, - const QString &data, - const TextParseOptions &options); + const EntityLinkData &data, + const std::any &context); [[nodiscard]] virtual bool handleUrlClick( const QString &url, const QVariant &context); diff --git a/ui/text/text.cpp b/ui/text/text.cpp index ca49fdc..3acc621 100644 --- a/ui/text/text.cpp +++ b/ui/text/text.cpp @@ -263,33 +263,18 @@ public: Parser( not_null string, const QString &text, - const TextParseOptions &options); + const TextParseOptions &options, + const std::any &context); Parser( not_null string, const TextWithEntities &textWithEntities, - const TextParseOptions &options); + const TextParseOptions &options, + const std::any &context); private: struct ReadyToken { }; - enum LinkDisplayStatus { - LinkDisplayedFull, - LinkDisplayedElided, - }; - - struct TextLinkData { - TextLinkData() = default; - TextLinkData( - EntityType type, - const QString &text, - const QString &data, - LinkDisplayStatus displayStatus); - EntityType type = EntityType::Invalid; - QString text, data; - LinkDisplayStatus displayStatus = LinkDisplayedFull; - }; - class StartedEntity { public: explicit StartedEntity(TextBlockFlags flags); @@ -307,6 +292,7 @@ private: not_null string, TextWithEntities &&source, const TextParseOptions &options, + const std::any &context, ReadyToken); void trimSourceRange(); @@ -336,14 +322,11 @@ private: void computeLinkText( const QString &linkData, QString *outLinkText, - LinkDisplayStatus *outDisplayStatus); - - static ClickHandlerPtr CreateHandlerForLink( - const TextLinkData &link, - const TextParseOptions &options); + EntityLinkShown *outShown); const not_null _t; const TextWithEntities _source; + const std::any &_context; const QChar * const _start = nullptr; const QChar *_end = nullptr; // mutable, because we trim by decrementing. const QChar *_ptr = nullptr; @@ -355,7 +338,7 @@ 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 _links; + std::vector _links; base::flat_map< const QChar*, std::vector> _startedEntities; @@ -379,17 +362,6 @@ private: }; -Parser::TextLinkData::TextLinkData( - EntityType type, - const QString &text, - const QString &data, - LinkDisplayStatus displayStatus) -: type(type) -, text(text) -, data(data) -, displayStatus(displayStatus) { -} - Parser::StartedEntity::StartedEntity(TextBlockFlags flags) : _value(flags) { Expects(_value >= 0 && _value < int(kStringLinkIndexShift)); } @@ -415,22 +387,26 @@ std::optional Parser::StartedEntity::lnkIndex() const { Parser::Parser( not_null string, const QString &text, - const TextParseOptions &options) + const TextParseOptions &options, + const std::any &context) : Parser( string, PrepareRichFromPlain(text, options), options, + context, ReadyToken()) { } Parser::Parser( not_null string, const TextWithEntities &textWithEntities, - const TextParseOptions &options) + const TextParseOptions &options, + const std::any &context) : Parser( string, PrepareRichFromRich(textWithEntities, options), options, + context, ReadyToken()) { } @@ -438,9 +414,11 @@ Parser::Parser( not_null string, TextWithEntities &&source, const TextParseOptions &options, + const std::any &context, ReadyToken) : _t(string) , _source(std::move(source)) +, _context(context) , _start(_source.text.constData()) , _end(_start + _source.text.size()) , _ptr(_start) @@ -552,7 +530,7 @@ bool Parser::checkEntities() { } auto flags = TextBlockFlags(); - auto link = TextLinkData(); + auto link = EntityLinkData(); const auto entityType = _waitingEntity->type(); const auto entityLength = _waitingEntity->length(); const auto entityBegin = _start + _waitingEntity->offset(); @@ -584,7 +562,7 @@ bool Parser::checkEntities() { link.type = entityType; link.data = QString(entityBegin, entityLength); if (link.type == EntityType::Url) { - computeLinkText(link.data, &link.text, &link.displayStatus); + computeLinkText(link.data, &link.text, &link.shown); } else { link.text = link.data; } @@ -745,7 +723,10 @@ bool Parser::readCommand() { case TextCommandLinkText: { createBlock(); int32 len = _ptr->unicode(); - _links.emplace_back(EntityType::CustomUrl, QString(), QString(++_ptr, len), LinkDisplayedFull); + _links.push_back(EntityLinkData{ + .data = QString(++_ptr, len), + .type = EntityType::CustomUrl + }); _lnkIndex = kStringLinkIndexShift + _links.size(); } break; @@ -942,9 +923,9 @@ void Parser::finalize(const TextParseOptions &options) { } _t->_links.resize(index); - const auto handler = CreateHandlerForLink( + const auto handler = Integration::Instance().createLinkHandler( _links[realIndex - 1], - options); + _context); if (handler) { _t->setLink(index, handler); } @@ -954,7 +935,10 @@ void Parser::finalize(const TextParseOptions &options) { _t->_text.squeeze(); } -void Parser::computeLinkText(const QString &linkData, QString *outLinkText, LinkDisplayStatus *outDisplayStatus) { +void Parser::computeLinkText( + const QString &linkData, + QString *outLinkText, + EntityLinkShown *outShown) { auto url = QUrl(linkData); auto good = QUrl(url.isValid() ? url.toEncoded() @@ -963,28 +947,9 @@ void Parser::computeLinkText(const QString &linkData, QString *outLinkText, Link ? good.toDisplayString() : linkData; *outLinkText = _t->_st->font->elided(readable, st::linkCropLimit); - *outDisplayStatus = (*outLinkText == readable) ? LinkDisplayedFull : LinkDisplayedElided; -} - -ClickHandlerPtr Parser::CreateHandlerForLink( - const TextLinkData &link, - const TextParseOptions &options) { - const auto result = Integration::Instance().createLinkHandler( - link.type, - link.text, - link.data, - options); - if (result) { - return result; - } - switch (link.type) { - case EntityType::Email: - case EntityType::Url: - return std::make_shared( - link.data, - link.displayStatus == LinkDisplayedFull); - } - return nullptr; + *outShown = (*outLinkText == readable) + ? EntityLinkShown::Full + : EntityLinkShown::Partial; } namespace { @@ -2707,7 +2672,7 @@ void String::setText(const style::TextStyle &st, const QString &text, const Text _st = &st; clear(); { - Parser parser(this, text, options); + Parser parser(this, text, options, {}); } recountNaturalSize(true, options.dir); } @@ -2842,7 +2807,7 @@ int String::countMaxMonospaceWidth() const { return result.ceil().toInt(); } -void String::setMarkedText(const style::TextStyle &st, const TextWithEntities &textWithEntities, const TextParseOptions &options) { +void String::setMarkedText(const style::TextStyle &st, const TextWithEntities &textWithEntities, const TextParseOptions &options, const std::any &context) { _st = &st; clear(); { @@ -2870,9 +2835,9 @@ void String::setMarkedText(const style::TextStyle &st, const TextWithEntities &t // } // } // newText.append("},\n\n").append(text); -// Parser parser(this, { newText, EntitiesInText() }, options); +// Parser parser(this, { newText, EntitiesInText() }, options, context); - Parser parser(this, textWithEntities, options); + Parser parser(this, textWithEntities, options, context); } recountNaturalSize(true, options.dir); } diff --git a/ui/text/text.h b/ui/text/text.h index 9019990..75bc9c0 100644 --- a/ui/text/text.h +++ b/ui/text/text.h @@ -13,6 +13,7 @@ #include "base/flags.h" #include +#include static const QChar TextCommand(0x0010); enum TextCommands { @@ -126,7 +127,7 @@ public: void countLineWidths(int width, QVector *lineWidths, bool breakEverywhere = false) const; void setText(const style::TextStyle &st, const QString &text, const TextParseOptions &options = _defaultOptions); void setRichText(const style::TextStyle &st, const QString &text, TextParseOptions options = _defaultOptions); - void setMarkedText(const style::TextStyle &st, const TextWithEntities &textWithEntities, const TextParseOptions &options = _defaultOptions); + void setMarkedText(const style::TextStyle &st, const TextWithEntities &textWithEntities, const TextParseOptions &options = _defaultOptions, const std::any &context = {}); void setLink(uint16 lnkIndex, const ClickHandlerPtr &lnk); bool hasLinks() const; diff --git a/ui/text/text_entity.h b/ui/text/text_entity.h index 183ca77..a8916dc 100644 --- a/ui/text/text_entity.h +++ b/ui/text/text_entity.h @@ -12,7 +12,7 @@ #include #include -enum class EntityType { +enum class EntityType : uchar { Invalid = 0, Url, @@ -33,6 +33,18 @@ enum class EntityType { Pre, // block }; +enum class EntityLinkShown : uchar { + Full, + Partial, +}; + +struct EntityLinkData { + QString text; + QString data; + EntityType type = EntityType::Invalid; + EntityLinkShown shown = EntityLinkShown::Full; +}; + class EntityInText; using EntitiesInText = QList;