fix: Restore window after closing on MacOS (#11748)

This commit is contained in:
Igor Sirotin 2023-08-02 16:49:28 +03:00 committed by GitHub
parent ea91cd605f
commit d8c9802d77
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 76 additions and 114 deletions

View File

@ -4,5 +4,9 @@
<dict> <dict>
<key>com.apple.security.device.camera</key> <key>com.apple.security.device.camera</key>
<true/> <true/>
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:status.app</string>
</array>
</dict> </dict>
</plist> </plist>

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:status.app</string>
</array>
</dict>
</plist>

View File

@ -131,7 +131,6 @@ proc mainProc() =
QResource.registerResource(app.applicationDirPath & resourcesPath) QResource.registerResource(app.applicationDirPath & resourcesPath)
# Register events objects # Register events objects
let dockShowAppEvent = newStatusDockShowAppEventObject(singletonInstance.engine)
let osThemeEvent = newStatusOSThemeEventObject(singletonInstance.engine) let osThemeEvent = newStatusOSThemeEventObject(singletonInstance.engine)
if not main_constants.IS_MACOS: if not main_constants.IS_MACOS:
@ -149,7 +148,6 @@ proc mainProc() =
singletonInstance.engine.setRootContextProperty("signals", signalsManagerQVariant) singletonInstance.engine.setRootContextProperty("signals", signalsManagerQVariant)
singletonInstance.engine.setRootContextProperty("production", isProductionQVariant) singletonInstance.engine.setRootContextProperty("production", isProductionQVariant)
app.installEventFilter(dockShowAppEvent)
app.installEventFilter(osThemeEvent) app.installEventFilter(osThemeEvent)
app.installEventFilter(urlSchemeEvent) app.installEventFilter(urlSchemeEvent)
@ -161,7 +159,6 @@ proc mainProc() =
isExperimentalQVariant.delete() isExperimentalQVariant.delete()
signalsManagerQVariant.delete() signalsManagerQVariant.delete()
networkAccessFactory.delete() networkAccessFactory.delete()
dockShowAppEvent.delete()
osThemeEvent.delete() osThemeEvent.delete()
appController.delete() appController.delete()
statusFoundation.delete() statusFoundation.delete()

View File

@ -92,6 +92,11 @@ StatusWindow {
onWidthChanged: Qt.callLater(storeAppState) onWidthChanged: Qt.callLater(storeAppState)
onHeightChanged: Qt.callLater(storeAppState) onHeightChanged: Qt.callLater(storeAppState)
QtObject {
id: d
property int previousApplicationState: -1
}
Action { Action {
shortcut: StandardKey.FullScreen shortcut: StandardKey.FullScreen
onTriggered: { 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 //TODO remove direct backend access
Connections { Connections {
target: singleInstance target: singleInstance
@ -358,16 +377,17 @@ StatusWindow {
onClose: { onClose: {
if (loader.sourceComponent != app) { if (loader.sourceComponent != app) {
Qt.quit(); Qt.quit()
return
} }
else if (loader.sourceComponent == app) {
if (localAccountSensitiveSettings.quitOnClose) { if (localAccountSensitiveSettings.quitOnClose) {
Qt.quit(); Qt.quit();
} else { return
}
applicationWindow.visible = false; applicationWindow.visible = false;
} }
}
}
onMinimised: { onMinimised: {
applicationWindow.showMinimized() applicationWindow.showMinimized()

View File

@ -998,7 +998,6 @@ DOS_API void DOS_CALL dos_singleinstance_delete(DosSingleInstance *vptr);
#pragma region Events exposed methods #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_osThemeEvent(DosQQmlApplicationEngine* vptr);
DOS_API DosEvent* dos_event_create_urlSchemeEvent(); DOS_API DosEvent* dos_event_create_urlSchemeEvent();
DOS_API void dos_event_delete(DosEvent* vptr); DOS_API void dos_event_delete(DosEvent* vptr);

View File

@ -1,27 +0,0 @@
#ifndef STATUS_DOCK_SHOW_APP_EVENT_H
#define STATUS_DOCK_SHOW_APP_EVENT_H
#include "../DOtherSideTypes.h"
#include <QObject>
#include <QQmlApplicationEngine>
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

View File

@ -68,7 +68,6 @@
#include "DOtherSide/DosQQuickImageProvider.h" #include "DOtherSide/DosQQuickImageProvider.h"
#include "DOtherSide/DOtherSideSingleInstance.h" #include "DOtherSide/DOtherSideSingleInstance.h"
#include "DOtherSide/Status/DockShowAppEvent.h"
#include "DOtherSide/Status/OSThemeEvent.h" #include "DOtherSide/Status/OSThemeEvent.h"
#include "DOtherSide/Status/UrlSchemeEvent.h" #include "DOtherSide/Status/UrlSchemeEvent.h"
#include "DOtherSide/Status/OSNotification.h" #include "DOtherSide/Status/OSNotification.h"
@ -1553,11 +1552,6 @@ bool dos_singleinstance_isfirst(DosSingleInstance *vptr)
} }
#pragma region Events #pragma region Events
::DosEvent* dos_event_create_showAppEvent(::DosQQmlApplicationEngine* vptr)
{
auto engine = static_cast<QQmlApplicationEngine*>(vptr);
return new Status::DockShowAppEvent(engine);
}
::DosEvent* dos_event_create_osThemeEvent(::DosQQmlApplicationEngine* vptr) ::DosEvent* dos_event_create_osThemeEvent(::DosQQmlApplicationEngine* vptr)
{ {

View File

@ -2,23 +2,25 @@
#include <QString> #include <QString>
#include <QUrl> #include <QUrl>
#include <QDebug>
#import <AppKit/NSApplication.h> #import <AppKit/NSApplication.h>
#import <objc/runtime.h>
@interface StatusApplicationDelegate: NSObject <NSApplicationDelegate>
@interface AppDelegate: NSObject <NSApplicationDelegate>
- (BOOL)application:(NSApplication *)application - (BOOL)application:(NSApplication *)application
continueUserActivity:(NSUserActivity *)userActivity continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:(void (^)(NSArray<id<NSUserActivityRestoring>> *restorableObjects))restorationHandler; restorationHandler:(void (^)(NSArray<id<NSUserActivityRestoring>> *restorableObjects))restorationHandler;
@end @end
@implementation AppDelegate @implementation StatusApplicationDelegate
- (BOOL)application:(NSApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray<id<NSUserActivityRestoring>> *))restorationHandler { - (BOOL)application:(NSApplication *)application
continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:(void (^)(NSArray<id<NSUserActivityRestoring>> *))restorationHandler {
if (userActivity.activityType == NSUserActivityTypeBrowsingWeb) { if (userActivity.activityType == NSUserActivityTypeBrowsingWeb) {
NSURL *url = userActivity.webpageURL; NSURL *url = userActivity.webpageURL;
if (!url) { if (!url)
return FALSE; return FALSE;
}
QUrl deeplink = QUrl::fromNSURL(url); QUrl deeplink = QUrl::fromNSURL(url);
// set it to nim // set it to nim
return TRUE; return TRUE;
@ -26,14 +28,44 @@ continueUserActivity:(NSUserActivity *)userActivity
return FALSE; return FALSE;
} }
- (void)applicationDidFinishLaunching:(NSNotification *)notification
{
qDebug() << "StatusApplicationDelegate::applicationDidFinishLaunching";
}
@end @end
namespace app_delegate { namespace app_delegate {
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);
}
void install() void install()
{ {
NSApplication* applicationShared = [NSApplication sharedApplication]; /*
[applicationShared setDelegate:([[[AppDelegate alloc] init] autorelease])]; 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
} }
} }

View File

@ -1,47 +0,0 @@
#include "DOtherSide/Status/DockShowAppEvent.h"
#include <QQuickWindow>
/*
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<QQmlApplicationEngine*>(vptr);
}
bool DockShowAppEvent::eventFilter(QObject* obj, QEvent* event)
{
#ifdef Q_OS_MACOS
if (event->type() == QEvent::ApplicationStateChange)
{
auto ev = static_cast<QApplicationStateChangeEvent*>(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<QQuickWindow *>(topLevelObj);
if(window)
{
window->setVisible(true);
window->showNormal();
return true;
}
}
}
m_prevAppState = ev->applicationState();
}
#endif
return QObject::eventFilter(obj, event);
}

2
vendor/nimqml vendored

@ -1 +1 @@
Subproject commit ab78548aa3838fb51aff791d14203e29849ed200 Subproject commit 3f61ec4addbd1e59c6936658258214a58b768c4a