[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"]
path = 3rdparty/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
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.
_BUILD_NUMBER=$$cat(Build.Counter)
VERSION = 1.99.2.$$_BUILD_NUMBER
_BUILD_NUMBER = $$num_add($$_BUILD_NUMBER, 1)
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 += \
src/components/QvComponentsHandler.cpp \
@ -39,7 +40,6 @@ SOURCES += \
src/QvCoreConfigOperations_Convertion.cpp \
src/QvCoreConfigOperations_Generation.cpp \
src/QvUtils.cpp \
src/utils/QvRunguard.cpp \
src/utils/QJsonModel.cpp \
src/ui/w_ExportConfig.cpp \
src/ui/w_InboundEditor.cpp \
@ -89,7 +89,6 @@ HEADERS += \
src/utils/QvTinyLog.hpp \
src/utils/QJsonModel.hpp \
src/utils/QJsonObjectInsertMacros.h \
src/utils/QvRunguard.hpp \
libs/gen/v2ray_api_commands.pb.h \
libs/gen/v2ray_api_commands.grpc.pb.h

View File

@ -3,10 +3,13 @@
#include <QTranslator>
#include <QStyle>
#include <QLocale>
#include <QObject>
#include <QStyleFactory>
#include <QApplication>
#include <singleapplication.h>
#include "w_MainWindow.hpp"
#include "QvRunguard.hpp"
bool verifyConfigAvaliability(QString path, bool checkExistingConfig)
{
@ -157,7 +160,13 @@ bool initialiseQv2ray()
int main(int argc, char *argv[])
{
// 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
auto _lang = QLocale::system().name().replace("_", "-");
@ -208,18 +217,6 @@ int main(int argc, char *argv[])
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 confVersion = conf["config_version"].toVariant().toString();
@ -322,6 +319,7 @@ int main(int argc, char *argv[])
#else
QStringList themes = QStyleFactory::keys();
//_qApp.setDesktopFileName("qv2ray.desktop");
if (themes.contains(QSTRING(confObject.uiConfig.theme))) {
_qApp.setStyle(QSTRING(confObject.uiConfig.theme));
@ -335,6 +333,11 @@ int main(int argc, char *argv[])
try {
#endif
QObject::connect(&_qApp, &SingleApplication::instanceStarted, [&w]() {
w.show();
w.raise();
w.activateWindow();
});
auto rcode = _qApp.exec();
LOG(MODULE_INIT, "Quitting normally")
return rcode;

View File

@ -306,7 +306,6 @@ void MainWindow::VersionUpdate(QByteArray &data)
} else if (result == QMessageBox::Ignore) {
currentConfig.ignoredVersion = newversion.toString().toStdString();
SetGlobalConfig(currentConfig);
OnConfigListChanged(false);
}
}
}
@ -314,7 +313,16 @@ void MainWindow::ReloadConnections()
{
LOG(MODULE_UI, "Loading new GlobalConfig")
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();
SetEditWidgetEnable(false);
//
connectionListWidget->clear();
auto _regularConnections = GetRegularConnections(currentConfig.configs);
@ -325,7 +333,8 @@ void MainWindow::ReloadConnections()
_o.subscriptionName = "";
_o.connectionName = _regularConnections.keys()[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;
connectionListWidget->addTopLevelItem(new QTreeWidgetItem(QStringList() << _o.connectionName));
}
@ -343,17 +352,16 @@ void MainWindow::ReloadConnections()
_o.subscriptionName = subName;
_o.connectionName = _subsConnections.values()[i].keys()[j];
_o.config = _subsConnections.values()[i].values()[j];
_o.latency = 0;
//
auto connName = _o.connectionName + " (" + tr("Subscription:") + " " + _o.subscriptionName + ")";
_o.latency = latencyValueCache.contains(connName) ? latencyValueCache[connName] : 0;
connections[connName] = _o;
subTopLevel->addChild(new QTreeWidgetItem(QStringList() << connName));
}
}
SetEditWidgetEnable(false);
//// We set the current item back...
if (!CurrentConnectionName.isEmpty()) {
if (!CurrentConnectionName.isEmpty() && connections.contains(CurrentConnectionName)) {
auto items = connectionListWidget->findItems(CurrentConnectionName, Qt::MatchExactly | Qt::MatchRecursive);
if (items.count() > 0 && IsConnectableItem(items.first())) {
@ -516,7 +524,7 @@ void MainWindow::on_startButton_clicked()
}
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->>|=========|
SetSystemProxy(proxyAddress, currentConfig.inboundConfig.http_port, usePAC);
}
@ -532,6 +540,8 @@ void MainWindow::on_startButton_clicked()
//
startButton->setEnabled(!startFlag);
stopButton->setEnabled(startFlag);
} else {
LOG(MODULE_UI, "vCore already started.")
}
}
@ -875,6 +885,7 @@ void MainWindow::on_importConfigButton_clicked()
ImportConfigWindow *w = new ImportConfigWindow(this);
auto configs = w->OpenImport();
if (!configs.isEmpty()) {
for (auto conf : configs) {
auto name = configs.key(conf, "");
@ -888,6 +899,7 @@ void MainWindow::on_importConfigButton_clicked()
SetGlobalConfig(currentConfig);
OnConfigListChanged(false);
}
}
void MainWindow::on_editConfigButton_clicked()
{
// Check if we have a connection selected...
@ -897,6 +909,8 @@ void MainWindow::on_editConfigButton_clicked()
}
auto alias = connectionListWidget->selectedItems().first()->text(0);
SUBSCRIPTION_CONFIG_MODIFY_ASK(alias)
//
auto outBoundRoot = connections[alias].config;
CONFIGROOT root;
bool isChanged = false;
@ -917,11 +931,15 @@ void MainWindow::on_editConfigButton_clicked()
}
if (isChanged) {
if (IsSubscription(alias)) {
SaveSubscriptionConfig(root, connections[alias].subscriptionName, connections[alias].connectionName);
} else {
connections[alias].config = root;
// true indicates the alias will NOT change
SaveConnectionConfig(root, &alias, true);
}
OnConfigListChanged(alias == CurrentConnectionName);
ShowAndSetConnection(CurrentConnectionName, false, false);
}
}
void MainWindow::on_reconnectButton_clicked()
@ -939,16 +957,8 @@ void MainWindow::on_action_RCM_ConvToComplex_triggered()
}
auto alias = connectionListWidget->currentItem()->text(0);
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;
}
}
SUBSCRIPTION_CONFIG_MODIFY_ASK(alias)
//
auto outBoundRoot = connections[alias].config;
CONFIGROOT root;
bool isChanged = false;
@ -1004,9 +1014,9 @@ void MainWindow::on_pingTestBtn_clicked()
latencyLabel->setText(tr("Testing..."));
// We get data from UI?
auto alias = connectionListWidget->currentItem()->text(0);
int port = -1;
try {
int port = -1;
port = stoi(_portLabel->text().isEmpty() ? "0" : _portLabel->text().toStdString());
tcpingModel->StartPing(alias, _hostLabel->text(), port);
} catch (...) {
@ -1133,6 +1143,7 @@ void MainWindow::on_connectionListWidget_itemSelectionChanged()
_OutBoundTypeLabel->setText(tr("N/A"));
_hostLabel->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 we use built in theme, it should always be fusion.
themeCombo->setEnabled(!CurrentConfig.uiConfig.useDarkTheme);
darkThemeLabel->setText(tr("Use Dark Theme"));
darkThemeLabel->setText(tr("Use Darkmode Theme"));
#endif
languageComboBox->setCurrentText(QSTRING(CurrentConfig.uiConfig.language));
logLevelComboBox->setCurrentIndex(CurrentConfig.logLevel);

View File

@ -135,14 +135,14 @@
<item row="0" column="1">
<widget class="QLabel" name="darkThemeLabel">
<property name="text">
<string>Dark UI Icons</string>
<string>Darkmode UI Icons</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_36">
<property name="text">
<string>Dark Tray Icon</string>
<string>Darkmode Tray Icon</string>
</property>
</widget>
</item>
@ -1042,7 +1042,14 @@
<string>Network Toolbar Settings</string>
</attribute>
<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">
<property name="title">
<string>Items</string>
@ -1187,7 +1194,14 @@
</layout>
</widget>
</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">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
@ -1356,17 +1370,20 @@
</layout>
</widget>
</item>
<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>
<item row="1" column="0">
<widget class="QLabel" name="label_64">
<property name="font">
<font>
<weight>75</weight>
<italic>true</italic>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">color: red</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QPushButton" name="applyNSBarSettingsBtn">
<property name="text">
<string>Apply Network Speed Bar UI Settings</string>
<string>This feature for Windows is not stable yet.</string>
</property>
</widget>
</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