[fix] Some UI fixes, and added a Singleapplication model, Fixed #139

Former-commit-id: 7ea337d1f2
This commit is contained in:
Leroy.H.Y 2019-12-10 19:39:28 +08:00
parent 90ccfea5a1
commit 0af45dc678
No known key found for this signature in database
GPG Key ID: 6AC1673B587DC37D
10 changed files with 104 additions and 184 deletions

3
.gitmodules vendored
View File

@ -7,3 +7,6 @@
[submodule "3rdparty/qhttpserver"] [submodule "3rdparty/qhttpserver"]
path = 3rdparty/qhttpserver path = 3rdparty/qhttpserver
url = https://github.com/nikhilm/qhttpserver url = https://github.com/nikhilm/qhttpserver
[submodule "3rdparty/SingleApplication"]
path = 3rdparty/SingleApplication
url = https://github.com/itay-grudev/SingleApplication

1
3rdparty/SingleApplication vendored Submodule

@ -0,0 +1 @@
Subproject commit 16ea64b2548b02f59bf49b255466278c3ff0ace8

View File

@ -1 +1 @@
1776 1796

View File

@ -9,20 +9,21 @@ QT += core gui widgets network charts
TARGET = qv2ray TARGET = qv2ray
TEMPLATE = app TEMPLATE = app
# Don't merge those configs with below.
CONFIG += enable_decoder_qr_code enable_encoder_qr_code qt c++11 openssl-linked
include(3rdparty/qzxing_noTests/QZXing-components.pri)
# Main config
CONFIG += lrelease embed_translations
# Now read build number file. # Now read build number file.
_BUILD_NUMBER=$$cat(Build.Counter) _BUILD_NUMBER=$$cat(Build.Counter)
VERSION = 1.99.2.$$_BUILD_NUMBER VERSION = 1.99.2.$$_BUILD_NUMBER
_BUILD_NUMBER = $$num_add($$_BUILD_NUMBER, 1) _BUILD_NUMBER = $$num_add($$_BUILD_NUMBER, 1)
write_file("Build.Counter", _BUILD_NUMBER) write_file("Build.Counter", _BUILD_NUMBER)
DEFINES += QT_DEPRECATED_WARNINGS QV2RAY_VERSION_STRING=\"\\\"v$${VERSION}\\\"\" DEFINES += QT_DEPRECATED_WARNINGS QV2RAY_VERSION_STRING=\"\\\"v$${VERSION}\\\"\" QAPPLICATION_CLASS=QApplication
# Don't merge those configs with below.
CONFIG += enable_decoder_qr_code enable_encoder_qr_code qt c++11 openssl-linked
include(3rdparty/qzxing_noTests/QZXing-components.pri)
include(3rdparty/SingleApplication/singleapplication.pri)
# Main config
CONFIG += lrelease embed_translations
SOURCES += \ SOURCES += \
src/components/QvComponentsHandler.cpp \ src/components/QvComponentsHandler.cpp \
@ -39,7 +40,6 @@ SOURCES += \
src/QvCoreConfigOperations_Convertion.cpp \ src/QvCoreConfigOperations_Convertion.cpp \
src/QvCoreConfigOperations_Generation.cpp \ src/QvCoreConfigOperations_Generation.cpp \
src/QvUtils.cpp \ src/QvUtils.cpp \
src/utils/QvRunguard.cpp \
src/utils/QJsonModel.cpp \ src/utils/QJsonModel.cpp \
src/ui/w_ExportConfig.cpp \ src/ui/w_ExportConfig.cpp \
src/ui/w_InboundEditor.cpp \ src/ui/w_InboundEditor.cpp \
@ -89,7 +89,6 @@ HEADERS += \
src/utils/QvTinyLog.hpp \ src/utils/QvTinyLog.hpp \
src/utils/QJsonModel.hpp \ src/utils/QJsonModel.hpp \
src/utils/QJsonObjectInsertMacros.h \ src/utils/QJsonObjectInsertMacros.h \
src/utils/QvRunguard.hpp \
libs/gen/v2ray_api_commands.pb.h \ libs/gen/v2ray_api_commands.pb.h \
libs/gen/v2ray_api_commands.grpc.pb.h libs/gen/v2ray_api_commands.grpc.pb.h

View File

@ -3,10 +3,13 @@
#include <QTranslator> #include <QTranslator>
#include <QStyle> #include <QStyle>
#include <QLocale> #include <QLocale>
#include <QObject>
#include <QStyleFactory> #include <QStyleFactory>
#include <QApplication>
#include <singleapplication.h>
#include "w_MainWindow.hpp" #include "w_MainWindow.hpp"
#include "QvRunguard.hpp"
bool verifyConfigAvaliability(QString path, bool checkExistingConfig) bool verifyConfigAvaliability(QString path, bool checkExistingConfig)
{ {
@ -157,7 +160,13 @@ bool initialiseQv2ray()
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
// This line must be called before any other ones. // This line must be called before any other ones.
QApplication _qApp(argc, argv); // ----------------------------> For debug build...
SingleApplication _qApp(argc, argv);
// Early initialisation
#ifdef QT_DEBUG
_qApp.setApplicationName(_qApp.applicationName() + " - DEBUG");
#endif
//
// //
// Install a default translater. From the OS/DE // Install a default translater. From the OS/DE
auto _lang = QLocale::system().name().replace("_", "-"); auto _lang = QLocale::system().name().replace("_", "-");
@ -208,18 +217,6 @@ int main(int argc, char *argv[])
return -1; return -1;
} }
#ifdef QT_DEBUG
RunGuard guard("Qv2ray-Instance-Identifier-DEBUG_VERSION");
#else
RunGuard guard("Qv2ray-Instance-Identifier");
#endif
if (!guard.isSingleInstance()) {
LOG(MODULE_INIT, "Another Instance running, Quit.")
QvMessageBox(nullptr, "Qv2ray", QObject::tr("Another instance of Qv2ray is already running."));
return -1;
}
auto conf = CONFIGROOT(JsonFromString(StringFromFile(new QFile(QV2RAY_CONFIG_FILE)))); auto conf = CONFIGROOT(JsonFromString(StringFromFile(new QFile(QV2RAY_CONFIG_FILE))));
// //
auto confVersion = conf["config_version"].toVariant().toString(); auto confVersion = conf["config_version"].toVariant().toString();
@ -322,6 +319,7 @@ int main(int argc, char *argv[])
#else #else
QStringList themes = QStyleFactory::keys(); QStringList themes = QStyleFactory::keys();
//_qApp.setDesktopFileName("qv2ray.desktop");
if (themes.contains(QSTRING(confObject.uiConfig.theme))) { if (themes.contains(QSTRING(confObject.uiConfig.theme))) {
_qApp.setStyle(QSTRING(confObject.uiConfig.theme)); _qApp.setStyle(QSTRING(confObject.uiConfig.theme));
@ -335,6 +333,11 @@ int main(int argc, char *argv[])
try { try {
#endif #endif
QObject::connect(&_qApp, &SingleApplication::instanceStarted, [&w]() {
w.show();
w.raise();
w.activateWindow();
});
auto rcode = _qApp.exec(); auto rcode = _qApp.exec();
LOG(MODULE_INIT, "Quitting normally") LOG(MODULE_INIT, "Quitting normally")
return rcode; return rcode;

View File

@ -306,7 +306,6 @@ void MainWindow::VersionUpdate(QByteArray &data)
} else if (result == QMessageBox::Ignore) { } else if (result == QMessageBox::Ignore) {
currentConfig.ignoredVersion = newversion.toString().toStdString(); currentConfig.ignoredVersion = newversion.toString().toStdString();
SetGlobalConfig(currentConfig); SetGlobalConfig(currentConfig);
OnConfigListChanged(false);
} }
} }
} }
@ -314,7 +313,16 @@ void MainWindow::ReloadConnections()
{ {
LOG(MODULE_UI, "Loading new GlobalConfig") LOG(MODULE_UI, "Loading new GlobalConfig")
currentConfig = GetGlobalConfig(); currentConfig = GetGlobalConfig();
//
// Store the latency test value.
QMap<QString, double> latencyValueCache;
for (auto i = 0; i < connections.count(); i++) {
latencyValueCache[connections.keys()[i]] = connections.values()[i].latency;
}
connections.clear(); connections.clear();
SetEditWidgetEnable(false);
// //
connectionListWidget->clear(); connectionListWidget->clear();
auto _regularConnections = GetRegularConnections(currentConfig.configs); auto _regularConnections = GetRegularConnections(currentConfig.configs);
@ -325,7 +333,8 @@ void MainWindow::ReloadConnections()
_o.subscriptionName = ""; _o.subscriptionName = "";
_o.connectionName = _regularConnections.keys()[i]; _o.connectionName = _regularConnections.keys()[i];
_o.config = _regularConnections.values()[i]; _o.config = _regularConnections.values()[i];
_o.latency = 0; // Restore latency from cache.
_o.latency = latencyValueCache.contains(_o.connectionName) ? latencyValueCache[_o.connectionName] : 0;
connections[_o.connectionName] = _o; connections[_o.connectionName] = _o;
connectionListWidget->addTopLevelItem(new QTreeWidgetItem(QStringList() << _o.connectionName)); connectionListWidget->addTopLevelItem(new QTreeWidgetItem(QStringList() << _o.connectionName));
} }
@ -343,17 +352,16 @@ void MainWindow::ReloadConnections()
_o.subscriptionName = subName; _o.subscriptionName = subName;
_o.connectionName = _subsConnections.values()[i].keys()[j]; _o.connectionName = _subsConnections.values()[i].keys()[j];
_o.config = _subsConnections.values()[i].values()[j]; _o.config = _subsConnections.values()[i].values()[j];
_o.latency = 0; //
auto connName = _o.connectionName + " (" + tr("Subscription:") + " " + _o.subscriptionName + ")"; auto connName = _o.connectionName + " (" + tr("Subscription:") + " " + _o.subscriptionName + ")";
_o.latency = latencyValueCache.contains(connName) ? latencyValueCache[connName] : 0;
connections[connName] = _o; connections[connName] = _o;
subTopLevel->addChild(new QTreeWidgetItem(QStringList() << connName)); subTopLevel->addChild(new QTreeWidgetItem(QStringList() << connName));
} }
} }
SetEditWidgetEnable(false);
//// We set the current item back... //// We set the current item back...
if (!CurrentConnectionName.isEmpty()) { if (!CurrentConnectionName.isEmpty() && connections.contains(CurrentConnectionName)) {
auto items = connectionListWidget->findItems(CurrentConnectionName, Qt::MatchExactly | Qt::MatchRecursive); auto items = connectionListWidget->findItems(CurrentConnectionName, Qt::MatchExactly | Qt::MatchRecursive);
if (items.count() > 0 && IsConnectableItem(items.first())) { if (items.count() > 0 && IsConnectableItem(items.first())) {
@ -516,7 +524,7 @@ void MainWindow::on_startButton_clicked()
} }
if (canSetSystemProxy) { if (canSetSystemProxy) {
LOG(MODULE_UI, "Setting system proxy for simple config") LOG(MODULE_UI, "Setting system proxy for simple config, HTTP only")
// --------------------We only use HTTP here->>|=========| // --------------------We only use HTTP here->>|=========|
SetSystemProxy(proxyAddress, currentConfig.inboundConfig.http_port, usePAC); SetSystemProxy(proxyAddress, currentConfig.inboundConfig.http_port, usePAC);
} }
@ -532,6 +540,8 @@ void MainWindow::on_startButton_clicked()
// //
startButton->setEnabled(!startFlag); startButton->setEnabled(!startFlag);
stopButton->setEnabled(startFlag); stopButton->setEnabled(startFlag);
} else {
LOG(MODULE_UI, "vCore already started.")
} }
} }
@ -875,18 +885,20 @@ void MainWindow::on_importConfigButton_clicked()
ImportConfigWindow *w = new ImportConfigWindow(this); ImportConfigWindow *w = new ImportConfigWindow(this);
auto configs = w->OpenImport(); auto configs = w->OpenImport();
for (auto conf : configs) { if (!configs.isEmpty()) {
auto name = configs.key(conf, ""); for (auto conf : configs) {
auto name = configs.key(conf, "");
if (name.isEmpty()) if (name.isEmpty())
continue; continue;
SaveConnectionConfig(conf, &name, false); SaveConnectionConfig(conf, &name, false);
currentConfig.configs.push_back(name.toStdString()); currentConfig.configs.push_back(name.toStdString());
}
SetGlobalConfig(currentConfig);
OnConfigListChanged(false);
} }
SetGlobalConfig(currentConfig);
OnConfigListChanged(false);
} }
void MainWindow::on_editConfigButton_clicked() void MainWindow::on_editConfigButton_clicked()
{ {
@ -897,6 +909,8 @@ void MainWindow::on_editConfigButton_clicked()
} }
auto alias = connectionListWidget->selectedItems().first()->text(0); auto alias = connectionListWidget->selectedItems().first()->text(0);
SUBSCRIPTION_CONFIG_MODIFY_ASK(alias)
//
auto outBoundRoot = connections[alias].config; auto outBoundRoot = connections[alias].config;
CONFIGROOT root; CONFIGROOT root;
bool isChanged = false; bool isChanged = false;
@ -917,11 +931,15 @@ void MainWindow::on_editConfigButton_clicked()
} }
if (isChanged) { if (isChanged) {
connections[alias].config = root; if (IsSubscription(alias)) {
// true indicates the alias will NOT change SaveSubscriptionConfig(root, connections[alias].subscriptionName, connections[alias].connectionName);
SaveConnectionConfig(root, &alias, true); } else {
connections[alias].config = root;
// true indicates the alias will NOT change
SaveConnectionConfig(root, &alias, true);
}
OnConfigListChanged(alias == CurrentConnectionName); OnConfigListChanged(alias == CurrentConnectionName);
ShowAndSetConnection(CurrentConnectionName, false, false);
} }
} }
void MainWindow::on_reconnectButton_clicked() void MainWindow::on_reconnectButton_clicked()
@ -939,16 +957,8 @@ void MainWindow::on_action_RCM_ConvToComplex_triggered()
} }
auto alias = connectionListWidget->currentItem()->text(0); auto alias = connectionListWidget->currentItem()->text(0);
SUBSCRIPTION_CONFIG_MODIFY_ASK(alias)
if (connections[alias].configType == CON_SUBSCRIPTION) { //
if (QvMessageBoxAsk(this, tr("Editing a subscription config"), tr("You are trying to edit a config loaded from subscription.") +
NEWLINE + tr("All changes will be overwritten when the subscriptions are updated next time.") +
NEWLINE + tr("Are you still going to do so?")) != QMessageBox::Yes) {
// Not changing, return
return;
}
}
auto outBoundRoot = connections[alias].config; auto outBoundRoot = connections[alias].config;
CONFIGROOT root; CONFIGROOT root;
bool isChanged = false; bool isChanged = false;
@ -1004,9 +1014,9 @@ void MainWindow::on_pingTestBtn_clicked()
latencyLabel->setText(tr("Testing...")); latencyLabel->setText(tr("Testing..."));
// We get data from UI? // We get data from UI?
auto alias = connectionListWidget->currentItem()->text(0); auto alias = connectionListWidget->currentItem()->text(0);
int port = -1;
try { try {
int port = -1;
port = stoi(_portLabel->text().isEmpty() ? "0" : _portLabel->text().toStdString()); port = stoi(_portLabel->text().isEmpty() ? "0" : _portLabel->text().toStdString());
tcpingModel->StartPing(alias, _hostLabel->text(), port); tcpingModel->StartPing(alias, _hostLabel->text(), port);
} catch (...) { } catch (...) {
@ -1133,6 +1143,7 @@ void MainWindow::on_connectionListWidget_itemSelectionChanged()
_OutBoundTypeLabel->setText(tr("N/A")); _OutBoundTypeLabel->setText(tr("N/A"));
_hostLabel->setText(tr("N/A")); _hostLabel->setText(tr("N/A"));
_portLabel->setText(tr("N/A")); _portLabel->setText(tr("N/A"));
latencyLabel->setText(tr("N/A"));
} }
} }

View File

@ -43,7 +43,7 @@ PrefrencesWindow::PrefrencesWindow(QWidget *parent) : QDialog(parent),
#if QV2RAY_USE_BUILTIN_DARKTHEME #if QV2RAY_USE_BUILTIN_DARKTHEME
// If we use built in theme, it should always be fusion. // If we use built in theme, it should always be fusion.
themeCombo->setEnabled(!CurrentConfig.uiConfig.useDarkTheme); themeCombo->setEnabled(!CurrentConfig.uiConfig.useDarkTheme);
darkThemeLabel->setText(tr("Use Dark Theme")); darkThemeLabel->setText(tr("Use Darkmode Theme"));
#endif #endif
languageComboBox->setCurrentText(QSTRING(CurrentConfig.uiConfig.language)); languageComboBox->setCurrentText(QSTRING(CurrentConfig.uiConfig.language));
logLevelComboBox->setCurrentIndex(CurrentConfig.logLevel); logLevelComboBox->setCurrentIndex(CurrentConfig.logLevel);

View File

@ -135,14 +135,14 @@
<item row="0" column="1"> <item row="0" column="1">
<widget class="QLabel" name="darkThemeLabel"> <widget class="QLabel" name="darkThemeLabel">
<property name="text"> <property name="text">
<string>Dark UI Icons</string> <string>Darkmode UI Icons</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="1"> <item row="1" column="1">
<widget class="QLabel" name="label_36"> <widget class="QLabel" name="label_36">
<property name="text"> <property name="text">
<string>Dark Tray Icon</string> <string>Darkmode Tray Icon</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -1042,7 +1042,14 @@
<string>Network Toolbar Settings</string> <string>Network Toolbar Settings</string>
</attribute> </attribute>
<layout class="QGridLayout" name="gridLayout_3"> <layout class="QGridLayout" name="gridLayout_3">
<item row="1" column="0" rowspan="3"> <item row="3" column="1">
<widget class="QPushButton" name="applyNSBarSettingsBtn">
<property name="text">
<string>Apply Network Speed Bar UI Settings</string>
</property>
</widget>
</item>
<item row="2" column="0" rowspan="3">
<widget class="QGroupBox" name="groupBox_6"> <widget class="QGroupBox" name="groupBox_6">
<property name="title"> <property name="title">
<string>Items</string> <string>Items</string>
@ -1187,7 +1194,14 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item row="1" column="1"> <item row="0" column="0" colspan="2">
<widget class="QLabel" name="label_33">
<property name="text">
<string>You can config how the network speed toolbar looks like in this panel</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QFrame" name="nsBarVerticalLayout"> <widget class="QFrame" name="nsBarVerticalLayout">
<layout class="QVBoxLayout" name="verticalLayout_2"> <layout class="QVBoxLayout" name="verticalLayout_2">
<item> <item>
@ -1356,17 +1370,20 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item row="0" column="0" colspan="2"> <item row="1" column="0">
<widget class="QLabel" name="label_33"> <widget class="QLabel" name="label_64">
<property name="text"> <property name="font">
<string>You can config how the network speed toolbar looks like in this panel</string> <font>
<weight>75</weight>
<italic>true</italic>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">color: red</string>
</property> </property>
</widget>
</item>
<item row="2" column="1">
<widget class="QPushButton" name="applyNSBarSettingsBtn">
<property name="text"> <property name="text">
<string>Apply Network Speed Bar UI Settings</string> <string>This feature for Windows is not stable yet.</string>
</property> </property>
</widget> </widget>
</item> </item>

View File

@ -1,81 +0,0 @@
#include <QCryptographicHash>
#include "QvRunguard.hpp"
namespace Qv2ray
{
//from https://stackoverflow.com/a/28172162
QString RunGuard::generateKeyHash(const QString &key, const QString &salt)
{
QByteArray data;
data.append(key.toUtf8());
data.append(salt.toUtf8());
data = QCryptographicHash::hash(data, QCryptographicHash::Sha1).toHex();
return data;
}
RunGuard::RunGuard(const QString &key)
: key(key)
, memLockKey(generateKeyHash(key, "_memLockKey"))
, sharedmemKey(generateKeyHash(key, "_sharedmemKey"))
, sharedMem(sharedmemKey)
, memLock(memLockKey, 1)
{
memLock.acquire();
{
QSharedMemory fix(sharedmemKey); // Fix for *nix: http://habrahabr.ru/post/173281/
fix.attach();
}
memLock.release();
}
RunGuard::~RunGuard()
{
release();
}
bool RunGuard::isAnotherRunning()
{
if (sharedMem.isAttached()) {
return false;
}
memLock.acquire();
const bool isRunning = sharedMem.attach();
if (isRunning) {
sharedMem.detach();
}
memLock.release();
return isRunning;
}
bool RunGuard::isSingleInstance()
{
if (isAnotherRunning()) { // Extra check
return false;
}
memLock.acquire();
const bool result = sharedMem.create(sizeof(quint64));
memLock.release();
if (!result) {
release();
return false;
}
return true;
}
void RunGuard::release()
{
memLock.acquire();
if (sharedMem.isAttached()) {
sharedMem.detach();
}
memLock.release();
}
}

View File

@ -1,33 +0,0 @@
#ifndef RUNGUARD_H
#define RUNGUARD_H
#include <QObject>
#include <QSharedMemory>
#include <QSystemSemaphore>
namespace Qv2ray
{
// From https://stackoverflow.com/a/28172162
class RunGuard
{
public:
explicit RunGuard(const QString &key);
~RunGuard();
bool isAnotherRunning();
bool isSingleInstance();
void release();
private:
QString generateKeyHash(const QString &key, const QString &salt);
const QString key;
const QString memLockKey;
const QString sharedmemKey;
QSharedMemory sharedMem;
QSystemSemaphore memLock;
Q_DISABLE_COPY(RunGuard)
};
}
#endif // RUNGUARD_H