diff --git a/CMakeLists.txt b/CMakeLists.txt index 259e571..218418f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -211,8 +211,6 @@ PRIVATE ui/round_rect.h ui/rp_widget.cpp ui/rp_widget.h - ui/ui_log.cpp - ui/ui_log.h ui/ui_utility.cpp ui/ui_utility.h diff --git a/ui/emoji_config.cpp b/ui/emoji_config.cpp index dcde206..3d75700 100644 --- a/ui/emoji_config.cpp +++ b/ui/emoji_config.cpp @@ -10,10 +10,10 @@ #include "base/bytes.h" #include "base/openssl_help.h" #include "base/parse_helper.h" +#include "base/debug_log.h" #include "ui/style/style_core.h" #include "ui/painter.h" #include "ui/ui_utility.h" -#include "ui/ui_log.h" #include "styles/style_basic.h" #include @@ -218,7 +218,7 @@ void SaveToFile(int id, const QImage &image, int size, int index) { if (!f.open(QIODevice::WriteOnly)) { if (!QDir::current().mkpath(internal::CacheFileFolder()) || !f.open(QIODevice::WriteOnly)) { - UI_LOG(("App Error: Could not open emoji cache '%1' for size %2_%3" + LOG(("App Error: Could not open emoji cache '%1' for size %2_%3" ).arg(f.fileName() ).arg(size ).arg(index)); @@ -244,7 +244,7 @@ void SaveToFile(int id, const QImage &image, int size, int index) { || !write(data) || !write(openssl::Sha256(bytes::make_span(header), data)) || false) { - UI_LOG(("App Error: Could not write emoji cache '%1' for size %2" + LOG(("App Error: Could not write emoji cache '%1' for size %2" ).arg(f.fileName() ).arg(size)); } diff --git a/ui/integration.cpp b/ui/integration.cpp index f8df624..8bd1f71 100644 --- a/ui/integration.cpp +++ b/ui/integration.cpp @@ -36,12 +36,6 @@ void Integration::textActionsUpdated() { void Integration::activationFromTopPanel() { } -void Integration::startFontsBegin() { -} - -void Integration::startFontsEnd() { -} - QString Integration::timeFormat() { return u"hh:mm"_q; } @@ -79,10 +73,6 @@ const Emoji::One *Integration::defaultEmojiVariant(const Emoji::One *emoji) { return emoji; } -QWidget *Integration::modalWindowParent() { - return nullptr; -} - rpl::producer<> Integration::forcePopupMenuHideRequests() { return rpl::never(); } diff --git a/ui/integration.h b/ui/integration.h index e3c90db..94b09d3 100644 --- a/ui/integration.h +++ b/ui/integration.h @@ -35,15 +35,11 @@ public: virtual void registerLeaveSubscription(not_null widget) = 0; virtual void unregisterLeaveSubscription(not_null widget) = 0; - virtual void writeLogEntry(const QString &entry) = 0; [[nodiscard]] virtual QString emojiCacheFolder() = 0; virtual void textActionsUpdated(); virtual void activationFromTopPanel(); - virtual void startFontsBegin(); - virtual void startFontsEnd(); - [[nodiscard]] virtual QString timeFormat(); [[nodiscard]] virtual std::shared_ptr createLinkHandler( @@ -56,8 +52,6 @@ public: [[nodiscard]] virtual const Emoji::One *defaultEmojiVariant( const Emoji::One *emoji); - [[nodiscard]] virtual QWidget *modalWindowParent(); - [[nodiscard]] virtual rpl::producer<> forcePopupMenuHideRequests(); [[nodiscard]] virtual QString phraseContextCopyText(); diff --git a/ui/layers/generic_box.cpp b/ui/layers/generic_box.cpp index 94888e8..447bf8c 100644 --- a/ui/layers/generic_box.cpp +++ b/ui/layers/generic_box.cpp @@ -16,7 +16,7 @@ namespace Ui { void GenericBox::prepare() { _init(this); - auto wrap = object_ptr(this, std::move(_content)); + auto wrap = object_ptr(this, std::move(_owned)); setDimensionsToContent(_width ? _width : st::boxWidth, wrap.data()); setInnerWidget(std::move(wrap)); } @@ -26,7 +26,7 @@ void GenericBox::addSkip(int height) { } not_null GenericBox::verticalLayout() { - return _content.data(); + return _content; } } // namespace Ui diff --git a/ui/layers/generic_box.h b/ui/layers/generic_box.h index e0bceca..b6832f8 100644 --- a/ui/layers/generic_box.h +++ b/ui/layers/generic_box.h @@ -120,7 +120,8 @@ private: FnMut)> _init; Fn _focus; Fn _showFinished; - object_ptr _content; + object_ptr _owned; + not_null _content; int _width = 0; }; @@ -166,7 +167,8 @@ inline GenericBox::GenericBox( MakeIniter( std::forward(init), std::forward(args)...)) -, _content(this) { +, _owned(this) +, _content(_owned.data()) { } } // namespace Ui diff --git a/ui/platform/linux/ui_utility_linux.cpp b/ui/platform/linux/ui_utility_linux.cpp index b61e47b..e5a258f 100644 --- a/ui/platform/linux/ui_utility_linux.cpp +++ b/ui/platform/linux/ui_utility_linux.cpp @@ -6,9 +6,9 @@ // #include "ui/platform/linux/ui_utility_linux.h" -#include "ui/ui_log.h" #include "base/platform/base_platform_info.h" #include "base/platform/linux/base_linux_gtk_integration.h" +#include "base/debug_log.h" #include "ui/platform/linux/ui_linux_wayland_integration.h" #include "base/const_string.h" #include "base/qt_adapters.h" @@ -33,6 +33,259 @@ namespace { constexpr auto kXCBFrameExtentsAtomName = "_GTK_FRAME_EXTENTS"_cs; #ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION +std::optional XCBWindowMapped(xcb_window_t window) { + const auto connection = base::Platform::XCB::GetConnectionFromQt(); + if (!connection) { + return std::nullopt; + } + + const auto cookie = xcb_get_window_attributes(connection, window); + const auto reply = xcb_get_window_attributes_reply( + connection, + cookie, + nullptr); + + if (!reply) { + return std::nullopt; + } + + const auto guard = gsl::finally([&] { free(reply); }); + return reply->map_state == XCB_MAP_STATE_VIEWABLE; +} + +std::optional XCBWindowHidden(xcb_window_t window) { + const auto connection = base::Platform::XCB::GetConnectionFromQt(); + if (!connection) { + return std::nullopt; + } + + const auto stateAtom = base::Platform::XCB::GetAtom( + connection, + "_NET_WM_STATE"); + + const auto stateHiddenAtom = base::Platform::XCB::GetAtom( + connection, + "_NET_WM_STATE_HIDDEN"); + + if (!stateAtom.has_value() || !stateHiddenAtom.has_value()) { + return std::nullopt; + } + + const auto cookie = xcb_get_property( + connection, + false, + window, + *stateAtom, + XCB_ATOM_ATOM, + 0, + 1024); + + const auto reply = xcb_get_property_reply( + connection, + cookie, + nullptr); + + if (!reply) { + return std::nullopt; + } + + const auto guard = gsl::finally([&] { free(reply); }); + if (reply->type != XCB_ATOM_ATOM || reply->format != 32) { + return std::nullopt; + } + + const auto atomsStart = reinterpret_cast( + xcb_get_property_value(reply)); + + const auto states = std::vector( + atomsStart, + atomsStart + reply->length); + + return ranges::contains(states, *stateHiddenAtom); +} + +QRect XCBWindowGeometry(xcb_window_t window) { + const auto connection = base::Platform::XCB::GetConnectionFromQt(); + if (!connection) { + return {}; + } + + const auto cookie = xcb_get_geometry(connection, window); + const auto reply = xcb_get_geometry_reply( + connection, + cookie, + nullptr); + + if (!reply) { + return {}; + } + + const auto guard = gsl::finally([&] { free(reply); }); + return QRect(reply->x, reply->y, reply->width, reply->height); +} + +std::optional XCBCurrentWorkspace() { + const auto connection = base::Platform::XCB::GetConnectionFromQt(); + if (!connection) { + return std::nullopt; + } + + const auto root = base::Platform::XCB::GetRootWindowFromQt(); + if (!root.has_value()) { + return std::nullopt; + } + + const auto currentDesktopAtom = base::Platform::XCB::GetAtom( + connection, + "_NET_CURRENT_DESKTOP"); + + if (!currentDesktopAtom.has_value()) { + return std::nullopt; + } + + const auto cookie = xcb_get_property( + connection, + false, + *root, + *currentDesktopAtom, + XCB_ATOM_CARDINAL, + 0, + 1024); + + const auto reply = xcb_get_property_reply( + connection, + cookie, + nullptr); + + if (!reply) { + return std::nullopt; + } + + const auto guard = gsl::finally([&] { free(reply); }); + return (reply->type == XCB_ATOM_CARDINAL) + ? std::make_optional( + *reinterpret_cast(xcb_get_property_value(reply))) + : std::nullopt; +} + +std::optional XCBWindowWorkspace(xcb_window_t window) { + const auto connection = base::Platform::XCB::GetConnectionFromQt(); + if (!connection) { + return std::nullopt; + } + + const auto desktopAtom = base::Platform::XCB::GetAtom( + connection, + "_NET_WM_DESKTOP"); + + if (!desktopAtom.has_value()) { + return std::nullopt; + } + + const auto cookie = xcb_get_property( + connection, + false, + window, + *desktopAtom, + XCB_ATOM_CARDINAL, + 0, + 1024); + + const auto reply = xcb_get_property_reply( + connection, + cookie, + nullptr); + + if (!reply) { + return std::nullopt; + } + + const auto guard = gsl::finally([&] { free(reply); }); + return (reply->type == XCB_ATOM_CARDINAL) + ? std::make_optional( + *reinterpret_cast(xcb_get_property_value(reply))) + : std::nullopt; +} + +std::optional XCBIsOverlapped( + not_null widget, + const QRect &rect) { + const auto window = widget->window()->winId(); + Expects(window != XCB_WINDOW_NONE); + + const auto connection = base::Platform::XCB::GetConnectionFromQt(); + if (!connection) { + return std::nullopt; + } + + const auto root = base::Platform::XCB::GetRootWindowFromQt(); + if (!root.has_value()) { + return std::nullopt; + } + + const auto windowWorkspace = XCBWindowWorkspace( + window); + + const auto currentWorkspace = XCBCurrentWorkspace(); + + if (windowWorkspace.has_value() + && currentWorkspace.has_value() + && *windowWorkspace != *currentWorkspace + && *windowWorkspace != 0xFFFFFFFF) { + return true; + } + + const auto cookie = xcb_query_tree(connection, *root); + const auto reply = xcb_query_tree_reply(connection, cookie, nullptr); + if (!reply) { + return std::nullopt; + } + + const auto guard = gsl::finally([&] { free(reply); }); + + const auto tree = xcb_query_tree_children(reply); + auto aboveTheWindow = false; + + for (auto i = 0, l = xcb_query_tree_children_length(reply); i < l; ++i) { + if (window == tree[i]) { + aboveTheWindow = true; + continue; + } + + if (!aboveTheWindow) { + continue; + } + + const auto geometry = XCBWindowGeometry(tree[i]); + if (!rect.intersects(geometry)) { + continue; + } + + const auto workspace = XCBWindowWorkspace(tree[i]); + if (workspace.has_value() + && windowWorkspace.has_value() + && *workspace != *windowWorkspace + && *workspace != 0xFFFFFFFF) { + continue; + } + + const auto mapped = XCBWindowMapped(tree[i]); + if (mapped.has_value() && !*mapped) { + continue; + } + + const auto hidden = XCBWindowHidden(tree[i]); + if (hidden.has_value() + && *hidden) { + continue; + } + + return true; + } + + return false; +} + bool SetXCBFrameExtents(QWindow *window, const QMargins &extents) { const auto connection = base::Platform::XCB::GetConnectionFromQt(); if (!connection) { @@ -169,10 +422,10 @@ bool TranslucentWindowsSupported(QPoint globalPosition) { static auto WarnedAbout = base::flat_set(); if (!WarnedAbout.contains(index)) { WarnedAbout.emplace(index); - UI_LOG(("WARNING: Compositing is disabled for screen index %1 (for position %2,%3)").arg(index).arg(globalPosition.x()).arg(globalPosition.y())); + LOG(("WARNING: Compositing is disabled for screen index %1 (for position %2,%3)").arg(index).arg(globalPosition.x()).arg(globalPosition.y())); } } else { - UI_LOG(("WARNING: Could not get screen for position %1,%2").arg(globalPosition.x()).arg(globalPosition.y())); + LOG(("WARNING: Could not get screen for position %1,%2").arg(globalPosition.x()).arg(globalPosition.y())); } } } @@ -189,12 +442,24 @@ void ClearTransientParent(not_null widget) { if (::Platform::IsX11()) { xcb_delete_property( base::Platform::XCB::GetConnectionFromQt(), - widget->windowHandle()->winId(), + widget->winId(), XCB_ATOM_WM_TRANSIENT_FOR); } #endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION } +std::optional IsOverlapped( + not_null widget, + const QRect &rect) { +#ifndef DESKTOP_APP_DISABLE_X11_INTEGRATION + if (::Platform::IsX11()) { + return XCBIsOverlapped(widget, rect); + } +#endif // !DESKTOP_APP_DISABLE_X11_INTEGRATION + + return std::nullopt; +} + bool WindowExtentsSupported() { #ifdef DESKTOP_APP_QT_PATCHED if (::Platform::IsWayland()) { diff --git a/ui/platform/mac/ui_utility_mac.mm b/ui/platform/mac/ui_utility_mac.mm index fd8b492..f6d8b55 100644 --- a/ui/platform/mac/ui_utility_mac.mm +++ b/ui/platform/mac/ui_utility_mac.mm @@ -7,6 +7,7 @@ #include "ui/platform/mac/ui_utility_mac.h" #include "ui/integration.h" +#include "base/qt_adapters.h" #include #include @@ -99,6 +100,60 @@ void DrainMainQueue() { void IgnoreAllActivation(not_null widget) { } +std::optional IsOverlapped( + not_null widget, + const QRect &rect) { + NSWindow *window = [reinterpret_cast(widget->window()->winId()) window]; + Assert(window != nullptr); + + if (![window isOnActiveSpace]) { + return true; + } + + const auto nativeRect = CGRectMake( + rect.x(), + rect.y(), + rect.width(), + rect.height()); + + CGWindowID windowId = (CGWindowID)[window windowNumber]; + const CGWindowListOption options = kCGWindowListExcludeDesktopElements + | kCGWindowListOptionOnScreenAboveWindow; + CFArrayRef windows = CGWindowListCopyWindowInfo(options, windowId); + if (!windows) { + return std::nullopt; + } + const auto guard = gsl::finally([&] { + CFRelease(windows); + }); + NSMutableArray *list = (__bridge NSMutableArray*)windows; + for (NSDictionary *window in list) { + NSNumber *alphaValue = [window objectForKey:@"kCGWindowAlpha"]; + const auto alpha = alphaValue ? [alphaValue doubleValue] : 1.; + if (alpha == 0.) { + continue; + } + NSString *owner = [window objectForKey:@"kCGWindowOwnerName"]; + NSNumber *layerValue = [window objectForKey:@"kCGWindowLayer"]; + const auto layer = layerValue ? [layerValue intValue] : 0; + if (owner && [owner isEqualToString:@"Dock"] && layer == 20) { + // It is always full screen. + continue; + } + CFDictionaryRef bounds = (__bridge CFDictionaryRef)[window objectForKey:@"kCGWindowBounds"]; + if (!bounds) { + continue; + } + CGRect rect; + if (!CGRectMakeWithDictionaryRepresentation(bounds, &rect)) { + continue; + } else if (CGRectIntersectsRect(rect, nativeRect)) { + return true; + } + } + return false; +} + TitleControls::Layout TitleControlsLayout() { return TitleControls::Layout{ .left = { diff --git a/ui/platform/ui_platform_utility.h b/ui/platform/ui_platform_utility.h index 3360418..32f1760 100644 --- a/ui/platform/ui_platform_utility.h +++ b/ui/platform/ui_platform_utility.h @@ -30,6 +30,10 @@ void BringToBack(not_null widget); void IgnoreAllActivation(not_null widget); void ClearTransientParent(not_null widget); +[[nodiscard]] std::optional IsOverlapped( + not_null widget, + const QRect &rect); + [[nodiscard]] constexpr bool UseMainQueueGeneric(); void DrainMainQueue(); // Needed only if UseMainQueueGeneric() is false. diff --git a/ui/platform/win/ui_utility_win.cpp b/ui/platform/win/ui_utility_win.cpp index 4c4cfcb..034299f 100644 --- a/ui/platform/win/ui_utility_win.cpp +++ b/ui/platform/win/ui_utility_win.cpp @@ -11,6 +11,11 @@ #include #include +#include +#include + +using namespace Microsoft::WRL; + namespace Ui { namespace Platform { @@ -45,6 +50,55 @@ void IgnoreAllActivation(not_null widget) { ShowWindow(handle, SW_SHOW); } +std::optional IsOverlapped( + not_null widget, + const QRect &rect) { + const auto handle = reinterpret_cast(widget->window()->winId()); + Expects(handle != nullptr); + + ComPtr virtualDesktopManager; + HRESULT hr = CoCreateInstance( + CLSID_VirtualDesktopManager, + nullptr, + CLSCTX_ALL, + IID_PPV_ARGS(&virtualDesktopManager)); + + if (SUCCEEDED(hr)) { + BOOL isCurrent; + hr = virtualDesktopManager->IsWindowOnCurrentVirtualDesktop( + handle, + &isCurrent); + if (SUCCEEDED(hr) && !isCurrent) { + return true; + } + } + + std::vector visited; + const RECT nativeRect{ + rect.left(), + rect.top(), + rect.right(), + rect.bottom(), + }; + + for (auto curHandle = handle; + curHandle != nullptr && !ranges::contains(visited, curHandle); + curHandle = GetWindow(curHandle, GW_HWNDPREV)) { + visited.push_back(curHandle); + if (curHandle == handle) { + continue; + } + RECT testRect, intersection; + if (IsWindowVisible(curHandle) + && GetWindowRect(curHandle, &testRect) + && IntersectRect(&intersection, &nativeRect, &testRect)) { + return true; + } + } + + return false; +} + bool ShowWindowMenu(QWindow *window) { const auto pos = QCursor::pos(); diff --git a/ui/platform/win/ui_window_win.cpp b/ui/platform/win/ui_window_win.cpp index 9a12ef5..70b8399 100644 --- a/ui/platform/win/ui_window_win.cpp +++ b/ui/platform/win/ui_window_win.cpp @@ -10,7 +10,7 @@ #include "ui/platform/win/ui_window_title_win.h" #include "base/platform/base_platform_info.h" #include "base/platform/win/base_windows_safe_library.h" -#include "base/integration.h" +#include "base/debug_log.h" #include "styles/palette.h" #include @@ -75,7 +75,7 @@ bool IsTaskbarAutoHidden(LPRECT rcMon = nullptr, PUINT pEdge = nullptr) { *pEdge = pos.uEdge; } } else { - base::Integration::Instance().logMessage("Failed to get taskbar pos"); + DEBUG_LOG(("Failed to get taskbar pos")); if (pEdge) { *pEdge = ABE_BOTTOM; } diff --git a/ui/style/style_core_font.cpp b/ui/style/style_core_font.cpp index 55d8fd5..648c3b2 100644 --- a/ui/style/style_core_font.cpp +++ b/ui/style/style_core_font.cpp @@ -6,9 +6,9 @@ // #include "ui/style/style_core_font.h" -#include "ui/style/style_core_custom_font.h" -#include "ui/ui_log.h" #include "base/algorithm.h" +#include "base/debug_log.h" +#include "ui/style/style_core_custom_font.h" #include "ui/integration.h" #include @@ -49,13 +49,13 @@ bool ValidateFont(const QString &familyName, int flags = 0) { checkFont.setUnderline(flags & style::internal::FontUnderline); 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, realFamily)); + LOG(("Font Error: could not resolve '%1' font, got '%2'.").arg(familyName, realFamily)); return false; } auto metrics = QFontMetrics(checkFont); if (!metrics.height()) { - UI_LOG(("Font Error: got a zero height in '%1'.").arg(familyName)); + LOG(("Font Error: got a zero height in '%1'.").arg(familyName)); return false; } @@ -65,13 +65,13 @@ bool ValidateFont(const QString &familyName, int flags = 0) { bool LoadCustomFont(const QString &filePath, const QString &familyName, int flags = 0) { auto regularId = QFontDatabase::addApplicationFont(filePath); if (regularId < 0) { - UI_LOG(("Font Error: could not add '%1'.").arg(filePath)); + LOG(("Font Error: could not add '%1'.").arg(filePath)); return false; } const auto found = [&] { for (auto &family : QFontDatabase::applicationFontFamilies(regularId)) { - UI_LOG(("Font: from '%1' loaded '%2'").arg(filePath, family)); + LOG(("Font: from '%1' loaded '%2'").arg(filePath, family)); if (!family.trimmed().compare(familyName, Qt::CaseInsensitive)) { return true; } @@ -79,7 +79,7 @@ bool LoadCustomFont(const QString &filePath, const QString &familyName, int flag return false; }(); if (!found) { - UI_LOG(("Font Error: could not locate '%1' font in '%2'.").arg(familyName, filePath)); + LOG(("Font Error: could not locate '%1' font in '%2'.").arg(familyName, filePath)); return false; } @@ -194,11 +194,6 @@ void StartFonts() { style_InitFontsResource(); - const auto integrationExists = Ui::Integration::Exists(); - if (integrationExists) { - Ui::Integration::Instance().startFontsBegin(); - } - #ifndef DESKTOP_APP_USE_PACKAGED_FONTS if (!UseSystemFont) { bool areGood[FontTypesCount] = { false }; @@ -222,7 +217,7 @@ void StartFonts() { if (!areGood[i]) { if (ValidateFont(fallback, flags)) { Overrides[i] = fallback; - UI_LOG(("Fonts Info: Using '%1' instead of '%2'.").arg(fallback).arg(name)); + LOG(("Fonts Info: Using '%1' instead of '%2'.").arg(fallback).arg(name)); } } // Disable default fallbacks to Segoe UI, see: @@ -264,10 +259,6 @@ void StartFonts() { auto appFont = QApplication::font(); appFont.setStyleStrategy(QFont::PreferQuality); QApplication::setFont(appFont); - - if (integrationExists) { - Ui::Integration::Instance().startFontsEnd(); - } } QString GetPossibleEmptyOverride(int32 flags) { diff --git a/ui/text/text_block.cpp b/ui/text/text_block.cpp index d62a114..b07c14b 100644 --- a/ui/text/text_block.cpp +++ b/ui/text/text_block.cpp @@ -7,7 +7,7 @@ #include "ui/text/text_block.h" #include "styles/style_basic.h" -#include "base/integration.h" +#include "base/debug_log.h" #include @@ -161,8 +161,8 @@ void addNextCluster( for (auto i = 0; i < pos; ++i) { str.append(QString::number(logClusters[i])); } - base::Integration::Instance().logAssertionViolation(QString("text: %1 (from: %2, length: %3) part: %4").arg(DebugCurrentParsingString).arg(DebugCurrentParsingFrom).arg(DebugCurrentParsingLength).arg(DebugCurrentParsingPart)); - base::Integration::Instance().logAssertionViolation(QString("pos: %1, end: %2, glyphPosition: %3, glyphCount: %4, lineLength: %5, num_glyphs: %6, logClusters[0..pos]: %7").arg(pos).arg(end).arg(glyphPosition).arg(glyphCount).arg(line.length).arg(current.num_glyphs).arg(str.join(","))); + LOG(("text: %1 (from: %2, length: %3) part: %4").arg(DebugCurrentParsingString).arg(DebugCurrentParsingFrom).arg(DebugCurrentParsingLength).arg(DebugCurrentParsingPart)); + LOG(("pos: %1, end: %2, glyphPosition: %3, glyphCount: %4, lineLength: %5, num_glyphs: %6, logClusters[0..pos]: %7").arg(pos).arg(end).arg(glyphPosition).arg(glyphCount).arg(line.length).arg(current.num_glyphs).arg(str.join(","))); Unexpected("Values in addNextCluster()"); } Q_ASSERT((pos == end && glyphPosition == current.num_glyphs) diff --git a/ui/text/text_entity.h b/ui/text/text_entity.h index aa04175..d251bd4 100644 --- a/ui/text/text_entity.h +++ b/ui/text/text_entity.h @@ -301,17 +301,22 @@ QStringList PrepareSearchWords(const QString &query, const QRegularExpression *S bool CutPart(TextWithEntities &sending, TextWithEntities &left, int limit); struct MentionNameFields { - MentionNameFields(int32 userId = 0, uint64 accessHash = 0) : userId(userId), accessHash(accessHash) { + MentionNameFields(uint64 userId = 0, uint64 accessHash = 0) + : userId(userId), accessHash(accessHash) { } - int32 userId = 0; + uint64 userId = 0; uint64 accessHash = 0; }; + inline MentionNameFields MentionNameDataToFields(const QString &data) { auto components = data.split('.'); if (!components.isEmpty()) { - return { components.at(0).toInt(), (components.size() > 1) ? components.at(1).toULongLong() : 0 }; + return { + components.at(0).toULongLong(), + (components.size() > 1) ? components.at(1).toULongLong() : 0 + }; } - return MentionNameFields {}; + return MentionNameFields{}; } inline QString MentionNameDataFromFields(const MentionNameFields &fields) { diff --git a/ui/ui_log.cpp b/ui/ui_log.cpp deleted file mode 100644 index 648985e..0000000 --- a/ui/ui_log.cpp +++ /dev/null @@ -1,19 +0,0 @@ -// This file is part of Desktop App Toolkit, -// a set of libraries for developing nice desktop applications. -// -// For license and copyright information please follow this link: -// https://github.com/desktop-app/legal/blob/master/LEGAL -// -#include "ui/ui_log.h" - -#include "ui/integration.h" - -namespace Ui { - -void WriteLogEntry(const QString &message) { - if (Integration::Exists()) { - Integration::Instance().writeLogEntry(message); - } -} - -} // namespace Ui diff --git a/ui/ui_log.h b/ui/ui_log.h deleted file mode 100644 index bca1bd1..0000000 --- a/ui/ui_log.h +++ /dev/null @@ -1,15 +0,0 @@ -// This file is part of Desktop App Toolkit, -// a set of libraries for developing nice desktop applications. -// -// For license and copyright information please follow this link: -// https://github.com/desktop-app/legal/blob/master/LEGAL -// -#pragma once - -namespace Ui { - -void WriteLogEntry(const QString &message); - -} // namespace Ui - -#define UI_LOG(message) (::Ui::WriteLogEntry(QString message)) diff --git a/ui/ui_utility.cpp b/ui/ui_utility.cpp index 6fdbafe..097cd74 100644 --- a/ui/ui_utility.cpp +++ b/ui/ui_utility.cpp @@ -6,11 +6,13 @@ // #include "ui/ui_utility.h" +#include "ui/platform/ui_platform_utility.h" #include "ui/style/style_core.h" #include #include #include +#include #include @@ -179,6 +181,36 @@ QPixmap PixmapFromImage(QImage &&image) { return QPixmap::fromImage(std::move(image), Qt::ColorOnly); } +bool IsContentVisible( + not_null widget, + const QRect &rect) { + Expects(widget->window()->windowHandle()); + + const auto activeOrNotOverlapped = [&] { + if (const auto active = widget->isActiveWindow()) { + return active; + } + + const auto mappedRect = QHighDpi::toNativePixels( + rect.isNull() + ? QRect( + widget->mapToGlobal(QPoint()), + widget->mapToGlobal( + QPoint(widget->width(), widget->height()))) + : QRect( + widget->mapToGlobal(rect.topLeft()), + widget->mapToGlobal(rect.bottomRight())), + widget->window()->windowHandle()); + + const auto overlapped = Platform::IsOverlapped(widget, mappedRect); + return overlapped.has_value() && !*overlapped; + }(); + + return activeOrNotOverlapped + && widget->isVisible() + && !widget->window()->isMinimized(); +} + void DisableCustomScaling() { qunsetenv("QT_DEVICE_PIXEL_RATIO"); qunsetenv("QT_SCALE_FACTOR"); diff --git a/ui/ui_utility.h b/ui/ui_utility.h index a174909..ece602d 100644 --- a/ui/ui_utility.h +++ b/ui/ui_utility.h @@ -187,6 +187,10 @@ QPointer MakeWeak(not_null object) { [[nodiscard]] QPixmap PixmapFromImage(QImage &&image); +[[nodiscard]] bool IsContentVisible( + not_null widget, + const QRect &rect = QRect()); + void DisableCustomScaling(); } // namespace Ui diff --git a/ui/widgets/buttons.cpp b/ui/widgets/buttons.cpp index 2760a79..206ec73 100644 --- a/ui/widgets/buttons.cpp +++ b/ui/widgets/buttons.cpp @@ -296,6 +296,11 @@ void RoundButton::setWidthChangedCallback(Fn callback) { _numbers->setWidthChangedCallback(std::move(callback)); } +void RoundButton::setBrushOverride(std::optional brush) { + _brushOverride = std::move(brush); + update(); +} + void RoundButton::finishNumbersAnimation() { if (_numbers) { _numbers->finishAnimating(); @@ -367,8 +372,12 @@ void RoundButton::paintEvent(QPaintEvent *e) { const auto radius = rounded.height() / 2; PainterHighQualityEnabler hq(p); p.setPen(Qt::NoPen); - p.setBrush(rect.color()); + p.setBrush(_brushOverride ? *_brushOverride : rect.color()->b); p.drawRoundedRect(fill, radius, radius); + } else if (_brushOverride) { + p.setPen(Qt::NoPen); + p.setBrush(*_brushOverride); + p.drawRoundedRect(fill, st::buttonRadius, st::buttonRadius); } else { rect.paint(p, fill); } @@ -377,7 +386,7 @@ void RoundButton::paintEvent(QPaintEvent *e) { auto over = isOver(); auto down = isDown(); - if (over || down) { + if (!_brushOverride && (over || down)) { drawRect(_roundRectOver); } diff --git a/ui/widgets/buttons.h b/ui/widgets/buttons.h index 9b1e3f8..239f6a7 100644 --- a/ui/widgets/buttons.h +++ b/ui/widgets/buttons.h @@ -130,6 +130,7 @@ public: setNumbersText(QString::number(numbers), numbers); } void setWidthChangedCallback(Fn callback); + void setBrushOverride(std::optional brush); void finishNumbersAnimation(); int contentWidth() const; @@ -165,6 +166,7 @@ private: int _fullWidthOverride = 0; const style::RoundButton &_st; + std::optional _brushOverride; RoundRect _roundRect; RoundRect _roundRectOver;