Skip to content
This repository has been archived by the owner on Aug 18, 2021. It is now read-only.

Commit

Permalink
Merge pull request #11 from Qv2ray/dev-tons-of-improvements
Browse files Browse the repository at this point in the history
tons of user interface/experience improvements
  • Loading branch information
DuckSoft authored Oct 9, 2020
2 parents 78be5e2 + 9c375de commit 46f556d
Show file tree
Hide file tree
Showing 3 changed files with 199 additions and 39 deletions.
142 changes: 111 additions & 31 deletions widgets/TrojanGoSettingsWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

#include "TrojanGoPlugin.hpp"

#include <QDrag>
#include <QDragEnterEvent>
#include <QFileDialog>
#include <QMessageBox>
#include <QMimeData>

TrojanGoSettingsWidget::TrojanGoSettingsWidget(QWidget *parent) : Qv2rayPlugin::QvPluginSettingsWidget(parent)
{
Expand All @@ -23,11 +26,14 @@ void TrojanGoSettingsWidget::changeEvent(QEvent *e)
std::tuple<KernelPathCheckerResult, std::optional<QString>> TrojanGoSettingsWidget::preliminaryKernelPathChecker(const QString &pathToKernel)
{
// some random extensions that doesn't seem to be a Trojan-Go kernel.
const QStringList nonKernelExtensions = { ".so", ".dll", ".dylib", ".zip", ".rar", ".gz", ".tar", ".xz" };
if (std::any_of(nonKernelExtensions.cbegin(), nonKernelExtensions.cend(),
[&pathToKernel](const QString &extension) -> bool { return pathToKernel.endsWith(extension, Qt::CaseInsensitive); }))
return { KernelPathCheckerResult::KPCR_WARNING, tr("Suffix `%1` does not seem to be a Trojan-Go Core.\r\n"
"It should be like `trojan-go` or `trojan-go.exe`.") };
const QStringList nonKernelExtensions = { ".so", ".dll", ".dylib", ".zip", ".rar", ".gz", ".tar", ".xz", ".txt" };
if (const auto it =
std::find_if(nonKernelExtensions.cbegin(), nonKernelExtensions.cend(),
[&pathToKernel](const QString &extension) -> bool { return pathToKernel.endsWith(extension, Qt::CaseInsensitive); });
it != nonKernelExtensions.cend())
return { KernelPathCheckerResult::KPCR_WARNING, tr("The suffix `%1` does not seem to be of a Trojan-Go Core.\r\n"
"Its filename should have been like `trojan-go` or `trojan-go.exe`.")
.arg(*it) };

// check the existance, read permission and file size issues.
if (QFile kernelFile(pathToKernel); !kernelFile.exists())
Expand All @@ -43,50 +49,124 @@ std::tuple<KernelPathCheckerResult, std::optional<QString>> TrojanGoSettingsWidg

void TrojanGoSettingsWidget::on_selectKernelBtn_clicked()
{

#if defined(Q_OS_MAC)
const static QString platformDefaultKernelDir = "/usr/local/bin";
const static QString platformKernelPathFilter = "Trojan-Go Core(trojan-go*);;All Files(*)";
#elif defined(Q_OS_UNIX)
const static QString platformDefaultKernelDir = "/usr/bin";
const static QString platformKernelPathFilter = "Trojan-Go Core(trojan-go*);;All Files(*)";
#elif defined(Q_OS_WIN)
const static QString platformDefaultKernelDir = "";
const static QString platformKernelPathFilter = "Trojan-Go Core(trojan-go.exe);;All Files(*)";
#else
const static QString platformDefaultKernelDir = "";
const static QString platformKernelPathFilter = "";
#endif

const auto path = QFileDialog::getOpenFileName(this, tr("Select Trojan-Go Core"), platformDefaultKernelDir, platformKernelPathFilter);
if (path.isEmpty())
return;

const auto [result, someErrMsg] = this->preliminaryKernelPathChecker(path);
// debounce: disabling this button for now
selectKernelBtn->setEnabled(false);

if (handleKernelPathCheck(path))
{
settings.insert("kernelPath", path);
kernelPathTxt->setText(path);
}

// debounce: recover in 800ms
debounceTimer.singleShot(800, this, &TrojanGoSettingsWidget::debounceUnfreeze);
}

void TrojanGoSettingsWidget::debounceUnfreeze()
{
selectKernelBtn->setEnabled(true);
}

bool TrojanGoSettingsWidget::handleKernelPathCheck(const QString &pathToCheck)
{
const auto [result, someErrMsg] = this->preliminaryKernelPathChecker(pathToCheck);
switch (result)
{
case KernelPathCheckerResult::KPCR_VERY_OK: break;
case KernelPathCheckerResult::KPCR_FAILURE:
{
QMessageBox::critical(this, tr("Trojan-Go Core Path Check Failed"),
tr("Path `%1` did not pass kernel path checker:\r\n%2").arg(path, *someErrMsg));
return;
tr("Path `%1` did not pass kernel path checker:\r\n%2").arg(pathToCheck, *someErrMsg));
return false;
}
case KernelPathCheckerResult::KPCR_WARNING:
{
const auto choice = QMessageBox::question(this, tr("Trojan-Go Core Path Warning"),
tr("Path `%1` might not be the correct Trojan-Go kernel path:\r\n%2").arg(path, *someErrMsg),
QMessageBox::Ignore | QMessageBox::Abort, QMessageBox::Abort);
const auto choice =
QMessageBox::question(this, tr("Trojan-Go Core Path Warning"),
tr("Path `%1` might not be the correct Trojan-Go kernel path:\r\n%2").arg(pathToCheck, *someErrMsg),
QMessageBox::Ignore | QMessageBox::Abort, QMessageBox::Abort);
if (choice == QMessageBox::Abort)
return;
return false;
}
}
return true;
}

void TrojanGoSettingsWidget::dragEnterEvent(QDragEnterEvent *event)
{
// accept only file-like drops
const auto mimeData = event->mimeData();
if (!mimeData->hasUrls())
{
event->ignore();
return;
}

// accept only single file drop
if (const auto &urlList = mimeData->urls(); urlList.isEmpty() || urlList.length() != 1)
{
event->ignore();
return;
}

// else: let the drag pass
event->acceptProposedAction();
}

void TrojanGoSettingsWidget::dropEvent(QDropEvent *event)
{
const auto mimeData = event->mimeData();
if (!mimeData->hasUrls())
{
return;
}

settings.insert("kernelPath", path);
kernelPathTxt->setText(path);
const auto path = mimeData->urls().first().toLocalFile();
if (path.isEmpty())
{
return;
}

if (handleKernelPathCheck(path))
{
settings.insert("kernelPath", path);
kernelPathTxt->setText(path);
}
}

void TrojanGoSettingsWidget::on_kernelPathTxt_textEdited(const QString &arg1)
void TrojanGoSettingsWidget::on_testKernelBtn_clicked()
{
settings.insert("kernelPath", arg1);
const auto path = settings["kernelPath"].toString();
if (path.isEmpty())
{
QMessageBox::critical(this, tr("Nothing to Test"), tr("Kernel Path is empty."));
return;
}

QProcess process;
#ifdef Q_OS_WIN32
process.setProgram(path);
process.setProcessChannelMode(QProcess::MergedChannels);
process.setNativeArguments("--version");
process.start();
#else
process.start(path, { "--version" });
#endif
process.waitForStarted();
process.waitForFinished();

const auto exitCode = process.exitCode();
if (exitCode != 0)
{
QMessageBox::critical(this, tr("Trojan-Go Core Test Failed"),
tr("Trojan-Go Core failed with exit code %1.\r\nReason: %2").arg(exitCode).arg(process.errorString()));
return;
}

QString output = process.readAllStandardOutput();
QMessageBox::information(this, tr("Trojan-Go Test Result"), output);
}
31 changes: 30 additions & 1 deletion widgets/TrojanGoSettingsWidget.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "interface/QvGUIPluginInterface.hpp"
#include "ui_TrojanGoSettingsWidget.h"

#include <QTimer>
#include <optional>

/**
Expand Down Expand Up @@ -44,13 +45,33 @@ class TrojanGoSettingsWidget

protected:
void changeEvent(QEvent *e) override;
void dragEnterEvent(QDragEnterEvent *event) override;
void dropEvent(QDropEvent *event) override;

private slots:
void debounceUnfreeze();
void on_selectKernelBtn_clicked();
void on_kernelPathTxt_textEdited(const QString &arg1);
void on_testKernelBtn_clicked();

private:
QJsonObject settings;
QTimer debounceTimer;

private:
// platform-dependent options.
#if defined(Q_OS_MAC)
const inline static char *platformDefaultKernelDir = "/usr/local/bin";
const inline static char *platformKernelPathFilter = "Trojan-Go Core(trojan-go*);;All Files(*)";
#elif defined(Q_OS_UNIX)
const inline static char *platformDefaultKernelDir = "/usr/bin";
const inline static char *platformKernelPathFilter = "Trojan-Go Core(trojan-go*);;All Files(*)";
#elif defined(Q_OS_WIN)
const inline static char *platformDefaultKernelDir = "";
const inline static char *platformKernelPathFilter = "Trojan-Go Core(trojan-go.exe);;All Files(*)";
#else
const inline static char *platformDefaultKernelDir = "";
const inline static char *platformKernelPathFilter = "";
#endif

private:
/**
Expand All @@ -59,4 +80,12 @@ class TrojanGoSettingsWidget
* @return if the kernel path check passes, and if not, the detailed error message.
*/
std::tuple<KernelPathCheckerResult, std::optional<QString>> preliminaryKernelPathChecker(const QString &pathToKernel);

/**
* @brief The UI routine to handle kernel path check.
* To be reused by multiple part of this program.
* @param pathToCheck the path to be checked.
* @return if the backend should accept the user input.
*/
bool handleKernelPathCheck(const QString &pathToCheck);
};
65 changes: 58 additions & 7 deletions widgets/TrojanGoSettingsWidget.ui
Original file line number Diff line number Diff line change
Expand Up @@ -6,35 +6,86 @@
<rect>
<x>0</x>
<y>0</y>
<width>651</width>
<height>300</height>
<width>534</width>
<height>332</height>
</rect>
</property>
<property name="acceptDrops">
<bool>true</bool>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<widget class="QLabel" name="kernelLabel">
<property name="text">
<string>Trojan-Go Kernel Path</string>
<string>Kernel</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="kernelPathTxt">
<property name="acceptDrops">
<bool>false</bool>
</property>
<property name="text">
<string notr="true"/>
</property>
<property name="dragEnabled">
<bool>false</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="placeholderText">
<string>(N/A)</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="kernelPathTxt"/>
<widget class="QPushButton" name="selectKernelBtn">
<property name="text">
<string>&amp;Browse...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="selectKernelBtn">
<widget class="QPushButton" name="testKernelBtn">
<property name="text">
<string>Select Kernel</string>
<string>&amp;Test Run...</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0" colspan="2">
<widget class="QLabel" name="notesLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>&lt;h3&gt;Tips:&lt;/h3&gt;
QvPlugin-Trojan-Go requires Trojan-Go Core to function well.
&lt;br&gt;
Please locate Trojan-Go kernel using &lt;b&gt;Browse...&lt;/b&gt; button, or by &lt;b&gt;dragging&lt;/b&gt; the kernel file in. Optionally, do a &lt;b&gt;Test Run&lt;/b&gt; before leaving.</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
Expand Down

0 comments on commit 46f556d

Please sign in to comment.