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>
<key>com.apple.security.device.camera</key>
<true/>
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:status.app</string>
</array>
</dict>
</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)
# 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()

View File

@ -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: {

View File

@ -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);

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/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<QQmlApplicationEngine*>(vptr);
return new Status::DockShowAppEvent(engine);
}
::DosEvent* dos_event_create_osThemeEvent(::DosQQmlApplicationEngine* vptr)
{

View File

@ -2,23 +2,25 @@
#include <QString>
#include <QUrl>
#include <QDebug>
#import <AppKit/NSApplication.h>
#import <objc/runtime.h>
@interface AppDelegate: NSObject <NSApplicationDelegate>
@interface StatusApplicationDelegate: NSObject <NSApplicationDelegate>
- (BOOL)application:(NSApplication *)application
continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:(void (^)(NSArray<id<NSUserActivityRestoring>> *restorableObjects))restorationHandler;
@end
@implementation AppDelegate
- (BOOL)application:(NSApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray<id<NSUserActivityRestoring>> *))restorationHandler {
@implementation StatusApplicationDelegate
- (BOOL)application:(NSApplication *)application
continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:(void (^)(NSArray<id<NSUserActivityRestoring>> *))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);
}
}
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
}
}

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