From dbcb6a35b0cf47bac83d596a239a61b0084f9f4c Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 8 Jun 2020 11:57:05 +0400 Subject: [PATCH 1/8] Fix possible crash in InnerDropdown hiding. --- ui/widgets/inner_dropdown.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ui/widgets/inner_dropdown.cpp b/ui/widgets/inner_dropdown.cpp index 0c793c8..98f40f5 100644 --- a/ui/widgets/inner_dropdown.cpp +++ b/ui/widgets/inner_dropdown.cpp @@ -247,15 +247,16 @@ void InnerDropdown::hideFinished() { void InnerDropdown::prepareCache() { if (_a_opacity.animating()) return; + const auto animating = _a_show.animating(); auto showAnimation = base::take(_a_show); auto showAnimationData = base::take(_showAnimation); showChildren(); _cache = GrabWidget(this); - _showAnimation = base::take(showAnimationData); - _a_show = base::take(showAnimation); - if (_a_show.animating()) { + if (animating) { hideChildren(); } + _showAnimation = base::take(showAnimationData); + _a_show = base::take(showAnimation); } void InnerDropdown::startOpacityAnimation(bool hiding) { From 4ef97b57f6925ac9f1e3221676f49dffb4fe8c19 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 8 Jun 2020 12:13:15 +0400 Subject: [PATCH 2/8] Guard against inputMethodEvent destroying the field. --- ui/widgets/input_fields.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/ui/widgets/input_fields.cpp b/ui/widgets/input_fields.cpp index 23abd56..c435025 100644 --- a/ui/widgets/input_fields.cpp +++ b/ui/widgets/input_fields.cpp @@ -2901,10 +2901,15 @@ void InputField::inputMethodEventInner(QInputMethodEvent *e) { startPlaceholderAnimation(); } _inputMethodCommit = e->commitString(); + + const auto weak = Ui::MakeWeak(this); _inner->QTextEdit::inputMethodEvent(e); - const auto text = *base::take(_inputMethodCommit); - if (!processMarkdownReplaces(text)) { - processInstantReplaces(text); + + if (weak) { + const auto text = *base::take(_inputMethodCommit); + if (!processMarkdownReplaces(text)) { + processInstantReplaces(text); + } } } From 771f7020566a6dce1c81083a69ceaeb57909b25a Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 8 Jun 2020 22:08:09 +0400 Subject: [PATCH 3/8] Remove a slot from BoxContent. --- ui/layers/box_content.cpp | 2 +- ui/layers/box_content.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ui/layers/box_content.cpp b/ui/layers/box_content.cpp index c2b765f..bb6d3e2 100644 --- a/ui/layers/box_content.cpp +++ b/ui/layers/box_content.cpp @@ -101,7 +101,7 @@ void BoxContent::onScrollToY(int top, int bottom) { } } -void BoxContent::onDraggingScrollDelta(int delta) { +void BoxContent::scrollByDraggingDelta(int delta) { _draggingScrollDelta = _scroll ? delta : 0; if (_draggingScrollDelta) { if (!_draggingScrollTimer) { diff --git a/ui/layers/box_content.h b/ui/layers/box_content.h index 28426d5..f293fe4 100644 --- a/ui/layers/box_content.h +++ b/ui/layers/box_content.h @@ -192,11 +192,11 @@ public: getDelegate()->setNoContentMargin(noContentMargin); } + void scrollByDraggingDelta(int delta); + public slots: void onScrollToY(int top, int bottom = -1); - void onDraggingScrollDelta(int delta); - protected: virtual void prepare() = 0; From ab61721a134d4dbea28fc79ab55eb9b18e992658 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 9 Jun 2020 13:04:44 +0400 Subject: [PATCH 4/8] Allow passing std::any context to link creation method. --- ui/click_handler.cpp | 6 ++- ui/click_handler.h | 2 +- ui/integration.cpp | 19 +++++--- ui/integration.h | 10 ++-- ui/text/text.cpp | 105 ++++++++++++++---------------------------- ui/text/text.h | 3 +- ui/text/text_entity.h | 14 +++++- 7 files changed, 72 insertions(+), 87 deletions(-) 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; From f749fc734978f4060d4d77ae36f279d1f2c8aac5 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Sat, 13 Jun 2020 05:22:55 +0400 Subject: [PATCH 5/8] Use Q_OS_UNIX instead of Q_OS_LINUX since linux-specific code can be used also on *BSD/Haiku --- ui/platform/ui_platform_utility.h | 6 +++--- ui/style/style_core_font.cpp | 4 ++-- ui/widgets/labels.cpp | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ui/platform/ui_platform_utility.h b/ui/platform/ui_platform_utility.h index 4398ed7..0e23059 100644 --- a/ui/platform/ui_platform_utility.h +++ b/ui/platform/ui_platform_utility.h @@ -37,8 +37,8 @@ void DrainMainQueue(); // Needed only if UseMainQueueGeneric() is false. #ifdef Q_OS_MAC #include "ui/platform/mac/ui_utility_mac.h" -#elif defined Q_OS_LINUX // Q_OS_MAC +#elif defined Q_OS_UNIX // Q_OS_MAC #include "ui/platform/linux/ui_utility_linux.h" -#elif defined Q_OS_WINRT || defined Q_OS_WIN // Q_OS_MAC || Q_OS_LINUX +#elif defined Q_OS_WINRT || defined Q_OS_WIN // Q_OS_MAC || Q_OS_UNIX #include "ui/platform/win/ui_utility_win.h" -#endif // Q_OS_MAC || Q_OS_LINUX || Q_OS_WINRT || Q_OS_WIN +#endif // Q_OS_MAC || Q_OS_UNIX || Q_OS_WINRT || Q_OS_WIN diff --git a/ui/style/style_core_font.cpp b/ui/style/style_core_font.cpp index 6da92d9..83310a4 100644 --- a/ui/style/style_core_font.cpp +++ b/ui/style/style_core_font.cpp @@ -25,9 +25,9 @@ void style_InitFontsResource() { Q_INIT_RESOURCE(win); #elif defined Q_OS_MAC // Q_OS_WIN Q_INIT_RESOURCE(mac); -#elif defined Q_OS_LINUX && !defined DESKTOP_APP_USE_PACKAGED // Q_OS_WIN || Q_OS_MAC +#elif defined Q_OS_UNIX && !defined DESKTOP_APP_USE_PACKAGED // Q_OS_WIN || Q_OS_MAC Q_INIT_RESOURCE(linux); -#endif // Q_OS_WIN || Q_OS_MAC || (Q_OS_LINUX && !DESKTOP_APP_USE_PACKAGED) +#endif // Q_OS_WIN || Q_OS_MAC || (Q_OS_UNIX && !DESKTOP_APP_USE_PACKAGED) } namespace style { diff --git a/ui/widgets/labels.cpp b/ui/widgets/labels.cpp index 2e01081..a5c58fd 100644 --- a/ui/widgets/labels.cpp +++ b/ui/widgets/labels.cpp @@ -454,13 +454,13 @@ Text::StateResult FlatLabel::dragActionFinish(const QPoint &p, Qt::MouseButton b } } -#if defined Q_OS_LINUX32 || defined Q_OS_LINUX64 +#if defined Q_OS_UNIX && !defined Q_OS_MAC if (!_selection.empty()) { TextUtilities::SetClipboardText( _text.toTextForMimeData(_selection), QClipboard::Selection); } -#endif // Q_OS_LINUX32 || Q_OS_LINUX64 +#endif // Q_OS_UNIX && !Q_OS_MAC return state; } From 4e6763d1769e6305a3da158e1a332ccd6a30398a Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 16 Jun 2020 19:09:57 +0400 Subject: [PATCH 6/8] Remove old ways to customize mentions. --- ui/text/text_entity.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/ui/text/text_entity.h b/ui/text/text_entity.h index a8916dc..8003626 100644 --- a/ui/text/text_entity.h +++ b/ui/text/text_entity.h @@ -238,11 +238,6 @@ enum { TextParseHashtags = 0x010, TextParseBotCommands = 0x020, TextParseMarkdown = 0x040, - - TextTwitterMentions = 0x100, - TextTwitterHashtags = 0x200, - TextInstagramMentions = 0x400, - TextInstagramHashtags = 0x800, }; struct TextWithTags { From d31d94e8fb47b8492fbf341b93c19ddfbcb42fa2 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 22 Jun 2020 16:50:30 +0400 Subject: [PATCH 7/8] Allow external RippleButton customization. --- ui/widgets/buttons.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/widgets/buttons.h b/ui/widgets/buttons.h index e010c70..d654d2f 100644 --- a/ui/widgets/buttons.h +++ b/ui/widgets/buttons.h @@ -58,11 +58,11 @@ public: void clearState() override; + void paintRipple(QPainter &p, int x, int y, const QColor *colorOverride = nullptr); + ~RippleButton(); protected: - void paintRipple(QPainter &p, int x, int y, const QColor *colorOverride = nullptr); - void onStateChanged(State was, StateChangeSource source) override; virtual QImage prepareRippleMask() const; From 2a0d189ee22d8573bd4f9f0971ef1aa27a12f02d Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 23 Jun 2020 13:13:22 +0400 Subject: [PATCH 8/8] Allow badge in main menu button. --- ui/layers/layer_widget.cpp | 2 +- ui/widgets/side_bar_button.cpp | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/ui/layers/layer_widget.cpp b/ui/layers/layer_widget.cpp index 1dd165d..66a47db 100644 --- a/ui/layers/layer_widget.cpp +++ b/ui/layers/layer_widget.cpp @@ -473,7 +473,7 @@ void LayerStackWidget::closeLayer(not_null layer) { return; } - if (layer == _specialLayer) { + if (layer == _specialLayer || layer == _mainMenu) { hideAll(anim::type::normal); } else if (layer == currentLayer()) { if (_layers.size() == 1) { diff --git a/ui/widgets/side_bar_button.cpp b/ui/widgets/side_bar_button.cpp index 193d23a..654be8c 100644 --- a/ui/widgets/side_bar_button.cpp +++ b/ui/widgets/side_bar_button.cpp @@ -145,7 +145,6 @@ const style::icon &SideBarButton::computeIcon() const { void SideBarButton::validateIconCache() { Expects(_st.iconPosition.x() < 0); - Expects(_st.iconPosition.y() >= 0); if (!(_active ? _iconCacheActive : _iconCache).isNull()) { return; @@ -173,7 +172,10 @@ void SideBarButton::validateIconCache() { - st::defaultScrollArea.width - (width() / 2) + (icon.width() / 2))); - const auto y = _st.badgePosition.y() - _st.iconPosition.y(); + const auto top = (_st.iconPosition.y() >= 0) + ? _st.iconPosition.y() + : (height() - icon.height()) / 2; + const auto y = _st.badgePosition.y() - top; const auto r = _st.badgeHeight / 2.; p.drawRoundedRect(x, y, _iconCacheBadgeWidth, _st.badgeHeight, r, r); }