fix: Restore window after closing on MacOS (#11748)
This commit is contained in:
parent
ea91cd605f
commit
d8c9802d77
|
@ -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>
|
||||||
|
|
|
@ -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>
|
|
|
@ -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()
|
||||||
|
|
30
ui/main.qml
30
ui/main.qml
|
@ -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()
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit ab78548aa3838fb51aff791d14203e29849ed200
|
Subproject commit 3f61ec4addbd1e59c6936658258214a58b768c4a
|
Loading…
Reference in New Issue