Merge pull request #125 from vSLG/system-tray

Add a system tray icon
This commit is contained in:
miruka 2020-09-11 13:52:20 -04:00 committed by GitHub
commit 0ccf3d03c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 131 additions and 4 deletions

View File

@ -273,6 +273,7 @@ class UISettings(JSONDataFile):
"clearRoomFilterOnEnter": True, "clearRoomFilterOnEnter": True,
"clearRoomFilterOnEscape": True, "clearRoomFilterOnEscape": True,
"clearMemberFilterOnEscape": True, "clearMemberFilterOnEscape": True,
"closeMinimizesToTray": False,
"collapseSidePanesUnderWindowWidth": 450, "collapseSidePanesUnderWindowWidth": 450,
"enableKineticScrolling": True, "enableKineticScrolling": True,
"hideProfileChangeEvents": True, "hideProfileChangeEvents": True,

58
src/gui/TrayIcon.qml Normal file
View File

@ -0,0 +1,58 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
import QtQuick.Controls 2.12
import Qt.labs.platform 1.1
import Qt.labs.folderlistmodel 2.12
SystemTrayIcon {
property ApplicationWindow window
property alias settingsFolder: showUpWatcher.folder
property string iconPack: theme ? theme.icons.preferredPack : "thin"
property FolderListModel showUpWatcher: FolderListModel {
id: showUpWatcher
showDirs: false
showHidden: true
nameFilters: [".show"]
onCountChanged: {
if (count) {
window.restoreFromTray()
py.importModule("os", () => {
py.call("os.remove", [get(0, "filePath")])
})
}
}
}
visible: true
tooltip: Qt.application.displayName
icon.source: `../icons/${iconPack}/tray-icon.png`
onActivated: {
if (reason === SystemTrayIcon.MiddleClick)
Qt.quit()
else if (reason !== SystemTrayIcon.Context)
window.visible ? window.hide() : window.restoreFromTray()
}
menu: Menu {
MenuItem {
text:
window.visible ?
"Minimize to tray" :
qsTr("Open %1").arg(Qt.application.displayName)
onTriggered:
window.visible ?
window.hide() :
window.restoreFromTray()
}
MenuItem {
text: qsTr("Quit %1").arg(Qt.application.displayName)
onTriggered: Qt.quit()
}
}
}

View File

@ -22,6 +22,7 @@ ApplicationWindow {
property var uiState: ({}) property var uiState: ({})
property var history: ({}) property var history: ({})
property var theme: null property var theme: null
property string settingsFolder
readonly property var visibleMenus: ({}) readonly property var visibleMenus: ({})
readonly property var visiblePopups: ({}) readonly property var visiblePopups: ({})
@ -64,6 +65,11 @@ ApplicationWindow {
) )
} }
function restoreFromTray() {
window.show()
window.raise()
window.requestActivate()
}
flags: Qt.WA_TranslucentBackground flags: Qt.WA_TranslucentBackground
@ -80,6 +86,11 @@ ApplicationWindow {
onUiStateChanged: py.saveConfig("ui_state", uiState) onUiStateChanged: py.saveConfig("ui_state", uiState)
onHistoryChanged: py.saveConfig("history", history) onHistoryChanged: py.saveConfig("history", history)
onClosing: {
close.accepted = ! settings.closeMinimizesToTray
settings.closeMinimizesToTray ? hide() : Qt.quit()
}
PythonRootBridge { id: py } PythonRootBridge { id: py }
Utils { id: utils } Utils { id: utils }
@ -100,4 +111,9 @@ ApplicationWindow {
Behavior on scale { HNumberAnimation { overshoot: 3; factor: 1.2 } } Behavior on scale { HNumberAnimation { overshoot: 3; factor: 1.2 } }
} }
TrayIcon {
window: window
settingsFolder: window.settingsFolder
}
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -12,6 +12,10 @@
#include <QQuickStyle> #include <QQuickStyle>
#include <QFontDatabase> #include <QFontDatabase>
#include <QDateTime> #include <QDateTime>
#include <QStandardPaths>
#include <QDir>
#include <QFile>
#include <QLockFile>
#include <signal.h> #include <signal.h>
#ifdef Q_OS_UNIX #ifdef Q_OS_UNIX
@ -23,6 +27,9 @@
#include "clipboard_image_provider.h" #include "clipboard_image_provider.h"
QLockFile *lockFile = nullptr;
void loggingHandler( void loggingHandler(
QtMsgType type, QtMsgType type,
const QMessageLogContext &context, const QMessageLogContext &context,
@ -86,6 +93,35 @@ void onExitSignal(int signum) {
} }
bool setLockFile(QString configPath) {
QDir settingsFolder(configPath);
if (! settingsFolder.mkpath(".")) {
qFatal("Could not create config directory");
exit(EXIT_FAILURE);
}
lockFile = new QLockFile(settingsFolder.absoluteFilePath(".lock"));
lockFile->tryLock(0);
switch (lockFile->error()) {
case QLockFile::NoError:
return true;
case QLockFile::LockFailedError: {
qWarning("Opening already running instance");
QFile showFile(settingsFolder.absoluteFilePath(".show"));
showFile.open(QIODevice::WriteOnly);
showFile.close();
return false;
}
default:
qFatal("Cannot create lock file: no permission or unknown error");
exit(EXIT_FAILURE);
}
}
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
qInstallMessageHandler(loggingHandler); qInstallMessageHandler(loggingHandler);
@ -95,6 +131,16 @@ int main(int argc, char *argv[]) {
QApplication::setApplicationDisplayName("Mirage"); QApplication::setApplicationDisplayName("Mirage");
QApplication::setApplicationVersion("0.6.2"); QApplication::setApplicationVersion("0.6.2");
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QString customConfigDir(qEnvironmentVariable("MIRAGE_CONFIG_DIR"));
QString settingsFolder(
customConfigDir.isEmpty() ?
QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation)
+ "/" + QApplication::applicationName() :
customConfigDir
);
if (! setLockFile(settingsFolder)) return EXIT_SUCCESS;
QApplication app(argc, argv); QApplication app(argc, argv);
// Register handlers for quit signals, e.g. SIGINT/Ctrl-C in unix terminals // Register handlers for quit signals, e.g. SIGINT/Ctrl-C in unix terminals
@ -135,6 +181,9 @@ int main(int argc, char *argv[]) {
QQmlEngine engine; QQmlEngine engine;
QQmlContext *objectContext = new QQmlContext(engine.rootContext()); QQmlContext *objectContext = new QQmlContext(engine.rootContext());
// To able to use Qt.quit() from QML side
QObject::connect(&engine, &QQmlEngine::quit, &QApplication::quit);
// Set the debugMode properties depending of if we're running in debug mode // Set the debugMode properties depending of if we're running in debug mode
// or not (`qmake CONFIG+=dev ...`, default in live-reload.sh) // or not (`qmake CONFIG+=dev ...`, default in live-reload.sh)
#ifdef QT_DEBUG #ifdef QT_DEBUG
@ -184,7 +233,7 @@ int main(int argc, char *argv[]) {
if (component.isError()) { if (component.isError()) {
for (QQmlError e : component.errors()) { for (QQmlError e : component.errors()) {
qFatal( qCritical(
"%s:%d:%d: %s", "%s:%d:%d: %s",
e.url().toString().toStdString().c_str(), e.url().toString().toStdString().c_str(),
e.line(), e.line(),
@ -192,11 +241,14 @@ int main(int argc, char *argv[]) {
e.description().toStdString().c_str() e.description().toStdString().c_str()
); );
} }
qFatal("One or more errors have occurred, exiting");
app.exit(EXIT_FAILURE); app.exit(EXIT_FAILURE);
} }
component.create(objectContext); component.create(objectContext)->setProperty("settingsFolder", settingsFolder);
// Finally, execute the app. Return its system exit code when it exits. // Finally, execute the app. Return its exit code after clearing the lock.
return app.exec(); int exit_code = app.exec();
delete lockFile;
return exit_code;
} }