From d8c9802d774f2e7d06c09053ca96c1518cf2bf6e Mon Sep 17 00:00:00 2001 From: Igor Sirotin Date: Wed, 2 Aug 2023 16:49:28 +0300 Subject: [PATCH] fix: Restore window after closing on MacOS (#11748) --- Entitlements.plist | 4 ++ Status.entitlements | 10 ---- src/nim_status_client.nim | 3 -- ui/main.qml | 36 ++++++++++--- .../lib/include/DOtherSide/DOtherSide.h | 1 - .../DOtherSide/Status/DockShowAppEvent.h | 27 ---------- vendor/DOtherSide/lib/src/DOtherSide.cpp | 6 --- .../DOtherSide/lib/src/Status/AppDelegate.mm | 54 +++++++++++++++---- .../lib/src/Status/DockShowAppEvent.cpp | 47 ---------------- vendor/nimqml | 2 +- 10 files changed, 76 insertions(+), 114 deletions(-) delete mode 100644 Status.entitlements delete mode 100644 vendor/DOtherSide/lib/include/DOtherSide/Status/DockShowAppEvent.h delete mode 100644 vendor/DOtherSide/lib/src/Status/DockShowAppEvent.cpp diff --git a/Entitlements.plist b/Entitlements.plist index 6cfee69ba5..a2d5d573f5 100644 --- a/Entitlements.plist +++ b/Entitlements.plist @@ -4,5 +4,9 @@ com.apple.security.device.camera + com.apple.developer.associated-domains + + applinks:status.app + diff --git a/Status.entitlements b/Status.entitlements deleted file mode 100644 index 961335b119..0000000000 --- a/Status.entitlements +++ /dev/null @@ -1,10 +0,0 @@ - - - - - com.apple.developer.associated-domains - - applinks:status.app - - - diff --git a/src/nim_status_client.nim b/src/nim_status_client.nim index 361196212e..b365d43383 100644 --- a/src/nim_status_client.nim +++ b/src/nim_status_client.nim @@ -131,7 +131,6 @@ proc mainProc() = QResource.registerResource(app.applicationDirPath & resourcesPath) # Register events objects - let dockShowAppEvent = newStatusDockShowAppEventObject(singletonInstance.engine) let osThemeEvent = newStatusOSThemeEventObject(singletonInstance.engine) if not main_constants.IS_MACOS: @@ -149,7 +148,6 @@ proc mainProc() = singletonInstance.engine.setRootContextProperty("signals", signalsManagerQVariant) singletonInstance.engine.setRootContextProperty("production", isProductionQVariant) - app.installEventFilter(dockShowAppEvent) app.installEventFilter(osThemeEvent) app.installEventFilter(urlSchemeEvent) @@ -161,7 +159,6 @@ proc mainProc() = isExperimentalQVariant.delete() signalsManagerQVariant.delete() networkAccessFactory.delete() - dockShowAppEvent.delete() osThemeEvent.delete() appController.delete() statusFoundation.delete() diff --git a/ui/main.qml b/ui/main.qml index d0bc7fa7ad..5044476e6f 100644 --- a/ui/main.qml +++ b/ui/main.qml @@ -92,6 +92,11 @@ StatusWindow { onWidthChanged: Qt.callLater(storeAppState) onHeightChanged: Qt.callLater(storeAppState) + QtObject { + id: d + property int previousApplicationState: -1 + } + Action { shortcut: StandardKey.FullScreen onTriggered: { @@ -220,6 +225,20 @@ StatusWindow { } } + // On MacOS, explicitely restore the window on activating + Connections { + target: Qt.application + enabled: Qt.platform.os === Constants.mac + function onStateChanged() { + if (Qt.application.state == d.previousApplicationState + && Qt.application.state == Qt.ApplicationActive) { + applicationWindow.visible = true + applicationWindow.showNormal() + } + d.previousApplicationState = Qt.application.state + } + } + //TODO remove direct backend access Connections { target: singleInstance @@ -358,15 +377,16 @@ StatusWindow { onClose: { if (loader.sourceComponent != app) { + Qt.quit() + return + } + + if (localAccountSensitiveSettings.quitOnClose) { Qt.quit(); - } - else if (loader.sourceComponent == app) { - if (localAccountSensitiveSettings.quitOnClose) { - Qt.quit(); - } else { - applicationWindow.visible = false; - } - } + return + } + + applicationWindow.visible = false; } onMinimised: { diff --git a/vendor/DOtherSide/lib/include/DOtherSide/DOtherSide.h b/vendor/DOtherSide/lib/include/DOtherSide/DOtherSide.h index 885b985037..31dde1575c 100644 --- a/vendor/DOtherSide/lib/include/DOtherSide/DOtherSide.h +++ b/vendor/DOtherSide/lib/include/DOtherSide/DOtherSide.h @@ -998,7 +998,6 @@ DOS_API void DOS_CALL dos_singleinstance_delete(DosSingleInstance *vptr); #pragma region Events exposed methods -DOS_API DosEvent* dos_event_create_showAppEvent(DosQQmlApplicationEngine* vptr); DOS_API DosEvent* dos_event_create_osThemeEvent(DosQQmlApplicationEngine* vptr); DOS_API DosEvent* dos_event_create_urlSchemeEvent(); DOS_API void dos_event_delete(DosEvent* vptr); diff --git a/vendor/DOtherSide/lib/include/DOtherSide/Status/DockShowAppEvent.h b/vendor/DOtherSide/lib/include/DOtherSide/Status/DockShowAppEvent.h deleted file mode 100644 index f2833535a7..0000000000 --- a/vendor/DOtherSide/lib/include/DOtherSide/Status/DockShowAppEvent.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef STATUS_DOCK_SHOW_APP_EVENT_H -#define STATUS_DOCK_SHOW_APP_EVENT_H - -#include "../DOtherSideTypes.h" - -#include -#include - -namespace Status -{ - class DockShowAppEvent : public QObject - { - Q_OBJECT - - public: - DockShowAppEvent(DosQQmlApplicationEngine* vptr, QObject* parent = nullptr); - - protected: - bool eventFilter(QObject* obj, QEvent* event) override; - - private: - Qt::ApplicationState m_prevAppState; - QQmlApplicationEngine* m_engine; - }; -} - -#endif diff --git a/vendor/DOtherSide/lib/src/DOtherSide.cpp b/vendor/DOtherSide/lib/src/DOtherSide.cpp index 578b340816..b0989ee3cc 100644 --- a/vendor/DOtherSide/lib/src/DOtherSide.cpp +++ b/vendor/DOtherSide/lib/src/DOtherSide.cpp @@ -68,7 +68,6 @@ #include "DOtherSide/DosQQuickImageProvider.h" #include "DOtherSide/DOtherSideSingleInstance.h" -#include "DOtherSide/Status/DockShowAppEvent.h" #include "DOtherSide/Status/OSThemeEvent.h" #include "DOtherSide/Status/UrlSchemeEvent.h" #include "DOtherSide/Status/OSNotification.h" @@ -1553,11 +1552,6 @@ bool dos_singleinstance_isfirst(DosSingleInstance *vptr) } #pragma region Events -::DosEvent* dos_event_create_showAppEvent(::DosQQmlApplicationEngine* vptr) -{ - auto engine = static_cast(vptr); - return new Status::DockShowAppEvent(engine); -} ::DosEvent* dos_event_create_osThemeEvent(::DosQQmlApplicationEngine* vptr) { diff --git a/vendor/DOtherSide/lib/src/Status/AppDelegate.mm b/vendor/DOtherSide/lib/src/Status/AppDelegate.mm index de91c3fca1..7179cf22e4 100644 --- a/vendor/DOtherSide/lib/src/Status/AppDelegate.mm +++ b/vendor/DOtherSide/lib/src/Status/AppDelegate.mm @@ -2,23 +2,25 @@ #include #include +#include #import +#import - -@interface AppDelegate: NSObject +@interface StatusApplicationDelegate: NSObject - (BOOL)application:(NSApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray> *restorableObjects))restorationHandler; @end -@implementation AppDelegate -- (BOOL)application:(NSApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray> *))restorationHandler { +@implementation StatusApplicationDelegate +- (BOOL)application:(NSApplication *)application + continueUserActivity:(NSUserActivity *)userActivity + restorationHandler:(void (^)(NSArray> *))restorationHandler { if (userActivity.activityType == NSUserActivityTypeBrowsingWeb) { NSURL *url = userActivity.webpageURL; - if (!url) { + if (!url) return FALSE; - } QUrl deeplink = QUrl::fromNSURL(url); // set it to nim return TRUE; @@ -26,14 +28,44 @@ continueUserActivity:(NSUserActivity *)userActivity return FALSE; } +- (void)applicationDidFinishLaunching:(NSNotification *)notification +{ + qDebug() << "StatusApplicationDelegate::applicationDidFinishLaunching"; +} + @end namespace app_delegate { -void install() -{ - NSApplication* applicationShared = [NSApplication sharedApplication]; - [applicationShared setDelegate:([[[AppDelegate alloc] init] autorelease])]; +void swizzle_appdelegate_method(SEL selector) { + Class originalClass = [NSApplication sharedApplication].delegate.class; + Class swizzledClass = [StatusApplicationDelegate class]; + + Method originalMethod = class_getInstanceMethod(originalClass, selector); + Method swizzledMethod = class_getInstanceMethod(swizzledClass, selector); + + method_exchangeImplementations(originalMethod, swizzledMethod); } -} \ No newline at end of file +void install() +{ + /* + A simple solution to implement custom ApplicationDelegate would be to call `NSApplication::setDelegate` + with an instantse of StatusApplicationDelegate. But this will override Qt's ApplicationDelegate implementation + `QCocoaApplicationDelegate` (qtbase/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm). + + Overriding breaks some Qt events like `QApplicationStateChangeEvent`. And I suppose (and pretty sure) + that this might also break other things. + + Would be cool to simply inherit `QCocoaApplicationDelegate`, but it's only available in Qt's sources. + + The solution here is to use "method swizzling" technique. + We replace the method of a class with own implementation. + + In future we could contribute to Qt to add "continueUserActivity" event. + */ + swizzle_appdelegate_method(@selector(applicationDidFinishLaunching:)); // for testing purposes (ApplicationDelegate works, method swizzled) + swizzle_appdelegate_method(@selector(application:continueUserActivity:restorationHandler:)); // for Univeral Links support +} + +} diff --git a/vendor/DOtherSide/lib/src/Status/DockShowAppEvent.cpp b/vendor/DOtherSide/lib/src/Status/DockShowAppEvent.cpp deleted file mode 100644 index 2b1aa170b6..0000000000 --- a/vendor/DOtherSide/lib/src/Status/DockShowAppEvent.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "DOtherSide/Status/DockShowAppEvent.h" - -#include - - -/* - Code here is exactly the same as it was before, logic is not changed. I only - put it in another form, nothing else. To match an improved flow for - installing filters. -*/ - -using namespace Status; - -DockShowAppEvent::DockShowAppEvent(DosQQmlApplicationEngine* vptr, - QObject* parent) - : QObject(parent) -{ - m_engine = static_cast(vptr); -} - -bool DockShowAppEvent::eventFilter(QObject* obj, QEvent* event) -{ -#ifdef Q_OS_MACOS - if (event->type() == QEvent::ApplicationStateChange) - { - auto ev = static_cast(event); - if (m_prevAppState == Qt::ApplicationActive - && ev->applicationState() == Qt::ApplicationActive) - { - QObject* topLevelObj = m_engine->rootObjects().value(0); - if(topLevelObj && topLevelObj->objectName() == "mainWindow") - { - QQuickWindow* window = qobject_cast(topLevelObj); - if(window) - { - window->setVisible(true); - window->showNormal(); - return true; - } - } - } - m_prevAppState = ev->applicationState(); - } -#endif - - return QObject::eventFilter(obj, event); -} diff --git a/vendor/nimqml b/vendor/nimqml index ab78548aa3..3f61ec4add 160000 --- a/vendor/nimqml +++ b/vendor/nimqml @@ -1 +1 @@ -Subproject commit ab78548aa3838fb51aff791d14203e29849ed200 +Subproject commit 3f61ec4addbd1e59c6936658258214a58b768c4a