From 0cdac83f8aa50af58722baa39fc5e17469eea307 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 2 May 2017 10:25:20 +0300 Subject: [PATCH] Fix calls build in Xcode. Fix calls panel in Retina. Also implement panels that appear in all spaces on macOS. Using them for calls panels and custom notifications, so it will be possible to use custom notifications in macOS as well. --- Telegram/Patches/macold/qtbase_5_3_2.diff | 23 ++++++++++++-- Telegram/Patches/qtbase_5_6_2.diff | 23 ++++++++++++-- .../SourceFiles/boxes/notifications_box.cpp | 2 +- .../calls/calls_box_controller.cpp | 14 ++++----- Telegram/SourceFiles/calls/calls_panel.cpp | 14 +++++---- Telegram/SourceFiles/calls/calls_top_bar.cpp | 13 +++++--- Telegram/SourceFiles/calls/calls_top_bar.h | 1 + .../linux/notifications_manager_linux.h | 3 -- .../platform/linux/specific_linux.cpp | 3 ++ .../platform/mac/notifications_manager_mac.mm | 10 ------ .../SourceFiles/platform/mac/specific_mac_p.h | 1 - .../platform/mac/specific_mac_p.mm | 31 ++++++++++++++++--- .../platform/platform_notifications_manager.h | 1 - .../SourceFiles/platform/platform_specific.h | 2 ++ .../platform/win/notifications_manager_win.h | 3 -- .../SourceFiles/platform/win/specific_win.cpp | 3 ++ .../window/notifications_manager_default.cpp | 7 +++-- 17 files changed, 104 insertions(+), 50 deletions(-) diff --git a/Telegram/Patches/macold/qtbase_5_3_2.diff b/Telegram/Patches/macold/qtbase_5_3_2.diff index 549a14341..552623148 100644 --- a/Telegram/Patches/macold/qtbase_5_3_2.diff +++ b/Telegram/Patches/macold/qtbase_5_3_2.diff @@ -483,7 +483,7 @@ index 83c960d..03ae969 100755 } @end diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm -index 4d0458a..fde238a 100644 +index 4d0458a..3357a5e 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -167,7 +167,8 @@ static bool isMouseEvent(NSEvent *ev) @@ -496,7 +496,24 @@ index 4d0458a..fde238a 100644 NSPoint loc = [theEvent locationInWindow]; NSRect windowFrame = [self.window legacyConvertRectFromScreen:[self.window frame]]; NSRect contentFrame = [[self.window contentView] frame]; -@@ -914,6 +915,19 @@ void QCocoaWindow::setWindowFilePath(const QString &filePath) +@@ -795,6 +796,16 @@ NSUInteger QCocoaWindow::windowStyleMask(Qt::WindowFlags flags) + { + Qt::WindowType type = static_cast(int(flags & Qt::WindowType_Mask)); + NSInteger styleMask = NSBorderlessWindowMask; ++ ++ // Patch: allow creating panels floating on all spaces in macOS. ++ // If you call "setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary" before ++ // setting the "NSNonactivatingPanelMask" bit in the style mask it won't work after that. ++ // So we need a way to set that bit before Qt sets collection behavior the way it does. ++ QVariant nonactivatingPanelMask = window()->property("_td_macNonactivatingPanelMask"); ++ if (nonactivatingPanelMask.isValid() && nonactivatingPanelMask.toBool()) { ++ styleMask |= NSNonactivatingPanelMask; ++ } ++ + if (flags & Qt::FramelessWindowHint) + return styleMask; + if ((type & Qt::Popup) == Qt::Popup) { +@@ -914,6 +925,19 @@ void QCocoaWindow::setWindowFilePath(const QString &filePath) [m_nsWindow setRepresentedFilename: fi.exists() ? QCFString::toNSString(filePath) : @""]; } @@ -516,7 +533,7 @@ index 4d0458a..fde238a 100644 void QCocoaWindow::setWindowIcon(const QIcon &icon) { QCocoaAutoReleasePool pool; -@@ -929,7 +943,10 @@ void QCocoaWindow::setWindowIcon(const QIcon &icon) +@@ -929,7 +953,10 @@ void QCocoaWindow::setWindowIcon(const QIcon &icon) if (icon.isNull()) { [iconButton setImage:nil]; } else { diff --git a/Telegram/Patches/qtbase_5_6_2.diff b/Telegram/Patches/qtbase_5_6_2.diff index 4961a5bc8..236a4dbf2 100644 --- a/Telegram/Patches/qtbase_5_6_2.diff +++ b/Telegram/Patches/qtbase_5_6_2.diff @@ -11505,7 +11505,7 @@ index 8152c57..5ddd7b3 100644 } } diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm -index c0d5904..2991fca 100644 +index c0d5904..f3c2047 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -141,7 +141,8 @@ static bool isMouseEvent(NSEvent *ev) @@ -11518,7 +11518,24 @@ index c0d5904..2991fca 100644 NSPoint loc = [theEvent locationInWindow]; NSRect windowFrame = [self.window convertRectFromScreen:[self.window frame]]; NSRect contentFrame = [[self.window contentView] frame]; -@@ -943,6 +944,19 @@ void QCocoaWindow::setWindowFilePath(const QString &filePath) +@@ -811,6 +812,16 @@ NSUInteger QCocoaWindow::windowStyleMask(Qt::WindowFlags flags) + { + Qt::WindowType type = static_cast(int(flags & Qt::WindowType_Mask)); + NSInteger styleMask = NSBorderlessWindowMask; ++ ++ // Patch: allow creating panels floating on all spaces in macOS. ++ // If you call "setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary" before ++ // setting the "NSNonactivatingPanelMask" bit in the style mask it won't work after that. ++ // So we need a way to set that bit before Qt sets collection behavior the way it does. ++ QVariant nonactivatingPanelMask = window()->property("_td_macNonactivatingPanelMask"); ++ if (nonactivatingPanelMask.isValid() && nonactivatingPanelMask.toBool()) { ++ styleMask |= NSNonactivatingPanelMask; ++ } ++ + if (flags & Qt::FramelessWindowHint) + return styleMask; + if ((type & Qt::Popup) == Qt::Popup) { +@@ -943,6 +954,19 @@ void QCocoaWindow::setWindowFilePath(const QString &filePath) [m_nsWindow setRepresentedFilename: fi.exists() ? QCFString::toNSString(filePath) : @""]; } @@ -11538,7 +11555,7 @@ index c0d5904..2991fca 100644 void QCocoaWindow::setWindowIcon(const QIcon &icon) { QMacAutoReleasePool pool; -@@ -958,7 +972,9 @@ void QCocoaWindow::setWindowIcon(const QIcon &icon) +@@ -958,7 +982,9 @@ void QCocoaWindow::setWindowIcon(const QIcon &icon) if (icon.isNull()) { [iconButton setImage:nil]; } else { diff --git a/Telegram/SourceFiles/boxes/notifications_box.cpp b/Telegram/SourceFiles/boxes/notifications_box.cpp index 810c6c7f6..46051c53e 100644 --- a/Telegram/SourceFiles/boxes/notifications_box.cpp +++ b/Telegram/SourceFiles/boxes/notifications_box.cpp @@ -46,10 +46,10 @@ public: , _cache(cache) { resize(cache.width() / cache.devicePixelRatio(), cache.height() / cache.devicePixelRatio()); + setWindowFlags(Qt::WindowFlags(Qt::FramelessWindowHint) | Qt::WindowStaysOnTopHint | Qt::BypassWindowManagerHint | Qt::NoDropShadowWindowHint | Qt::Tool); setAttribute(Qt::WA_MacAlwaysShowToolWindow); setAttribute(Qt::WA_TransparentForMouseEvents); setAttribute(Qt::WA_OpaquePaintEvent); - setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::BypassWindowManagerHint | Qt::NoDropShadowWindowHint); setWindowOpacity(0.); show(); diff --git a/Telegram/SourceFiles/calls/calls_box_controller.cpp b/Telegram/SourceFiles/calls/calls_box_controller.cpp index c220df79b..0dd10ec58 100644 --- a/Telegram/SourceFiles/calls/calls_box_controller.cpp +++ b/Telegram/SourceFiles/calls/calls_box_controller.cpp @@ -113,16 +113,16 @@ BoxController::Row::Row(HistoryItem *item) : PeerListBox::Row(item->history()->p } void BoxController::Row::paintStatusText(Painter &p, int x, int y, int outerWidth, bool selected) { - auto &icon = ([this] { + auto icon = ([this] { switch (_type) { - case Type::In: return st::callArrowIn; - case Type::Out: return st::callArrowOut; - case Type::Missed: return st::callArrowMissed; + case Type::In: return &st::callArrowIn; + case Type::Out: return &st::callArrowOut; + case Type::Missed: return &st::callArrowMissed; } Unexpected("_type in Calls::BoxController::Row::paintStatusText()."); })(); - icon.paint(p, x + st::callArrowPosition.x(), y + st::callArrowPosition.y(), outerWidth); - x += + st::callArrowPosition.x() + icon.width() + st::callArrowSkip; + icon->paint(p, x + st::callArrowPosition.x(), y + st::callArrowPosition.y(), outerWidth); + x += + st::callArrowPosition.x() + icon->width() + st::callArrowSkip; PeerListBox::Row::paintStatusText(p, x, y, outerWidth, selected); } @@ -285,7 +285,7 @@ bool BoxController::insertRow(HistoryItem *item, InsertWay way) { } } (way == InsertWay::Append) ? view()->appendRow(createRow(item)) : view()->prependRow(createRow(item)); - view()->reorderRows([](auto &begin, auto &end) { + view()->reorderRows([](auto &&begin, auto &&end) { std::sort(begin, end, [](auto &a, auto &b) { return static_cast(*a).maxItemId() > static_cast(*a).maxItemId(); }); diff --git a/Telegram/SourceFiles/calls/calls_panel.cpp b/Telegram/SourceFiles/calls/calls_panel.cpp index b8ed190e1..c62fd32f6 100644 --- a/Telegram/SourceFiles/calls/calls_panel.cpp +++ b/Telegram/SourceFiles/calls/calls_panel.cpp @@ -184,8 +184,6 @@ void Panel::refreshCallbacks() { } void Panel::initLayout() { - hide(); - setWindowFlags(Qt::WindowFlags(Qt::FramelessWindowHint) | Qt::WindowStaysOnTopHint | Qt::BypassWindowManagerHint | Qt::NoDropShadowWindowHint | Qt::Tool); setAttribute(Qt::WA_MacAlwaysShowToolWindow); setAttribute(Qt::WA_NoSystemBackground, true); @@ -203,6 +201,8 @@ void Panel::initLayout() { refreshUserPhoto(); }); createDefaultCacheImage(); + + Platform::InitOnTopPanel(this); } void Panel::processUserPhoto() { @@ -235,7 +235,7 @@ void Panel::refreshUserPhoto() { void Panel::createUserpicCache(ImagePtr image) { auto size = st::callWidth * cIntRetinaFactor(); - auto options = _useTransparency ? (Images::Option::RoundedLarge | Images::Option::RoundedTopLeft | Images::Option::RoundedTopRight | Images::Option::Smooth) : 0; + auto options = _useTransparency ? (Images::Option::RoundedLarge | Images::Option::RoundedTopLeft | Images::Option::RoundedTopRight | Images::Option::Smooth) : Images::Option::None; auto width = image->width(); auto height = image->height(); if (width > height) { @@ -245,7 +245,7 @@ void Panel::createUserpicCache(ImagePtr image) { height = qMax((height * size) / width, 1); width = size; } - _userPhoto = image->pixNoCache(width, height, options, size, size); + _userPhoto = image->pixNoCache(width, height, options, st::callWidth, st::callWidth); if (cRetina()) _userPhoto.setDevicePixelRatio(cRetinaFactor()); refreshCacheImageUserPhoto(); @@ -301,7 +301,8 @@ void Panel::createDefaultCacheImage() { if (!_useTransparency || !_cache.isNull()) { return; } - auto cache = QImage(size(), QImage::Format_ARGB32_Premultiplied); + auto cache = QImage(size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); + cache.setDevicePixelRatio(cRetinaFactor()); cache.fill(Qt::transparent); { Painter p(&cache); @@ -317,7 +318,8 @@ void Panel::createDefaultCacheImage() { } void Panel::refreshCacheImageUserPhoto() { - auto cache = QImage(size(), QImage::Format_ARGB32_Premultiplied); + auto cache = QImage(size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); + cache.setDevicePixelRatio(cRetinaFactor()); cache.fill(Qt::transparent); { Painter p(&cache); diff --git a/Telegram/SourceFiles/calls/calls_top_bar.cpp b/Telegram/SourceFiles/calls/calls_top_bar.cpp index 9000abbe1..88357fce3 100644 --- a/Telegram/SourceFiles/calls/calls_top_bar.cpp +++ b/Telegram/SourceFiles/calls/calls_top_bar.cpp @@ -45,11 +45,9 @@ void TopBar::initControls() { _mute->setClickedCallback([this] { _call->setMute(!_call->isMute()); }); + setMuted(_call->isMute()); subscribe(_call->muteChanged(), [this](bool mute) { - _mute->setIconOverride(mute ? &st::callBarUnmuteIcon : nullptr); - _mute->setRippleColorOverride(mute ? &st::callBarUnmuteRipple : nullptr); - _hangup->setRippleColorOverride(mute ? &st::callBarUnmuteRipple : nullptr); - _muted = mute; + setMuted(mute); update(); }); _info->setClickedCallback([this] { @@ -66,6 +64,13 @@ void TopBar::initControls() { updateDurationText(); } +void TopBar::setMuted(bool mute) { + _mute->setIconOverride(mute ? &st::callBarUnmuteIcon : nullptr); + _mute->setRippleColorOverride(mute ? &st::callBarUnmuteRipple : nullptr); + _hangup->setRippleColorOverride(mute ? &st::callBarUnmuteRipple : nullptr); + _muted = mute; +} + void TopBar::updateDurationText() { if (!_call) { return; diff --git a/Telegram/SourceFiles/calls/calls_top_bar.h b/Telegram/SourceFiles/calls/calls_top_bar.h index 301c28784..6d5f2b64b 100644 --- a/Telegram/SourceFiles/calls/calls_top_bar.h +++ b/Telegram/SourceFiles/calls/calls_top_bar.h @@ -48,6 +48,7 @@ private: void updateDurationText(); void updateControlsGeometry(); void startDurationUpdateTimer(TimeMs currentDuration); + void setMuted(bool mute); base::weak_unique_ptr _call; diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h index cd4642d13..d70bcffea 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h @@ -25,9 +25,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org namespace Platform { namespace Notifications { -inline void CustomNotificationShownHook(QWidget *widget) { -} - inline bool SkipAudio() { return false; } diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp index df2da3371..157c03383 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp @@ -405,6 +405,9 @@ bool TransparentWindowsSupported(QPoint globalPosition) { return false; } +void InitOnTopPanel(QWidget *panel) { +} + namespace ThirdParty { void start() { diff --git a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm index 89741484b..d6e8e5d92 100644 --- a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm +++ b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm @@ -168,16 +168,6 @@ void FlashBounce() { [NSApp requestUserAttention:NSInformationalRequest]; } -void CustomNotificationShownHook(QWidget *widget) { - widget->hide(); - objc_holdOnTop(widget->winId()); - widget->show(); - psShowOverAll(widget, false); - if (auto window = App::wnd()) { - window->customNotificationCreated(widget); - } -} - class Manager::Private : public QObject, private base::Subscriber { public: Private(Manager *manager); diff --git a/Telegram/SourceFiles/platform/mac/specific_mac_p.h b/Telegram/SourceFiles/platform/mac/specific_mac_p.h index 62daf8fce..838203880 100644 --- a/Telegram/SourceFiles/platform/mac/specific_mac_p.h +++ b/Telegram/SourceFiles/platform/mac/specific_mac_p.h @@ -20,7 +20,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org // e is NSEvent* bool objc_handleMediaKeyEvent(void *e); -void objc_holdOnTop(WId winId); bool objc_darkMode(); void objc_showOverAll(WId winId, bool canFocus = true); void objc_bringToBack(WId winId); diff --git a/Telegram/SourceFiles/platform/mac/specific_mac_p.mm b/Telegram/SourceFiles/platform/mac/specific_mac_p.mm index 73290e991..9751a4a81 100644 --- a/Telegram/SourceFiles/platform/mac/specific_mac_p.mm +++ b/Telegram/SourceFiles/platform/mac/specific_mac_p.mm @@ -36,7 +36,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org namespace { -constexpr auto kIgnoreActivationTimeoutMs = 1500; +constexpr auto kIgnoreActivationTimeoutMs = 500; } // namespace @@ -196,13 +196,34 @@ void SetWatchingMediaKeys(bool watching) { } } -} // namespace Platform +void InitOnTopPanel(QWidget *panel) { + Expects(!panel->windowHandle()); -void objc_holdOnTop(WId winId) { - NSWindow *wnd = [reinterpret_cast(winId) window]; - [wnd setHidesOnDeactivate:NO]; + // Force creating windowHandle() without creating the platform window yet. + panel->setAttribute(Qt::WA_NativeWindow, true); + panel->windowHandle()->setProperty("_td_macNonactivatingPanelMask", QVariant(true)); + panel->setAttribute(Qt::WA_NativeWindow, false); + + panel->createWinId(); + + auto platformWindow = [reinterpret_cast(panel->winId()) window]; + t_assert([platformWindow isKindOfClass:[NSPanel class]]); + + auto platformPanel = static_cast(platformWindow); + [platformPanel setLevel:NSPopUpMenuWindowLevel]; + [platformPanel setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces|NSWindowCollectionBehaviorStationary|NSWindowCollectionBehaviorFullScreenAuxiliary|NSWindowCollectionBehaviorIgnoresCycle]; + [platformPanel setFloatingPanel:YES]; + [platformPanel setHidesOnDeactivate:NO]; + + if (auto window = App::wnd()) { + window->customNotificationCreated(panel); + } + + objc_ignoreApplicationActivationRightNow(); } +} // namespace Platform + bool objc_darkMode() { bool result = false; @autoreleasepool { diff --git a/Telegram/SourceFiles/platform/platform_notifications_manager.h b/Telegram/SourceFiles/platform/platform_notifications_manager.h index 50b8e0af3..e303272e1 100644 --- a/Telegram/SourceFiles/platform/platform_notifications_manager.h +++ b/Telegram/SourceFiles/platform/platform_notifications_manager.h @@ -25,7 +25,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org namespace Platform { namespace Notifications { -void CustomNotificationShownHook(QWidget *widget); bool SkipAudio(); bool SkipToast(); diff --git a/Telegram/SourceFiles/platform/platform_specific.h b/Telegram/SourceFiles/platform/platform_specific.h index fad961c4e..f5cca73ce 100644 --- a/Telegram/SourceFiles/platform/platform_specific.h +++ b/Telegram/SourceFiles/platform/platform_specific.h @@ -36,6 +36,8 @@ void finish(); void SetWatchingMediaKeys(bool watching); bool TransparentWindowsSupported(QPoint globalPosition); +void InitOnTopPanel(QWidget *panel); + namespace ThirdParty { void start(); diff --git a/Telegram/SourceFiles/platform/win/notifications_manager_win.h b/Telegram/SourceFiles/platform/win/notifications_manager_win.h index 5171b10c4..48516a402 100644 --- a/Telegram/SourceFiles/platform/win/notifications_manager_win.h +++ b/Telegram/SourceFiles/platform/win/notifications_manager_win.h @@ -25,9 +25,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org namespace Platform { namespace Notifications { -inline void CustomNotificationShownHook(QWidget *widget) { -} - class Manager : public Window::Notifications::NativeManager { public: Manager(Window::Notifications::System *system); diff --git a/Telegram/SourceFiles/platform/win/specific_win.cpp b/Telegram/SourceFiles/platform/win/specific_win.cpp index 1a5fad6f0..f452fcd4d 100644 --- a/Telegram/SourceFiles/platform/win/specific_win.cpp +++ b/Telegram/SourceFiles/platform/win/specific_win.cpp @@ -534,6 +534,9 @@ bool TransparentWindowsSupported(QPoint globalPosition) { return true; } +void InitOnTopPanel(QWidget *panel) { +} + namespace ThirdParty { void start() { diff --git a/Telegram/SourceFiles/window/notifications_manager_default.cpp b/Telegram/SourceFiles/window/notifications_manager_default.cpp index 1f2417892..34736afbb 100644 --- a/Telegram/SourceFiles/window/notifications_manager_default.cpp +++ b/Telegram/SourceFiles/window/notifications_manager_default.cpp @@ -199,7 +199,6 @@ void Manager::showNextFromQueue() { queued.item, queued.forwardedCount, startPosition, startShift, shiftDirection); - Platform::Notifications::CustomNotificationShownHook(notification.get()); _notifications.push_back(std::move(notification)); --count; } while (count > 0 && !_queuedNotifications.empty()); @@ -353,9 +352,11 @@ Widget::Widget(Manager *manager, QPoint startPosition, int shift, Direction shif , _a_shift(animation(this, &Widget::step_shift)) { setWindowOpacity(0.); - setWindowFlags(qFlags(Qt::Tool) | Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::BypassWindowManagerHint | Qt::NoDropShadowWindowHint); - setAttribute(Qt::WA_OpaquePaintEvent); + setWindowFlags(Qt::WindowFlags(Qt::FramelessWindowHint) | Qt::WindowStaysOnTopHint | Qt::BypassWindowManagerHint | Qt::NoDropShadowWindowHint | Qt::Tool); setAttribute(Qt::WA_MacAlwaysShowToolWindow); + setAttribute(Qt::WA_OpaquePaintEvent); + + Platform::InitOnTopPanel(this); _a_opacity.start([this] { opacityAnimationCallback(); }, 0., 1., st::notifyFastAnim); }