Prevent multiple instances and center settings dialog
This commit is contained in:
@@ -1,7 +1,12 @@
|
||||
#include <QApplication>
|
||||
#include <QByteArray>
|
||||
#include <QCoreApplication>
|
||||
#include <QIcon>
|
||||
#include <QIODevice>
|
||||
#include <QLocalServer>
|
||||
#include <QLocalSocket>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
#include "src/config/ConfigManager.h"
|
||||
#include "src/tray/TrayController.h"
|
||||
@@ -9,12 +14,41 @@
|
||||
#include "src/util/Logger.h"
|
||||
#include "src/util/ResourcePaths.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
const QString SingleInstanceServerName = QStringLiteral("Make.QtDesktopPet.SingleInstance");
|
||||
const QByteArray ActivateMessage("activate\n");
|
||||
constexpr int SingleInstanceConnectTimeoutMs = 500;
|
||||
constexpr int SingleInstanceWriteTimeoutMs = 500;
|
||||
|
||||
bool notifyRunningInstance()
|
||||
{
|
||||
QLocalSocket socket;
|
||||
socket.connectToServer(SingleInstanceServerName, QIODevice::WriteOnly);
|
||||
if (!socket.waitForConnected(SingleInstanceConnectTimeoutMs))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
socket.write(ActivateMessage);
|
||||
socket.flush();
|
||||
socket.waitForBytesWritten(SingleInstanceWriteTimeoutMs);
|
||||
socket.disconnectFromServer();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication app(argc, argv);
|
||||
QApplication::setApplicationName("QtDesktopPet");
|
||||
QApplication::setOrganizationName("QtDesktopPet");
|
||||
|
||||
if (notifyRunningInstance())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const QIcon appIcon(ResourcePaths::appIconPath());
|
||||
if (!appIcon.isNull())
|
||||
{
|
||||
@@ -23,6 +57,19 @@ int main(int argc, char *argv[])
|
||||
|
||||
Logger::info(QStringLiteral("Application started."));
|
||||
|
||||
QLocalServer singleInstanceServer;
|
||||
QLocalServer::removeServer(SingleInstanceServerName);
|
||||
if (!singleInstanceServer.listen(SingleInstanceServerName))
|
||||
{
|
||||
if (notifyRunningInstance())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
Logger::warning(QStringLiteral("Unable to start single instance server: ")
|
||||
+ singleInstanceServer.errorString());
|
||||
}
|
||||
|
||||
ConfigManager configManager;
|
||||
PetWindow window;
|
||||
window.applyAppConfig(configManager.loadAppConfig());
|
||||
@@ -31,6 +78,37 @@ int main(int argc, char *argv[])
|
||||
window.setSettingsFallbackInContextMenuEnabled(!trayController.isAvailable());
|
||||
trayController.show();
|
||||
|
||||
QObject::connect(&singleInstanceServer, &QLocalServer::newConnection, [&singleInstanceServer, &window]() {
|
||||
while (singleInstanceServer.hasPendingConnections())
|
||||
{
|
||||
QLocalSocket *socket = singleInstanceServer.nextPendingConnection();
|
||||
if (socket == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto handleActivationMessage = [socket, &window]() {
|
||||
const QByteArray message = socket->readAll().trimmed();
|
||||
if (message == ActivateMessage.trimmed())
|
||||
{
|
||||
window.activateFromExternalInstance();
|
||||
}
|
||||
};
|
||||
QObject::connect(socket, &QLocalSocket::readyRead, handleActivationMessage);
|
||||
QObject::connect(socket, &QLocalSocket::disconnected, [socket, handleActivationMessage]() {
|
||||
if (socket->bytesAvailable() > 0)
|
||||
{
|
||||
handleActivationMessage();
|
||||
}
|
||||
socket->deleteLater();
|
||||
});
|
||||
if (socket->bytesAvailable() > 0)
|
||||
{
|
||||
handleActivationMessage();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
QObject::connect(&app, &QCoreApplication::aboutToQuit, [&configManager, &window]() {
|
||||
if (!configManager.saveAppConfig(window.currentAppConfig()))
|
||||
{
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "SettingsDialog.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QApplication>
|
||||
#include <QContextMenuEvent>
|
||||
#include <QCursor>
|
||||
#include <QDialog>
|
||||
@@ -89,6 +90,46 @@ AppConfig normalizedAppConfig(AppConfig config)
|
||||
return config;
|
||||
}
|
||||
|
||||
QScreen *screenForPopup(const QWidget *reference)
|
||||
{
|
||||
if (reference != nullptr)
|
||||
{
|
||||
if (QScreen *screen = QGuiApplication::screenAt(reference->frameGeometry().center()))
|
||||
{
|
||||
return screen;
|
||||
}
|
||||
}
|
||||
|
||||
if (QScreen *screen = QGuiApplication::screenAt(QCursor::pos()))
|
||||
{
|
||||
return screen;
|
||||
}
|
||||
|
||||
return QGuiApplication::primaryScreen();
|
||||
}
|
||||
|
||||
void centerDialogOnScreen(QDialog *dialog, const QWidget *reference)
|
||||
{
|
||||
if (dialog == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QScreen *screen = screenForPopup(reference);
|
||||
if (screen == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const QRect availableGeometry = screen->availableGeometry();
|
||||
const QSize dialogSize = dialog->size().isValid()
|
||||
? dialog->size()
|
||||
: dialog->sizeHint();
|
||||
const int x = availableGeometry.left() + qMax(0, (availableGeometry.width() - dialogSize.width()) / 2);
|
||||
const int y = availableGeometry.top() + qMax(0, (availableGeometry.height() - dialogSize.height()) / 2);
|
||||
dialog->move(QPoint(x, y));
|
||||
}
|
||||
|
||||
QString userVisibleErrorMessage(const ChatResponse &response)
|
||||
{
|
||||
QString message = response.errorMessage.trimmed();
|
||||
@@ -284,6 +325,7 @@ void PetWindow::openSettingsDialog()
|
||||
}, [this]() {
|
||||
clearConversation();
|
||||
}, this);
|
||||
centerDialogOnScreen(&dialog, this);
|
||||
if (dialog.exec() != QDialog::Accepted)
|
||||
{
|
||||
return;
|
||||
@@ -301,6 +343,31 @@ void PetWindow::openSettingsDialog()
|
||||
}
|
||||
}
|
||||
|
||||
void PetWindow::activateFromExternalInstance()
|
||||
{
|
||||
if (!isVisible())
|
||||
{
|
||||
show();
|
||||
resumeAnimation();
|
||||
}
|
||||
|
||||
if (isMinimized())
|
||||
{
|
||||
setWindowState(windowState() & ~Qt::WindowMinimized);
|
||||
}
|
||||
|
||||
if (QWidget *modalWidget = QApplication::activeModalWidget())
|
||||
{
|
||||
modalWidget->raise();
|
||||
modalWidget->activateWindow();
|
||||
return;
|
||||
}
|
||||
|
||||
raise();
|
||||
activateWindow();
|
||||
updateBubblePosition();
|
||||
}
|
||||
|
||||
void PetWindow::setSettingsFallbackInContextMenuEnabled(bool enabled)
|
||||
{
|
||||
m_settingsFallbackInContextMenuEnabled = enabled;
|
||||
|
||||
@@ -37,6 +37,7 @@ public:
|
||||
void applyAppConfig(const AppConfig &config);
|
||||
AppConfig currentAppConfig() const;
|
||||
void openSettingsDialog();
|
||||
void activateFromExternalInstance();
|
||||
void setSettingsFallbackInContextMenuEnabled(bool enabled);
|
||||
void pauseAnimation();
|
||||
void resumeAnimation();
|
||||
|
||||
Reference in New Issue
Block a user