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 549a14341c..5526231487 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 4961a5bc8e..236a4dbf2b 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 810c6c7f6b..46051c53e4 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 c220df79b5..0dd10ec589 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 b8ed190e1b..c62fd32f65 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 9000abbe16..88357fce33 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 301c287848..6d5f2b64be 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 cd4642d131..d70bcffea0 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 df2da33719..157c033834 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 89741484b0..d6e8e5d92b 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 62daf8fcea..838203880a 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 73290e9919..9751a4a816 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 50b8e0af31..e303272e1f 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 fad961c4e1..f5cca73ce5 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 5171b10c4a..48516a402d 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 1a5fad6f05..f452fcd4d4 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 1f2417892f..34736afbbb 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); }