From 8d8d56112735df6e3a5304214f52191d271b1807 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 7 Apr 2020 13:58:24 +0400 Subject: [PATCH 1/7] 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(); From 74889b8c2131adcf2a370cfdd485303049c85b44 Mon Sep 17 00:00:00 2001 From: ilya-fedin Date: Thu, 30 Jan 2020 11:43:51 +0400 Subject: [PATCH 2/7] Better algorithm for font choosing --- ui/basic.style | 2 - ui/style/style_core.cpp | 29 -------- ui/style/style_core.h | 2 - ui/style/style_core_font.cpp | 135 +++++++++++++++++++++++++---------- ui/style/style_core_font.h | 8 ++- ui/text/text.cpp | 12 +--- ui/text/text_block.cpp | 12 +--- ui/widgets/input_fields.cpp | 23 +----- 8 files changed, 114 insertions(+), 109 deletions(-) diff --git a/ui/basic.style b/ui/basic.style index 25f086a..9d0a66a 100644 --- a/ui/basic.style +++ b/ui/basic.style @@ -23,8 +23,6 @@ TextStyle { lineHeight: pixels; } -semibold: "Open Sans Semibold"; - fsize: 13px; normalFont: font(fsize); semiboldFont: font(fsize semibold); diff --git a/ui/style/style_core.cpp b/ui/style/style_core.cpp index 18a5bbb..171b390 100644 --- a/ui/style/style_core.cpp +++ b/ui/style/style_core.cpp @@ -12,7 +12,6 @@ #include "styles/palette.h" #include -#include #include #include @@ -28,7 +27,6 @@ constexpr auto kContrastDeltaL = 64; auto PaletteChanges = rpl::event_stream<>(); auto ShortAnimationRunning = rpl::variable(false); auto RunningShortAnimations = 0; -auto ResolvedMonospaceFont = style::font(); std::vector &StyleModules() { static auto result = std::vector(); @@ -41,28 +39,6 @@ void startModules(int scale) { } } -void ResolveMonospaceFont() { - auto family = QString(); - const auto tryFont = [&](const QString &attempt) { - if (family.isEmpty() - && !QFontInfo(QFont(attempt)).family().trimmed().compare( - attempt, - Qt::CaseInsensitive)) { - family = attempt; - } - }; - tryFont("Consolas"); - tryFont("Liberation Mono"); - tryFont("Menlo"); - tryFont("Courier"); - if (family.isEmpty()) { - const auto type = QFontDatabase::FixedFont; - family = QFontDatabase::systemFont(type).family(); - } - const auto size = st::normalFont->f.pixelSize(); - ResolvedMonospaceFont = style::font(size, 0, family); -} - } // namespace void registerModule(ModuleBase *module) { @@ -86,7 +62,6 @@ void StopShortAnimation() { void startManager(int scale) { internal::registerFontFamily("Open Sans"); internal::startModules(scale); - internal::ResolveMonospaceFont(); } void stopManager() { @@ -106,10 +81,6 @@ rpl::producer ShortAnimationPlaying() { return internal::ShortAnimationRunning.value(); } -const style::font &MonospaceFont() { - return internal::ResolvedMonospaceFont; -} - void colorizeImage(const QImage &src, QColor c, QImage *outResult, QRect srcRect, QPoint dstPoint) { // In background_box ColorizePattern we use the fact that // colorizeImage takes only first byte of the mask, so it diff --git a/ui/style/style_core.h b/ui/style/style_core.h index 62da03c..d996342 100644 --- a/ui/style/style_core.h +++ b/ui/style/style_core.h @@ -43,8 +43,6 @@ void NotifyPaletteChanged(); [[nodiscard]] rpl::producer ShortAnimationPlaying(); -const style::font &MonospaceFont(); - // *outResult must be r.width() x r.height(), ARGB32_Premultiplied. // QRect(0, 0, src.width(), src.height()) must contain r. void colorizeImage(const QImage &src, QColor c, QImage *outResult, QRect srcRect = QRect(), QPoint dstPoint = QPoint(0, 0)); diff --git a/ui/style/style_core_font.cpp b/ui/style/style_core_font.cpp index 318783b..08bb43b 100644 --- a/ui/style/style_core_font.cpp +++ b/ui/style/style_core_font.cpp @@ -14,6 +14,7 @@ #include #include #include +#include void style_InitFontsResource() { #ifndef DESKTOP_APP_USE_PACKAGED_FONTS @@ -37,16 +38,14 @@ QVector fontFamilies; QMap fontsMap; uint32 fontKey(int size, uint32 flags, int family) { - return (((uint32(family) << 10) | uint32(size)) << 4) | flags; + return (((uint32(family) << 12) | uint32(size)) << 6) | flags; } bool ValidateFont(const QString &familyName, int flags = 0) { QFont checkFont(familyName); - checkFont.setPixelSize(13); checkFont.setBold(flags & style::internal::FontBold); checkFont.setItalic(flags & style::internal::FontItalic); checkFont.setUnderline(flags & style::internal::FontUnderline); - checkFont.setStyleStrategy(QFont::PreferQuality); auto realFamily = QFontInfo(checkFont).family(); if (realFamily.trimmed().compare(familyName, Qt::CaseInsensitive)) { UI_LOG(("Font Error: could not resolve '%1' font, got '%2'.").arg(familyName).arg(realFamily)); @@ -86,6 +85,39 @@ bool LoadCustomFont(const QString &filePath, const QString &familyName, int flag return ValidateFont(familyName, flags); } +QString MonospaceFont() { + static const auto family = [&]() -> QString { +#ifdef Q_OS_WIN + const auto tryFont = [&](const QString &attempt) { + return !QFontInfo(QFont(attempt)).family().trimmed().compare( + attempt, + Qt::CaseInsensitive); + }; + + if (tryFont("Consolas")) { + return "Consolas"; + } + + if (tryFont("Liberation Mono")) { + return "Liberation Mono"; + } + + if (tryFont("Menlo")) { + return "Menlo"; + } + + if (tryFont("Courier")) { + return "Courier"; + } +#endif // Q_OS_WIN + + const auto type = QFontDatabase::FixedFont; + return QFontDatabase::systemFont(type).family(); + }(); + + return family; +} + enum { FontTypeRegular = 0, FontTypeRegularItalic, @@ -111,8 +143,8 @@ int32 FontTypeFlags[FontTypesCount] = { FontItalic, FontBold, FontBold | FontItalic, - 0, - FontItalic, + FontSemibold, + FontSemibold | FontItalic, }; #ifdef Q_OS_WIN QString FontTypeWindowsFallback[FontTypesCount] = { @@ -168,7 +200,18 @@ void StartFonts() { //QFont::insertSubstitution(name, fallback); #endif // Q_OS_WIN } -#endif // !DESKTOP_APP_USE_PACKAGED_FONTS + +#ifdef Q_OS_WIN + auto list = QStringList(); + list.append("Microsoft YaHei"); + list.append("Microsoft JhengHei UI"); + list.append("Yu Gothic UI"); + list.append("\xEB\xA7\x91\xEC\x9D\x80 \xEA\xB3\xA0\xEB\x94\x95"); + for (const auto &name : FontTypeNames) { + QFont::insertSubstitutions(name, list); + } +#endif // Q_OS_WIN + #ifdef Q_OS_MAC auto list = QStringList(); list.append("STIXGeneral"); @@ -179,36 +222,44 @@ void StartFonts() { QFont::insertSubstitutions(name, list); } #endif // Q_OS_MAC +#endif // !DESKTOP_APP_USE_PACKAGED_FONTS + + auto appFont = QFont(GetFontOverride()); + appFont.setPixelSize(13); + appFont.setStyleStrategy(QFont::PreferQuality); + QApplication::setFont(appFont); if (integrationExists) { Ui::Integration::Instance().startFontsEnd(); } } -QString GetPossibleEmptyOverride(const QString &familyName, int32 flags) { - flags = flags & (FontBold | FontItalic); - if (familyName == qstr("Open Sans")) { - if (flags == (FontBold | FontItalic)) { - return Overrides[FontTypeBoldItalic]; - } else if (flags == FontBold) { - return Overrides[FontTypeBold]; - } else if (flags == FontItalic) { - return Overrides[FontTypeRegularItalic]; - } else if (flags == 0) { - return Overrides[FontTypeRegular]; - } - } else if (familyName == qstr("Open Sans Semibold")) { - if (flags == FontItalic) { - return Overrides[FontTypeSemiboldItalic]; - } else if (flags == 0) { - return Overrides[FontTypeSemibold]; - } +QString GetPossibleEmptyOverride(int32 flags) { + flags = flags & (FontBold | FontSemibold | FontItalic); + int32 flagsBold = flags & (FontBold | FontItalic); + int32 flagsSemibold = flags & (FontSemibold | FontItalic); + if (flagsSemibold == (FontSemibold | FontItalic)) { + return Overrides[FontTypeSemiboldItalic]; + } else if (flagsSemibold == FontSemibold) { + return Overrides[FontTypeSemibold]; + } else if (flagsBold == (FontBold | FontItalic)) { + return Overrides[FontTypeBoldItalic]; + } else if (flagsBold == FontBold) { + return Overrides[FontTypeBold]; + } else if (flags == FontItalic) { + return Overrides[FontTypeRegularItalic]; + } else if (flags == 0) { + return Overrides[FontTypeRegular]; } return QString(); } -QString GetFontOverride(const QString &familyName, int32 flags) { - const auto result = GetPossibleEmptyOverride(familyName, flags); +QString GetFontOverride(int32 flags) { + const auto familyName = (flags & FontSemibold) + ? "Open Sans Semibold" + : "Open Sans"; + + const auto result = GetPossibleEmptyOverride(flags); return result.isEmpty() ? familyName : result; } @@ -230,8 +281,7 @@ int registerFontFamily(const QString &family) { } FontData::FontData(int size, uint32 flags, int family, Font *other) -: f(GetFontOverride(fontFamilies[family], flags)) -, m(f) +: m(f) , _size(size) , _flags(flags) , _family(family) { @@ -242,18 +292,23 @@ FontData::FontData(int size, uint32 flags, int family, Font *other) } modified[_flags] = Font(this); - f.setPixelSize(size); - if (_flags & FontBold) { - f.setBold(true); -#ifdef DESKTOP_APP_USE_PACKAGED_FONTS - } else if (fontFamilies[family] == "Open Sans Semibold") { - f.setWeight(QFont::DemiBold); -#endif + if (_flags & FontMonospace) { + f.setFamily(MonospaceFont()); + } else { + f.setFamily(GetFontOverride(flags)); } + +#ifdef DESKTOP_APP_USE_PACKAGED_FONTS + if (_flags & FontSemibold) { + f.setWeight(QFont::DemiBold); + } +#endif // DESKTOP_APP_USE_PACKAGED_FONTS + + f.setPixelSize(size); + f.setBold(_flags & FontBold); f.setItalic(_flags & FontItalic); f.setUnderline(_flags & FontUnderline); f.setStrikeOut(_flags & FontStrikeOut); - f.setStyleStrategy(QFont::PreferQuality); m = QFontMetrics(f); height = m.height(); @@ -279,6 +334,14 @@ Font FontData::strikeout(bool set) const { return otherFlagsFont(FontStrikeOut, set); } +Font FontData::semibold(bool set) const { + return otherFlagsFont(FontSemibold, set); +} + +Font FontData::monospace(bool set) const { + return otherFlagsFont(FontMonospace, set); +} + int FontData::size() const { return _size; } diff --git a/ui/style/style_core_font.h b/ui/style/style_core_font.h index 31b5962..29e5299 100644 --- a/ui/style/style_core_font.h +++ b/ui/style/style_core_font.h @@ -15,7 +15,7 @@ namespace style { namespace internal { void StartFonts(); -[[nodiscard]] QString GetFontOverride(const QString &familyName, int32 flags = 0); +[[nodiscard]] QString GetFontOverride(int32 flags = 0); void destroyFonts(); int registerFontFamily(const QString &family); @@ -59,8 +59,10 @@ enum FontFlags { FontItalic = 0x02, FontUnderline = 0x04, FontStrikeOut = 0x08, + FontSemibold = 0x10, + FontMonospace = 0x20, - FontDifferentFlags = 0x10, + FontDifferentFlags = 0x40, }; class FontData { @@ -83,6 +85,8 @@ public: Font italic(bool set = true) const; Font underline(bool set = true) const; Font strikeout(bool set = true) const; + Font semibold(bool set = true) const; + Font monospace(bool set = true) const; int size() const; uint32 flags() const; diff --git a/ui/text/text.cpp b/ui/text/text.cpp index a48abae..b4c61cd 100644 --- a/ui/text/text.cpp +++ b/ui/text/text.cpp @@ -2014,24 +2014,18 @@ private: } auto result = f; if ((flags & TextBlockFPre) || (flags & TextBlockFCode)) { - result = style::MonospaceFont(); - if (result->size() != f->size() || result->flags() != f->flags()) { - result = style::font(f->size(), f->flags(), result->family()); - } + result = result->monospace(); } else { if (flags & TextBlockFBold) { result = result->bold(); } else if (flags & TextBlockFSemibold) { - result = st::semiboldFont; - if (result->size() != f->size() || result->flags() != f->flags()) { - result = style::font(f->size(), f->flags(), result->family()); - } + result = result->semibold(); } if (flags & TextBlockFItalic) result = result->italic(); if (flags & TextBlockFUnderline) result = result->underline(); if (flags & TextBlockFStrikeOut) result = result->strikeout(); if (flags & TextBlockFTilde) { // tilde fix in OpenSans - result = st::semiboldFont; + result = result->semibold(); } } return result; diff --git a/ui/text/text_block.cpp b/ui/text/text_block.cpp index ffa378f..c2a3f11 100644 --- a/ui/text/text_block.cpp +++ b/ui/text/text_block.cpp @@ -322,24 +322,18 @@ TextBlock::TextBlock(const style::font &font, const QString &str, QFixed minResi } if ((flags & TextBlockFPre) || (flags & TextBlockFCode)) { - blockFont = style::MonospaceFont(); - if (blockFont->size() != font->size() || blockFont->flags() != font->flags()) { - blockFont = style::font(font->size(), font->flags(), blockFont->family()); - } + blockFont = blockFont->monospace(); } else { if (flags & TextBlockFBold) { blockFont = blockFont->bold(); } else if (flags & TextBlockFSemibold) { - blockFont = st::semiboldFont; - if (blockFont->size() != font->size() || blockFont->flags() != font->flags()) { - blockFont = style::font(font->size(), font->flags(), blockFont->family()); - } + blockFont = blockFont->semibold(); } if (flags & TextBlockFItalic) blockFont = blockFont->italic(); if (flags & TextBlockFUnderline) blockFont = blockFont->underline(); if (flags & TextBlockFStrikeOut) blockFont = blockFont->strikeout(); if (flags & TextBlockFTilde) { // tilde fix in OpenSans - blockFont = st::semiboldFont; + blockFont = blockFont->semibold(); } } diff --git a/ui/widgets/input_fields.cpp b/ui/widgets/input_fields.cpp index db17e0c..e37a125 100644 --- a/ui/widgets/input_fields.cpp +++ b/ui/widgets/input_fields.cpp @@ -651,15 +651,6 @@ void RemoveDocumentTags( cursor.mergeCharFormat(format); } -style::font AdjustFont( - const style::font &font, - const style::font &original) { - return (font->size() != original->size() - || font->flags() != original->flags()) - ? style::font(original->size(), original->flags(), font->family()) - : font; -} - bool IsValidMarkdownLink(const QString &link) { return (link.indexOf('.') >= 0) || (link.indexOf(':') >= 0); } @@ -672,16 +663,8 @@ QTextCharFormat PrepareTagFormat( result.setForeground(st::defaultTextPalette.linkFg); result.setFont(st.font); } else if (tag == kTagBold) { - auto semibold = st::semiboldFont; - if (semibold->size() != st.font->size() - || semibold->flags() != st.font->flags()) { - semibold = style::font( - st.font->size(), - st.font->flags(), - semibold->family()); - } result.setForeground(st.textFg); - result.setFont(AdjustFont(st::semiboldFont, st.font)); + result.setFont(st.font->semibold()); } else if (tag == kTagItalic) { result.setForeground(st.textFg); result.setFont(st.font->italic()); @@ -693,7 +676,7 @@ QTextCharFormat PrepareTagFormat( result.setFont(st.font->strikeout()); } else if (tag == kTagCode || tag == kTagPre) { result.setForeground(st::defaultTextPalette.monoFg); - result.setFont(AdjustFont(style::MonospaceFont(), st.font)); + result.setFont(st.font->monospace()); } else { result.setForeground(st.textFg); result.setFont(st.font); @@ -1985,7 +1968,7 @@ void InputField::processFormatting(int insertPosition, int insertEnd) { const auto tildeFormatting = (_st.font->f.pixelSize() * style::DevicePixelRatio() == 13) && (_st.font->f.family() == qstr("DAOpenSansRegular")); auto isTildeFragment = false; - const auto tildeFixedFont = AdjustFont(st::semiboldFont, _st.font); + const auto tildeFixedFont = _st.font->semibold(); // First tag handling (the one we inserted text to). bool startTagFound = false; From 3090ba77217279f438025abba46a553ab4fc464a Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 13 Apr 2020 17:12:46 +0400 Subject: [PATCH 3/7] Use predefined monospace fonts on macOS. --- ui/style/style_core_font.cpp | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/ui/style/style_core_font.cpp b/ui/style/style_core_font.cpp index 08bb43b..fcd2bf1 100644 --- a/ui/style/style_core_font.cpp +++ b/ui/style/style_core_font.cpp @@ -87,29 +87,20 @@ bool LoadCustomFont(const QString &filePath, const QString &familyName, int flag QString MonospaceFont() { static const auto family = [&]() -> QString { -#ifdef Q_OS_WIN - const auto tryFont = [&](const QString &attempt) { - return !QFontInfo(QFont(attempt)).family().trimmed().compare( - attempt, - Qt::CaseInsensitive); +#ifndef Q_OS_LINUX + const auto kTryFirst = std::initializer_list{ + "Consolas", + "Liberation Mono", + "Menlo", + "Courier" }; - - if (tryFont("Consolas")) { - return "Consolas"; + for (const auto &family : kTryFirst) { + const auto resolved = QFontInfo(QFont(family)).family(); + if (!resolved.trimmed().compare(family, Qt::CaseInsensitive)) { + return family; + } } - - if (tryFont("Liberation Mono")) { - return "Liberation Mono"; - } - - if (tryFont("Menlo")) { - return "Menlo"; - } - - if (tryFont("Courier")) { - return "Courier"; - } -#endif // Q_OS_WIN +#endif // !Q_OS_LINUX const auto type = QFontDatabase::FixedFont; return QFontDatabase::systemFont(type).family(); From 2cb10e1d13b49653f67474c13a3281a84ec82c03 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 15 Apr 2020 18:05:28 +0400 Subject: [PATCH 4/7] Use default QApplication::font. --- ui/style/style_core_font.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ui/style/style_core_font.cpp b/ui/style/style_core_font.cpp index fcd2bf1..0cd6580 100644 --- a/ui/style/style_core_font.cpp +++ b/ui/style/style_core_font.cpp @@ -215,8 +215,7 @@ void StartFonts() { #endif // Q_OS_MAC #endif // !DESKTOP_APP_USE_PACKAGED_FONTS - auto appFont = QFont(GetFontOverride()); - appFont.setPixelSize(13); + auto appFont = QApplication::font(); appFont.setStyleStrategy(QFont::PreferQuality); QApplication::setFont(appFont); From 6d261326857f48906c272601a9f265d22269bb87 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 20 Apr 2020 14:17:37 +0400 Subject: [PATCH 5/7] Allow custom click handlers in toasts. --- ui/toast/toast.h | 4 ++++ ui/toast/toast_widget.cpp | 10 +++++++--- ui/toast/toast_widget.h | 2 ++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/ui/toast/toast.h b/ui/toast/toast.h index 4094b7e..2152acc 100644 --- a/ui/toast/toast.h +++ b/ui/toast/toast.h @@ -8,6 +8,7 @@ #include "ui/effects/animations.h" #include "ui/text/text_entity.h" +#include "ui/click_handler.h" namespace Ui { namespace Toast { @@ -17,6 +18,8 @@ class Manager; class Widget; } // namespace internal +using ClickHandlerFilter = Fn; + inline constexpr auto kDefaultDuration = crl::time(1500); struct Config { TextWithEntities text; @@ -26,6 +29,7 @@ struct Config { int maxWidth = 0; int maxLines = 16; bool multiline = false; + ClickHandlerFilter filter; }; void SetDefaultParent(not_null parent); void Show(not_null parent, const Config &config); diff --git a/ui/toast/toast_widget.cpp b/ui/toast/toast_widget.cpp index 5181cf8..b88c72a 100644 --- a/ui/toast/toast_widget.cpp +++ b/ui/toast/toast_widget.cpp @@ -24,7 +24,8 @@ Widget::Widget(QWidget *parent, const Config &config) , _maxTextWidth(widthWithoutPadding(_maxWidth)) , _maxTextHeight( st::toastTextStyle.font->height * (_multiline ? config.maxLines : 1)) -, _text(_multiline ? widthWithoutPadding(config.minWidth) : QFIXED_MAX) { +, _text(_multiline ? widthWithoutPadding(config.minWidth) : QFIXED_MAX) +, _clickHandlerFilter(config.filter) { const auto toastOptions = TextParseOptions{ TextParseMultiline, _maxTextWidth, @@ -116,11 +117,14 @@ void Widget::mouseReleaseEvent(QMouseEvent *e) { return; } if (const auto handler = ClickHandler::unpressed()) { - handler->onClick({ e->button() }); + const auto button = e->button(); + if (!_clickHandlerFilter + || _clickHandlerFilter(handler, button)) { + ActivateClickHandler(this, handler, button); + } } } - } // namespace internal } // namespace Toast } // namespace Ui diff --git a/ui/toast/toast_widget.h b/ui/toast/toast_widget.h index a29d274..02c9276 100644 --- a/ui/toast/toast_widget.h +++ b/ui/toast/toast_widget.h @@ -49,6 +49,8 @@ private: int _textWidth = 0; Text::String _text; + ClickHandlerFilter _clickHandlerFilter; + }; } // namespace internal From 40084cab9577064436e2b381c27f498ac4a210a3 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 21 Apr 2020 14:03:54 +0400 Subject: [PATCH 6/7] Allow twice as dark toasts for quizes. --- ui/toast/toast.h | 1 + ui/toast/toast_widget.cpp | 4 ++++ ui/toast/toast_widget.h | 1 + 3 files changed, 6 insertions(+) diff --git a/ui/toast/toast.h b/ui/toast/toast.h index 2152acc..b0e0198 100644 --- a/ui/toast/toast.h +++ b/ui/toast/toast.h @@ -29,6 +29,7 @@ struct Config { int maxWidth = 0; int maxLines = 16; bool multiline = false; + bool dark = false; ClickHandlerFilter filter; }; void SetDefaultParent(not_null parent); diff --git a/ui/toast/toast_widget.cpp b/ui/toast/toast_widget.cpp index b88c72a..933782c 100644 --- a/ui/toast/toast_widget.cpp +++ b/ui/toast/toast_widget.cpp @@ -19,6 +19,7 @@ Widget::Widget(QWidget *parent, const Config &config) : TWidget(parent) , _roundRect(ImageRoundRadius::Large, st::toastBg) , _multiline(config.multiline) +, _dark(config.dark) , _maxWidth((config.maxWidth > 0) ? config.maxWidth : st::toastMaxWidth) , _padding((config.padding.left() > 0) ? config.padding : st::toastPadding) , _maxTextWidth(widthWithoutPadding(_maxWidth)) @@ -69,6 +70,9 @@ void Widget::paintEvent(QPaintEvent *e) { p.setOpacity(_shownLevel); _roundRect.paint(p, rect()); + if (_dark) { + _roundRect.paint(p, rect()); + } p.setTextPalette(st::toastTextPalette); diff --git a/ui/toast/toast_widget.h b/ui/toast/toast_widget.h index 02c9276..22b6db8 100644 --- a/ui/toast/toast_widget.h +++ b/ui/toast/toast_widget.h @@ -41,6 +41,7 @@ private: float64 _shownLevel = 0; bool _multiline = false; + bool _dark = false; int _maxWidth = 0; QMargins _padding; From 725d768f0ff1c22f6b477c28c324c20cbf2419da Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 23 Apr 2020 17:30:57 +0400 Subject: [PATCH 7/7] Add methods to detect suspicious urls and encode domains. --- ui/basic_click_handlers.cpp | 38 +++++++++++++++++++++++++++++++++++++ ui/basic_click_handlers.h | 2 ++ 2 files changed, 40 insertions(+) diff --git a/ui/basic_click_handlers.cpp b/ui/basic_click_handlers.cpp index 534ea57..21b55d9 100644 --- a/ui/basic_click_handlers.cpp +++ b/ui/basic_click_handlers.cpp @@ -69,6 +69,44 @@ void UrlClickHandler::Open(QString url, QVariant context) { } } +bool UrlClickHandler::IsSuspicious(const QString &url) { + static const auto Check1 = QRegularExpression( + "^(https?://)?([^/#\\:]+)([/#\\:]|$)", + QRegularExpression::CaseInsensitiveOption); + const auto match1 = Check1.match(url); + if (!match1.hasMatch()) { + return false; + } + const auto domain = match1.capturedRef(2); + static const auto Check2 = QRegularExpression("^(.*)\\.[a-zA-Z]+$"); + const auto match2 = Check2.match(domain); + if (!match2.hasMatch()) { + return false; + } + const auto part = match2.capturedRef(1); + static const auto Check3 = QRegularExpression("[^a-zA-Z0-9\\.\\-]"); + return Check3.match(part).hasMatch(); +} + + +QString UrlClickHandler::ShowEncoded(const QString &url) { + if (const auto u = QUrl(url); u.isValid()) { + return QString::fromUtf8(u.toEncoded()); + } + static const auto Check1 = QRegularExpression( + "^(https?://)?([^/#\\:]+)([/#\\:]|$)", + QRegularExpression::CaseInsensitiveOption); + if (const auto match1 = Check1.match(url); match1.hasMatch()) { + const auto domain = match1.captured(1).append(match1.capturedRef(2)); + if (const auto u = QUrl(domain); u.isValid()) { + return QString( + ).append(QString::fromUtf8(u.toEncoded()) + ).append(url.midRef(match1.capturedEnd(2))); + } + } + return url; +} + auto UrlClickHandler::getTextEntity() const -> TextEntity { const auto type = isEmail() ? EntityType::Email : EntityType::Url; return { type, _originalUrl }; diff --git a/ui/basic_click_handlers.h b/ui/basic_click_handlers.h index ffcab83..53969c6 100644 --- a/ui/basic_click_handlers.h +++ b/ui/basic_click_handlers.h @@ -59,6 +59,8 @@ public: const auto at = url.indexOf('@'), slash = url.indexOf('/'); return ((at > 0) && (slash < 0 || slash > at)); } + [[nodiscard]] static bool IsSuspicious(const QString &url); + [[nodiscard]] static QString ShowEncoded(const QString &url); protected: QString url() const override;