From 8d8d56112735df6e3a5304214f52191d271b1807 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 7 Apr 2020 13:58:24 +0400 Subject: [PATCH] Support rich text in toasts. --- ui/basic.style | 4 +++ ui/text/text_entity.cpp | 6 +++++ ui/text/text_entity.h | 1 + ui/toast/toast.cpp | 4 +-- ui/toast/toast.h | 3 ++- ui/toast/toast_widget.cpp | 57 +++++++++++++++++++++++++++++++++++++-- ui/toast/toast_widget.h | 5 ++++ 7 files changed, 75 insertions(+), 5 deletions(-) diff --git a/ui/basic.style b/ui/basic.style index a43d42c..25f086a 100644 --- a/ui/basic.style +++ b/ui/basic.style @@ -271,6 +271,10 @@ inlineResultsMinWidth: 48px; inlineDurationMargin: 3px; toastTextStyle: defaultTextStyle; +toastTextPalette: TextPalette(defaultTextPalette) { + linkFg: mediaviewTextLinkFg; + monoFg: mediaviewCaptionFg; +} toastMaxWidth: 480px; toastMinMargin: 13px; toastPadding: margins(19px, 13px, 19px, 12px); diff --git a/ui/text/text_entity.cpp b/ui/text/text_entity.cpp index e9f95e4..c1c43c2 100644 --- a/ui/text/text_entity.cpp +++ b/ui/text/text_entity.cpp @@ -1321,6 +1321,12 @@ QString SingleLine(const QString &text) { return result; } +TextWithEntities SingleLine(const TextWithEntities &text) { + auto copy = text; + Trim(copy); + return { SingleLine(copy.text), std::move(copy.entities) }; +} + QString RemoveAccents(const QString &text) { auto result = text; auto copying = false; diff --git a/ui/text/text_entity.h b/ui/text/text_entity.h index 7d421bb..0bef028 100644 --- a/ui/text/text_entity.h +++ b/ui/text/text_entity.h @@ -285,6 +285,7 @@ QString MarkdownPreBadAfter(); QString Clean(const QString &text); QString EscapeForRichParsing(const QString &text); QString SingleLine(const QString &text); +TextWithEntities SingleLine(const TextWithEntities &text); QString RemoveAccents(const QString &text); QString RemoveEmoji(const QString &text); QStringList PrepareSearchWords(const QString &query, const QRegularExpression *SplitterOverride = nullptr); diff --git a/ui/toast/toast.cpp b/ui/toast/toast.cpp index 69cfc01..8c5075b 100644 --- a/ui/toast/toast.cpp +++ b/ui/toast/toast.cpp @@ -50,13 +50,13 @@ void Show(const Config &config) { void Show(not_null parent, const QString &text) { auto config = Config(); - config.text = text; + config.text = { text }; Show(parent, config); } void Show(const QString &text) { auto config = Config(); - config.text = text; + config.text = { text }; Show(config); } diff --git a/ui/toast/toast.h b/ui/toast/toast.h index 6ced4e9..4094b7e 100644 --- a/ui/toast/toast.h +++ b/ui/toast/toast.h @@ -7,6 +7,7 @@ #pragma once #include "ui/effects/animations.h" +#include "ui/text/text_entity.h" namespace Ui { namespace Toast { @@ -18,7 +19,7 @@ class Widget; inline constexpr auto kDefaultDuration = crl::time(1500); struct Config { - QString text; + TextWithEntities text; QMargins padding; crl::time durationMs = kDefaultDuration; int minWidth = 0; diff --git a/ui/toast/toast_widget.cpp b/ui/toast/toast_widget.cpp index 962ca5d..5181cf8 100644 --- a/ui/toast/toast_widget.cpp +++ b/ui/toast/toast_widget.cpp @@ -9,6 +9,8 @@ #include "ui/image/image_prepare.h" #include "styles/palette.h" +#include + namespace Ui { namespace Toast { namespace internal { @@ -29,12 +31,16 @@ Widget::Widget(QWidget *parent, const Config &config) _maxTextHeight, Qt::LayoutDirectionAuto }; - _text.setText( + _text.setMarkedText( st::toastTextStyle, _multiline ? config.text : TextUtilities::SingleLine(config.text), toastOptions); - setAttribute(Qt::WA_TransparentForMouseEvents); + if (_text.hasLinks()) { + setMouseTracking(true); + } else { + setAttribute(Qt::WA_TransparentForMouseEvents); + } onParentResized(); show(); @@ -63,11 +69,58 @@ void Widget::paintEvent(QPaintEvent *e) { p.setOpacity(_shownLevel); _roundRect.paint(p, rect()); + p.setTextPalette(st::toastTextPalette); + const auto lines = _maxTextHeight / st::toastTextStyle.font->height; p.setPen(st::toastFg); _text.drawElided(p, _padding.left(), _padding.top(), _textWidth + 1, lines); } +void Widget::leaveEventHook(QEvent *e) { + if (!_text.hasLinks()) { + return; + } + if (ClickHandler::getActive()) { + ClickHandler::setActive(nullptr); + setCursor(style::cur_default); + update(); + } +} + +void Widget::mouseMoveEvent(QMouseEvent *e) { + if (!_text.hasLinks()) { + return; + } + const auto point = e->pos() - QPoint(_padding.left(), _padding.top()); + const auto lines = _maxTextHeight / st::toastTextStyle.font->height; + const auto state = _text.getStateElided(point, _textWidth + 1); + const auto was = ClickHandler::getActive(); + if (was != state.link) { + ClickHandler::setActive(state.link); + if ((was != nullptr) != (state.link != nullptr)) { + setCursor(was ? style::cur_default : style::cur_pointer); + } + update(); + } +} + +void Widget::mousePressEvent(QMouseEvent *e) { + if (!_text.hasLinks() || e->button() != Qt::LeftButton) { + return; + } + ClickHandler::pressed(); +} + +void Widget::mouseReleaseEvent(QMouseEvent *e) { + if (!_text.hasLinks() || e->button() != Qt::LeftButton) { + return; + } + if (const auto handler = ClickHandler::unpressed()) { + handler->onClick({ e->button() }); + } +} + + } // namespace internal } // namespace Toast } // namespace Ui diff --git a/ui/toast/toast_widget.h b/ui/toast/toast_widget.h index 0dd8f69..a29d274 100644 --- a/ui/toast/toast_widget.h +++ b/ui/toast/toast_widget.h @@ -27,6 +27,11 @@ public: protected: void paintEvent(QPaintEvent *e) override; + void leaveEventHook(QEvent *e) override; + void mouseMoveEvent(QMouseEvent *e) override; + void mousePressEvent(QMouseEvent *e) override; + void mouseReleaseEvent(QMouseEvent *e) override; + private: int widthWithoutPadding(int w) { return w - _padding.left() - _padding.right();