diff --git a/src/LayoutManager.qml b/src/LayoutManager.qml index d971edb61ba14f98d7063366572413e247ba4c46..5273e1ec76e3681a789dcb39e00ff72bc4798a21 100644 --- a/src/LayoutManager.qml +++ b/src/LayoutManager.qml @@ -20,6 +20,8 @@ import QtQuick import QtQuick.Controls import net.jami.Adapters 1.1 +import net.jami.Enums 1.1 +import net.jami.Constants 1.1 import "mainview/components" @@ -49,6 +51,73 @@ QtObject { } visibility = priv.windowedVisibility } + appWindow.allowVisibleWindow = true + } + + // Start in a hidden state. + function startMinimized(visibilitySetting) { + // Save the loaded setting for when the app is restored. + priv.windowedVisibility = visibilitySetting + appWindow.allowVisibleWindow = false + appWindow.hide(); + } + + // Close to a hidden state. + function closeToTray(visibilitySetting = undefined) { + // Save the current visibility. + priv.windowedVisibility = visibility + appWindow.hide(); + } + + // Save the window geometry and visibility settings. + function saveWindowSettings() { + var geometry = Qt.rect(appWindow.x, appWindow.y, + appWindow.width, appWindow.height) + AppSettingsManager.setValue(Settings.WindowGeometry, geometry) + + // If closed-to-tray or minimized, save the cached windowedVisibility + // value instead. + if (isHidden) { + AppSettingsManager.setValue(Settings.WindowState, priv.windowedVisibility) + } else { + AppSettingsManager.setValue(Settings.WindowState, visibility) + } + } + + // Restore the window geometry and visibility settings. + function restoreWindowSettings() { + var geometry = AppSettingsManager.getValue(Settings.WindowGeometry) + + // Position. + if (!isNaN(geometry.x) && !isNaN(geometry.y)) { + appWindow.x = geometry.x + appWindow.y = geometry.y + } + + // Dimensions. + appWindow.width = geometry.width ? + geometry.width : + JamiTheme.mainViewPreferredWidth + appWindow.height = geometry.height ? + geometry.height : + JamiTheme.mainViewPreferredHeight + appWindow.minimumWidth = JamiTheme.mainViewMinWidth + appWindow.minimumHeight = JamiTheme.mainViewMinHeight + + // State. + const visibilityStr = AppSettingsManager.getValue(Settings.WindowState) + var visibilitySetting = parseInt(visibilityStr) + + // We should never restore a hidden state here. Default to normal + // windowed state in such a case. This shouldn't happen. + if (visibilitySetting === Window.Hidden) { + visibilitySetting = Window.Windowed + } + if (MainApplication.startMinimized) { + startMinimized(visibilitySetting) + } else { + visibility = visibilitySetting + } } // Adds an item to the fullscreen item stack. Automatically puts diff --git a/src/MainApplicationWindow.qml b/src/MainApplicationWindow.qml index 7808b21aab3a26a1f8039ff9a8546e52939d1468..fa337d5eeacc5a5a0a20751cba0ed9678a19ee52 100644 --- a/src/MainApplicationWindow.qml +++ b/src/MainApplicationWindow.qml @@ -53,6 +53,7 @@ ApplicationWindow { } property bool windowSettingsLoaded: false + property bool allowVisibleWindow: true function checkLoadedSource() { var sourceString = mainApplicationLoader.source.toString() @@ -83,19 +84,18 @@ ApplicationWindow { if (force || !UtilsAdapter.getAppValue(Settings.MinimizeOnClose) || !UtilsAdapter.getAccountListSize()) { // Save the window geometry and state before quitting. - var geometry = Qt.rect(appWindow.x, appWindow.y, - appWindow.width, appWindow.height) - AppSettingsManager.setValue(Settings.WindowGeometry, geometry) - AppSettingsManager.setValue(Settings.WindowState, appWindow.visibility) + layoutManager.saveWindowSettings() Qt.quit() } else { - hide() + layoutManager.closeToTray() } } title: JamiStrings.appTitle - visible: mainApplicationLoader.status === Loader.Ready && windowSettingsLoaded + visible: mainApplicationLoader.status === Loader.Ready + && windowSettingsLoaded + && allowVisibleWindow // To facilitate reparenting of the callview during // fullscreen mode, we need QQuickItem based object. @@ -134,38 +134,21 @@ ApplicationWindow { onSourceChanged: windowSettingsLoaded = false onLoaded: { - if (UtilsAdapter.getAppValue(Settings.StartMinimized)) { - showMinimized() + if (checkLoadedSource() === MainApplicationWindow.LoadedSource.WizardView) { + // Onboarding wizard window, these settings are fixed. + // - window screen should default to the primary + // - position should default to being centered based on the + // following dimensions + // - the window will showNormal once windowSettingsLoaded is + // set to true(then forcing visible to true) + appWindow.width = JamiTheme.wizardViewMinWidth + appWindow.height = JamiTheme.wizardViewMinHeight + appWindow.minimumWidth = JamiTheme.wizardViewMinWidth + appWindow.minimumHeight = JamiTheme.wizardViewMinHeight } else { - if (checkLoadedSource() === MainApplicationWindow.LoadedSource.WizardView) { - appWindow.width = JamiTheme.wizardViewMinWidth - appWindow.height = JamiTheme.wizardViewMinHeight - appWindow.minimumWidth = JamiTheme.wizardViewMinWidth - appWindow.minimumHeight = JamiTheme.wizardViewMinHeight - } else { - // Main window, load settings if possible. - var geometry = AppSettingsManager.getValue(Settings.WindowGeometry) - - // Position. - if (!isNaN(geometry.x) && !isNaN(geometry.y)) { - appWindow.x = geometry.x - appWindow.y = geometry.y - } - - // Dimensions. - appWindow.width = geometry.width ? - geometry.width : - JamiTheme.mainViewPreferredWidth - appWindow.height = geometry.height ? - geometry.height : - JamiTheme.mainViewPreferredHeight - appWindow.minimumWidth = JamiTheme.mainViewMinWidth - appWindow.minimumHeight = JamiTheme.mainViewMinHeight - - // State. - const visibilityStr = AppSettingsManager.getValue(Settings.WindowState) - appWindow.visibility = parseInt(visibilityStr) - } + // Main window, load any valid app settings, and allow the + // layoutManager to handle as much as possible. + layoutManager.restoreWindowSettings() } // This will trigger `visible = true`. @@ -176,6 +159,9 @@ ApplicationWindow { UpdateManager.checkForUpdates(true) UpdateManager.setAutoUpdateCheck(true) } + + // Handle a start URI if set as start option. + MainApplication.handleUriAction(); } } diff --git a/src/instancemanager.cpp b/src/instancemanager.cpp index 11cf7fdec78300bc528c47d8d4ab8e234191eb4a..931218aa7dba218fb9865a47150e6addba4f24cb 100644 --- a/src/instancemanager.cpp +++ b/src/instancemanager.cpp @@ -52,13 +52,20 @@ public: {} ~Impl() = default; - bool tryToRun() + bool tryToRun(const QByteArray& startUri) { if (isAnotherRunning()) { - // This is a secondary instance, - // connect to the primary instance to trigger a restore - // then fail. + // This is a secondary instance, connect to the primary + // instance to trigger a restore then die. if (connectToLocal()) { + // Okay we connected. Send the start uri if not empty. + if (startUri.size()) { + qDebug() << "Sending start URI to secondary instance." << startUri; + socket_->write(startUri); + socket_->waitForBytesWritten(); + } + + // Now this instance can die. return false; } // If not connected, this means that the server doesn't exist @@ -99,7 +106,7 @@ public: return; } - socket_->write(reinterpret_cast<const char*>(terminateSeq_.data()), 4); + socket_->write(terminateSeq_); socket_->waitForBytesWritten(); }; @@ -139,10 +146,15 @@ private Q_SLOTS: if (recievedData == terminateSeq_) { qWarning() << "Received terminate signal."; mainAppInstance_->quit(); + } else { + qDebug() << "Received start URI:" << recievedData; + auto startUri = QString::fromLatin1(recievedData); + mainAppInstance_->handleUriAction(startUri); } }); // Restore primary instance + qDebug() << "Received wake-up from secondary instance."; mainAppInstance_->restoreApp(); }; @@ -193,9 +205,9 @@ InstanceManager::~InstanceManager() } bool -InstanceManager::tryToRun() +InstanceManager::tryToRun(const QByteArray& startUri) { - return pimpl_->tryToRun(); + return pimpl_->tryToRun(startUri); } void diff --git a/src/instancemanager.h b/src/instancemanager.h index 6da3056470a4706f4995511f90e910b79477734d..afd8fb7b00f614636f3a2dd24681de89cba4dcee 100644 --- a/src/instancemanager.h +++ b/src/instancemanager.h @@ -32,7 +32,7 @@ public: explicit InstanceManager(MainApplication* mainApp); ~InstanceManager(); - bool tryToRun(); + bool tryToRun(const QByteArray& startUri); void tryToKill(); private: diff --git a/src/main.cpp b/src/main.cpp index 52f6c9c9eff6770ac378bba436dcdbd8c0fad967..d401027e69c78021d8570925d064aa2d59d6c9b3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -106,9 +106,12 @@ main(int argc, char* argv[]) qWarning() << "Attempting to terminate other instances."; im.tryToKill(); return 0; - } else if (!im.tryToRun()) { - qWarning() << "Another instance is running."; - return 0; + } else { + auto startUri = app.getOpt(MainApplication::Option::StartUri); + if (!im.tryToRun(startUri.toByteArray())) { + qWarning() << "Another instance is running."; + return 0; + } } if (!app.init()) { diff --git a/src/mainapplication.cpp b/src/mainapplication.cpp index 8af2c3bbe2809c0bc56a89a739f07e2d1366ca74..02616e988ff914a74df257d8651e59f752577742 100644 --- a/src/mainapplication.cpp +++ b/src/mainapplication.cpp @@ -241,6 +241,10 @@ MainApplication::init() lrcInstance_->accountModel().autoTransferFromTrusted = allowTransferFromTrusted; lrcInstance_->accountModel().autoTransferSizeThreshold = acceptTransferBelow; + auto startMinimizedSetting = settingsManager_->getValue(Settings::Key::StartMinimized).toBool(); + // The presence of start URI should override the startMinimized setting for this instance. + set_startMinimized(startMinimizedSetting && runOptions_[Option::StartUri].isNull()); + initQmlLayer(); settingsManager_->setValue(Settings::Key::StartMinimized, @@ -257,6 +261,20 @@ MainApplication::restoreApp() Q_EMIT lrcInstance_->restoreAppRequested(); } +void +MainApplication::handleUriAction(const QString& arg) +{ + QString uri {}; + if (arg.isEmpty() && !runOptions_[Option::StartUri].isNull()) { + uri = runOptions_[Option::StartUri].toString(); + qDebug() << "URI action invoked by run option" << uri; + } else { + uri = arg; + qDebug() << "URI action invoked by secondary instance" << uri; + } + // TODO: implement URI protocol handling. +} + void MainApplication::initLrc(const QString& downloadUrl, ConnectivityMonitor* cm, bool logDaemon) { @@ -290,6 +308,13 @@ MainApplication::initLrc(const QString& downloadUrl, ConnectivityMonitor* cm, bo void MainApplication::parseArguments() { + // See if the app is being started with a URI. + for (const auto& arg : QApplication::arguments()) { + if (arg.startsWith("jami:")) { + runOptions_[Option::StartUri] = arg; + } + } + QCommandLineParser parser; parser.addHelpOption(); parser.addVersionOption(); diff --git a/src/mainapplication.h b/src/mainapplication.h index f2b06d9747e88f97011fc7fa82abdaebdb792b79..06597d3b3af26494ddbda51cbe4d51b63c8fe8db 100644 --- a/src/mainapplication.h +++ b/src/mainapplication.h @@ -58,7 +58,7 @@ class MainApplication : public QApplication { Q_OBJECT Q_DISABLE_COPY(MainApplication) - + QML_RO_PROPERTY(bool, startMinimized) public: explicit MainApplication(int& argc, char** argv); ~MainApplication(); @@ -66,6 +66,8 @@ public: bool init(); void restoreApp(); + Q_INVOKABLE void handleUriAction(const QString& uri = {}); + enum class Option { StartMinimized = 0, Debug, @@ -73,7 +75,8 @@ public: DebugToFile, UpdateUrl, MuteJamid, - TerminationRequested + TerminationRequested, + StartUri }; QVariant getOpt(const Option opt) {