From 8e44fb7780ad9b6f83fad2579c7782198a104398 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 10 May 2020 17:02:37 +0400 Subject: [PATCH 01/10] Fix tilde hack for non-empty styleName. --- ui/widgets/input_fields.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ui/widgets/input_fields.cpp b/ui/widgets/input_fields.cpp index de4a53d..e0a0c74 100644 --- a/ui/widgets/input_fields.cpp +++ b/ui/widgets/input_fields.cpp @@ -1968,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 = _st.font->semibold(); + auto tildeFixedFont = _st.font->semibold()->f; // First tag handling (the one we inserted text to). bool startTagFound = false; @@ -2018,6 +2018,11 @@ void InputField::processFormatting(int insertPosition, int insertEnd) { break; } if (tildeFormatting) { + const auto formatFont = format.font(); + if (!tildeFixedFont.styleName().isEmpty() + && formatFont.styleName().isEmpty()) { + tildeFixedFont.setStyleName(QString()); + } isTildeFragment = (format.font() == tildeFixedFont); } From 8a1aeb22d87dab20f010be446cb35de57c294080 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 10 May 2020 17:10:21 +0400 Subject: [PATCH 02/10] Use Vazir font on macOS too. --- ui/style/style_core_font.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/ui/style/style_core_font.cpp b/ui/style/style_core_font.cpp index 21d1080..3fad606 100644 --- a/ui/style/style_core_font.cpp +++ b/ui/style/style_core_font.cpp @@ -230,10 +230,8 @@ void StartFonts() { //QFont::insertSubstitution(name, fallback); #endif // Q_OS_WIN -#if defined Q_OS_WIN || defined Q_OS_LINUX const auto persianFallback = FontTypePersianFallback[i]; QFont::insertSubstitution(name, persianFallback); -#endif // Q_OS_WIN || Q_OS_LINUX } #ifdef Q_OS_MAC From ef31d05da547a76dd89c3bbb3505b2119ae67b6b Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Sun, 10 May 2020 20:54:14 +0400 Subject: [PATCH 03/10] Wayland always support compositing --- ui/platform/linux/ui_utility_linux.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ui/platform/linux/ui_utility_linux.cpp b/ui/platform/linux/ui_utility_linux.cpp index e43e672..3c1ee6a 100644 --- a/ui/platform/linux/ui_utility_linux.cpp +++ b/ui/platform/linux/ui_utility_linux.cpp @@ -22,6 +22,9 @@ bool IsApplicationActive() { } bool TranslucentWindowsSupported(QPoint globalPosition) { + if (QGuiApplication::platformName().startsWith(qsl("wayland"), Qt::CaseInsensitive)) { + return true; + } if (const auto native = QGuiApplication::platformNativeInterface()) { if (const auto desktop = QApplication::desktop()) { const auto index = desktop->screenNumber(globalPosition); From 95b49979fbf8956684ff41d5c1d72bff618a1957 Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Sun, 10 May 2020 19:05:43 +0400 Subject: [PATCH 04/10] Remove unneeded Segoe UI fallback struct and add Vazir file struct --- ui/style/style_core_font.cpp | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/ui/style/style_core_font.cpp b/ui/style/style_core_font.cpp index 3fad606..9c0efac 100644 --- a/ui/style/style_core_font.cpp +++ b/ui/style/style_core_font.cpp @@ -153,6 +153,14 @@ QString FontTypeNames[FontTypesCount] = { "DAOpenSansSemibold", "DAOpenSansSemiboldItalic", }; +QString FontTypePersianFallbackFiles[FontTypesCount] = { + "DAVazirRegular", + "DAVazirRegular", + "DAVazirMediumAsBold", + "DAVazirMediumAsBold", + "DAVazirMediumAsBold", + "DAVazirMediumAsBold", +}; QString FontTypePersianFallback[FontTypesCount] = { "DAVazirRegular", "DAVazirRegular", @@ -170,16 +178,6 @@ int32 FontTypeFlags[FontTypesCount] = { FontSemibold, FontSemibold | FontItalic, }; -#ifdef Q_OS_WIN -QString FontTypeWindowsFallback[FontTypesCount] = { - "Segoe UI", - "Segoe UI", - "Segoe UI", - "Segoe UI", - "Segoe UI", - "Segoe UI", -}; -#endif // Q_OS_WIN bool Started = false; QString Overrides[FontTypesCount]; @@ -200,10 +198,6 @@ void StartFonts() { } #ifndef DESKTOP_APP_USE_PACKAGED_FONTS - LoadCustomFont(":/gui/fonts/DAVazirRegular.ttf", "DAVazirRegular"); - LoadCustomFont(":/gui/fonts/DAVazirMediumAsBold.ttf", "DAVazirMedium", style::internal::FontBold); - LoadCustomFont(":/gui/fonts/DAVazirMediumAsBold.ttf", "DAVazirMedium", style::internal::FontSemibold); - bool areGood[FontTypesCount] = { false }; for (auto i = 0; i != FontTypesCount; ++i) { const auto file = FontTypeFiles[i]; @@ -212,12 +206,16 @@ void StartFonts() { areGood[i] = LoadCustomFont(":/gui/fonts/" + file + ".ttf", name, flags); Overrides[i] = name; + const auto persianFallbackFile = FontTypePersianFallbackFiles[i]; + const auto persianFallback = FontTypePersianFallback[i]; + LoadCustomFont(":/gui/fonts/" + persianFallbackFile + ".ttf", persianFallback, flags); + #ifdef Q_OS_WIN // Attempt to workaround a strange font bug with Open Sans Semibold not loading. // See https://github.com/telegramdesktop/tdesktop/issues/3276 for details. // Crash happens on "options.maxh / _t->_st->font->height" with "division by zero". // In that place "_t->_st->font" is "semiboldFont" is "font(13 "Open Sans Semibold"). - const auto fallback = FontTypeWindowsFallback[i]; + const auto fallback = "Segoe UI"; if (!areGood[i]) { if (ValidateFont(fallback, flags)) { Overrides[i] = fallback; @@ -230,7 +228,6 @@ void StartFonts() { //QFont::insertSubstitution(name, fallback); #endif // Q_OS_WIN - const auto persianFallback = FontTypePersianFallback[i]; QFont::insertSubstitution(name, persianFallback); } From d05d9b214da0e9708ff0c5f540c6fb09f6187e7d Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Sun, 10 May 2020 19:08:03 +0400 Subject: [PATCH 05/10] Fix bolding with packaged fonts --- ui/style/style_core_font.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ui/style/style_core_font.cpp b/ui/style/style_core_font.cpp index 9c0efac..4209fd4 100644 --- a/ui/style/style_core_font.cpp +++ b/ui/style/style_core_font.cpp @@ -313,13 +313,17 @@ FontData::FontData(int size, uint32 flags, int family, Font *other) } f.setPixelSize(size); - f.setBold((_flags & FontBold) || (_flags & FontSemibold)); f.setItalic(_flags & FontItalic); f.setUnderline(_flags & FontUnderline); f.setStrikeOut(_flags & FontStrikeOut); if ((_flags & FontBold) || (_flags & FontSemibold)) { +#ifdef DESKTOP_APP_USE_PACKAGED_FONTS + f.setWeight(QFont::DemiBold); +#else // DESKTOP_APP_USE_PACKAGED_FONTS + f.setBold(true); f.setStyleName("Semibold"); +#endif // !DESKTOP_APP_USE_PACKAGED_FONTS } m = QFontMetrics(f); From b1d00d0b28cfce60d88b1a0a088539980adcfdb5 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 12 May 2020 11:05:55 +0400 Subject: [PATCH 06/10] Add method to count max monospace paragraph width. --- ui/text/text.cpp | 59 +++++++++++++++++++++++++++++++++++++++++++++++- ui/text/text.h | 5 ++-- 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/ui/text/text.cpp b/ui/text/text.cpp index b8e4911..60a8a52 100644 --- a/ui/text/text.cpp +++ b/ui/text/text.cpp @@ -2755,7 +2755,7 @@ void String::recountNaturalSize(bool initial, Qt::LayoutDirection optionsDir) { _maxWidth = _minHeight = 0; int32 lineHeight = 0; - int32 result = 0, lastNewlineStart = 0; + int32 lastNewlineStart = 0; QFixed _width = 0, last_rBearing = 0, last_rPadding = 0; for (auto i = _blocks.cbegin(), e = _blocks.cend(); i != e; ++i) { auto b = i->get(); @@ -2823,6 +2823,63 @@ void String::recountNaturalSize(bool initial, Qt::LayoutDirection optionsDir) { } } +int String::countMaxMonospaceWidth() const { + NewlineBlock *lastNewline = 0; + + auto result = QFixed(); + auto paragraphWidth = QFixed(); + auto lastNewlineStart = 0; + auto fullMonospace = true; + QFixed _width = 0, last_rBearing = 0, last_rPadding = 0; + for (auto i = _blocks.cbegin(), e = _blocks.cend(); i != e; ++i) { + auto b = i->get(); + auto _btype = b->type(); + if (_btype == TextBlockTNewline) { + lastNewlineStart = b->from(); + lastNewline = static_cast(b); + + last_rBearing = b->f_rbearing(); + last_rPadding = b->f_rpadding(); + + if (fullMonospace) { + accumulate_max(paragraphWidth, _width); + accumulate_max(result, paragraphWidth); + paragraphWidth = 0; + } else { + fullMonospace = true; + } + _width = (b->f_width() - last_rBearing); + continue; + } + if (!(b->flags() & (TextBlockFPre | TextBlockFCode)) + && (b->type() != TextBlockTSkip)) { + fullMonospace = false; + } + auto b__f_rbearing = b->f_rbearing(); // cache + + // We need to accumulate max width after each block, because + // some blocks have width less than -1 * previous right bearing. + // In that cases the _width gets _smaller_ after moving to the next block. + // + // But when we layout block and we're sure that _maxWidth is enough + // for all the blocks to fit on their line we check each block, even the + // intermediate one with a large negative right bearing. + if (fullMonospace) { + accumulate_max(paragraphWidth, _width); + } + _width += last_rBearing + (last_rPadding + b->f_width() - b__f_rbearing); + + last_rBearing = b__f_rbearing; + last_rPadding = b->f_rpadding(); + continue; + } + if (_width > 0 && fullMonospace) { + accumulate_max(paragraphWidth, _width); + accumulate_max(result, paragraphWidth); + } + return result.ceil().toInt(); +} + void String::setMarkedText(const style::TextStyle &st, const TextWithEntities &textWithEntities, const TextParseOptions &options) { _st = &st; clear(); diff --git a/ui/text/text.h b/ui/text/text.h index dd7614e..709f22a 100644 --- a/ui/text/text.h +++ b/ui/text/text.h @@ -128,12 +128,13 @@ public: bool updateSkipBlock(int width, int height); bool removeSkipBlock(); - int32 maxWidth() const { + int maxWidth() const { return _maxWidth.ceil().toInt(); } - int32 minHeight() const { + int minHeight() const { return _minHeight; } + int countMaxMonospaceWidth() const; void draw(Painter &p, int32 left, int32 top, int32 width, style::align align = style::al_left, int32 yFrom = 0, int32 yTo = -1, TextSelection selection = { 0, 0 }, bool fullWidthSelection = true) const; void drawElided(Painter &p, int32 left, int32 top, int32 width, int32 lines = 1, style::align align = style::al_left, int32 yFrom = 0, int32 yTo = -1, int32 removeFromEnd = 0, bool breakEverywhere = false, TextSelection selection = { 0, 0 }) const; From e5a527b5ef0e83465d195a03442829980e01d89d Mon Sep 17 00:00:00 2001 From: Ilya Fedin Date: Mon, 11 May 2020 04:19:50 +0400 Subject: [PATCH 07/10] Adapt for building with MinGW and 64-bit --- ui/platform/win/ui_utility_win.cpp | 2 +- ui/platform/win/ui_window_shadow_win.cpp | 18 +++++++++++++++++- ui/platform/win/ui_window_win.cpp | 8 ++++---- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/ui/platform/win/ui_utility_win.cpp b/ui/platform/win/ui_utility_win.cpp index 9187e50..392a65d 100644 --- a/ui/platform/win/ui_utility_win.cpp +++ b/ui/platform/win/ui_utility_win.cpp @@ -36,7 +36,7 @@ void IgnoreAllActivation(not_null widget) { Assert(handle != nullptr); ShowWindow(handle, SW_HIDE); - const auto style = GetWindowLong(handle, GWL_EXSTYLE); + const auto style = GetWindowLongPtr(handle, GWL_EXSTYLE); SetWindowLong( handle, GWL_EXSTYLE, diff --git a/ui/platform/win/ui_window_shadow_win.cpp b/ui/platform/win/ui_window_shadow_win.cpp index 9b703fe..7661e50 100644 --- a/ui/platform/win/ui_window_shadow_win.cpp +++ b/ui/platform/win/ui_window_shadow_win.cpp @@ -16,6 +16,22 @@ #include +// WM_POINTER support from Windows 8 onwards (WINVER >= 0x0602) +#ifndef WM_POINTERUPDATE +# define WM_NCPOINTERUPDATE 0x0241 +# define WM_NCPOINTERDOWN 0x0242 +# define WM_NCPOINTERUP 0x0243 +# define WM_POINTERUPDATE 0x0245 +# define WM_POINTERDOWN 0x0246 +# define WM_POINTERUP 0x0247 +# define WM_POINTERENTER 0x0249 +# define WM_POINTERLEAVE 0x024A +# define WM_POINTERACTIVATE 0x024B +# define WM_POINTERCAPTURECHANGED 0x024C +# define WM_POINTERWHEEL 0x024E +# define WM_POINTERHWHEEL 0x024F +#endif // WM_POINTERUPDATE + namespace Ui { namespace Platform { namespace { @@ -197,7 +213,7 @@ void WindowShadow::init(QColor color) { return; } ShadowByHandle.emplace(_handles[i], this); - SetWindowLong(_handles[i], GWL_HWNDPARENT, (LONG)_handle); + SetWindowLongPtr(_handles[i], GWLP_HWNDPARENT, (LONG)_handle); _contexts[i] = CreateCompatibleDC(_screenContext); if (!_contexts[i]) { diff --git a/ui/platform/win/ui_window_win.cpp b/ui/platform/win/ui_window_win.cpp index 41a2260..b1dbd1c 100644 --- a/ui/platform/win/ui_window_win.cpp +++ b/ui/platform/win/ui_window_win.cpp @@ -70,7 +70,7 @@ bool WindowHelper::NativeFilter::nativeEventFilter( msg->message, msg->wParam, msg->lParam, - static_cast(result)) + reinterpret_cast(result)) : false; } @@ -269,7 +269,7 @@ bool WindowHelper::handleNativeEvent( } return false; case WM_SHOWWINDOW: { - const auto style = GetWindowLong(_handle, GWL_STYLE); + const auto style = GetWindowLongPtr(_handle, GWL_STYLE); const auto changes = WindowShadow::Change::Resized | ((wParam && !(style & (WS_MAXIMIZE | WS_MINIMIZE))) ? WindowShadow::Change::Shown @@ -375,8 +375,8 @@ void WindowHelper::updateMargins() { GetClientRect(_handle, &r); a = r; - const auto style = GetWindowLong(_handle, GWL_STYLE); - const auto styleEx = GetWindowLong(_handle, GWL_EXSTYLE); + const auto style = GetWindowLongPtr(_handle, GWL_STYLE); + const auto styleEx = GetWindowLongPtr(_handle, GWL_EXSTYLE); AdjustWindowRectEx(&a, style, false, styleEx); auto margins = QMargins( a.left - r.left, From 4d2f17d9fca1462ea875957b232b76515a0fd726 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 12 May 2020 16:34:30 +0400 Subject: [PATCH 08/10] Fix build on Linux. --- ui/platform/linux/ui_utility_linux.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/platform/linux/ui_utility_linux.cpp b/ui/platform/linux/ui_utility_linux.cpp index 3c1ee6a..4563f48 100644 --- a/ui/platform/linux/ui_utility_linux.cpp +++ b/ui/platform/linux/ui_utility_linux.cpp @@ -22,7 +22,7 @@ bool IsApplicationActive() { } bool TranslucentWindowsSupported(QPoint globalPosition) { - if (QGuiApplication::platformName().startsWith(qsl("wayland"), Qt::CaseInsensitive)) { + if (QGuiApplication::platformName().startsWith("wayland", Qt::CaseInsensitive)) { return true; } if (const auto native = QGuiApplication::platformNativeInterface()) { From 6aa6a143bfce4679a876f177da5699e8bcc8a30d Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 12 May 2020 19:11:46 +0400 Subject: [PATCH 09/10] Add a workaround for invalid Qt signal data. --- ui/widgets/input_fields.cpp | 21 +++++++++++++++++++-- ui/widgets/input_fields.h | 12 +++++++----- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/ui/widgets/input_fields.cpp b/ui/widgets/input_fields.cpp index e0a0c74..b049e38 100644 --- a/ui/widgets/input_fields.cpp +++ b/ui/widgets/input_fields.cpp @@ -2084,7 +2084,7 @@ void InputField::processFormatting(int insertPosition, int insertEnd) { break; } - if (breakTagOnNotLetter && !ch->isLetter()) { + if (breakTagOnNotLetter && !ch->isLetterOrNumber()) { // Remove tag name till the end if no current action is prepared. if (action.type != ActionType::Invalid) { break; @@ -2185,6 +2185,22 @@ void InputField::onDocumentContentsChange( return; } + // In case of input method events Qt emits + // document content change signals for a whole + // text block where the even took place. + // This breaks our wysiwyg markup, so we adjust + // the parameters to match the real change. + if (_inputMethodCommit.has_value() + && charsAdded > _inputMethodCommit->size() + && charsRemoved > 0) { + const auto inBlockBefore = charsAdded - _inputMethodCommit->size(); + if (charsRemoved >= inBlockBefore) { + charsAdded -= inBlockBefore; + charsRemoved -= inBlockBefore; + position += inBlockBefore; + } + } + const auto document = _inner->document(); // Qt bug workaround https://bugreports.qt.io/browse/QTBUG-49062 @@ -2882,8 +2898,9 @@ void InputField::inputMethodEventInner(QInputMethodEvent *e) { _lastPreEditText = preedit; startPlaceholderAnimation(); } - const auto text = e->commitString(); + _inputMethodCommit = e->commitString(); _inner->QTextEdit::inputMethodEvent(e); + const auto text = *base::take(_inputMethodCommit); if (!processMarkdownReplaces(text)) { processInstantReplaces(text); } diff --git a/ui/widgets/input_fields.h b/ui/widgets/input_fields.h index 2873ea7..2b7b7ce 100644 --- a/ui/widgets/input_fields.h +++ b/ui/widgets/input_fields.h @@ -462,19 +462,21 @@ private: int _maxLength = -1; int _minHeight = -1; int _maxHeight = -1; - bool _forcePlaceholderHidden = false; - bool _reverseMarkdownReplacement = false; const std::unique_ptr _inner; - TextWithTags _lastTextWithTags; - std::vector _lastMarkdownTags; - QString _lastPreEditText; Fn _editLinkCallback; + TextWithTags _lastTextWithTags; + std::vector _lastMarkdownTags; + QString _lastPreEditText; + std::optional _inputMethodCommit; + + bool _forcePlaceholderHidden = false; + bool _reverseMarkdownReplacement = false; // Tags list which we should apply while setText() call or insert from mime data. TagList _insertedTags; From 8e2cfbf303a43bbd946279dd2cb394be82a782a0 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 12 May 2020 19:12:33 +0400 Subject: [PATCH 10/10] Insert only paragraph separators in the field. --- ui/widgets/input_fields.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ui/widgets/input_fields.cpp b/ui/widgets/input_fields.cpp index b049e38..69460f6 100644 --- a/ui/widgets/input_fields.cpp +++ b/ui/widgets/input_fields.cpp @@ -2689,6 +2689,8 @@ void InputField::keyPressEventInner(QKeyEvent *e) { const auto oldPosition = textCursor().position(); if (enter && ctrl) { e->setModifiers(e->modifiers() & ~Qt::ControlModifier); + } else if (enter && shift) { + e->setModifiers(e->modifiers() & ~Qt::ShiftModifier); } _inner->QTextEdit::keyPressEvent(e); auto cursor = textCursor();