commit
0ccf3d03c9
|
@ -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
58
src/gui/TrayIcon.qml
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
BIN
src/icons/thin/tray-icon.png
Normal file
BIN
src/icons/thin/tray-icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.0 KiB |
60
src/main.cpp
60
src/main.cpp
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user