add custom tray icon support

This commit is contained in:
Nova 2025-11-18 13:08:23 +03:30
parent 7a32e4144c
commit 3354ab789b
8 changed files with 98 additions and 12 deletions

View File

@ -27,6 +27,8 @@ namespace Configs {
namespace Information {
inline QString HijackInfo = "Listens on the given addr:port (on Windows, port is always 53) and redirects the requests to the DNS module. Domains that match the rules will have their requests hijacked and the A and AAAA queries will be responded with the Inet4 response and Inet6 response respectively.\nThe Redirect settings sets up an inbound that listens on the given addr:port, sniffs the destination if possible and redirects the requests to their true destination.\nThe use case of these settings is apps that do not respect the system proxy for resolving their DNS requests (one such example is discord), You can hijack their DNS requests to 127.0.0.1 and then route them through the Throne tunnel. The same effect could be achieved using Tun mode, but one may not want to tunnel the whole device (For example when Gaming), this is where DNS hijack can transparently handle things.\n\nCurrently you can Automatically set the System DNS in windows.";
inline QString SimpleRuleInfo = "You can add rules with the following format:\ndomain:<your-domain>\nsuffix:<your-domain-suffix>\nkeyword:<your-domain-keyword>\nregex:<your-domain-keyword>\nruleset:<ruleset-name> or ruleset:<remote-ruleset-URL>\nip:<ip-cidr>\nprocessName:<process name>\nprocessPath:<process path>\nRules are validated on tab change or when pressing ok.\nImportant: On saving the rules, the previous rules are discarded, meaning\nany changes made to the generated rules in the Advanced tab will be lost.";
inline QString CustomIconManual = "To choose custom icons, you need to choose png images with an equal width and height (eg 512*512). Their names should be of \n(Dns.png, Off.png, Proxy.png, Proxy-Dns.png, Throne.png, Tun.png) So that each will be used in the appropriate state of the app. \nYou can provide a subset of the said images and only the corresponding states will be using them.\nIt is suggested that each image's size be less than 100KB.";
inline QStringList iconNames = {"Dns.png", "Off.png", "Proxy.png", "Proxy-Dns.png", "Throne.png", "Tun.png"};
}
namespace TestConfig

View File

@ -105,6 +105,7 @@ namespace Configs {
QString simple_dl_url = "http://cachefly.cachefly.net/1mb.test";
bool allow_beta_update = false;
bool show_system_dns = false;
bool use_custom_icons = false;
// Network
bool net_use_proxy = false;

View File

@ -28,6 +28,7 @@ private:
QString custom_inbound;
bool needRestart = false;
bool updateDisableTray = false;
bool updateTrayIcon = false;
bool updateSystemDns = false;
} CACHE;

View File

@ -418,8 +418,8 @@
<layout class="QHBoxLayout" name="style_h_1">
<item>
<widget class="QGroupBox" name="groupBox">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<layout class="QGridLayout" name="gridLayout_10">
<item row="0" column="0">
<widget class="QLabel" name="label_8">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
@ -432,7 +432,7 @@
</property>
</widget>
</item>
<item>
<item row="0" column="1">
<widget class="QComboBox" name="theme">
<item>
<property name="text">
@ -441,6 +441,20 @@
</item>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="select_custom_icon">
<property name="text">
<string>Select</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="enable_custom_icon">
<property name="text">
<string>Enable Custom Icons</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>

View File

@ -324,6 +324,7 @@ namespace Configs {
_add(new configItem("url_test_timeout_ms", &url_test_timeout_ms, itemType::integer));
_add(new configItem("show_system_dns", &show_system_dns, itemType::boolean));
_add(new configItem("main_window_geometry", &mainWindowGeometry, itemType::string));
_add(new configItem("use_custom_icons", &use_custom_icons, itemType::boolean));
}
void DataStore::UpdateStartedId(int id) {

View File

@ -237,6 +237,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
dashFile.close();
}
}
if (auto dashDir = QDir("icons"); !dashDir.exists("icons")) {
QDir().mkdir("icons") ? qDebug("created icons dir") : qDebug("Failed to create icons dir");
}
// top bar
ui->toolButton_program->setMenu(ui->menu_program);
@ -760,7 +763,7 @@ void MainWindow::show_group(int gid) {
void MainWindow::dialog_message_impl(const QString &sender, const QString &info) {
// info
if (info.contains("UpdateIcon")) {
if (info.contains("UpdateTrayIcon")) {
icon_status = -1;
refresh_status();
}

View File

@ -6,35 +6,71 @@
QPixmap Icon::GetTrayIcon(TrayIconStatus status) {
QPixmap pixmap;
QPixmap pixmap_read;
if (status == NONE)
{
auto pixmap_read = QPixmap(QString(":/Throne/") + "Off" + ".png");
if (Configs::dataStore->use_custom_icons) {
pixmap_read = QPixmap(QString("icons/") + "Off" + ".png");
}
if (pixmap_read.isNull()) {
pixmap_read = QPixmap(QString(":/Throne/") + "Off" + ".png");
}
if (!pixmap_read.isNull()) pixmap = pixmap_read;
} else if (status == RUNNING)
{
auto pixmap_read = QPixmap(QString(":/Throne/") + "Throne" + ".png");
if (Configs::dataStore->use_custom_icons) {
pixmap_read = QPixmap(QString("icons/") + "Throne" + ".png");
}
if (pixmap_read.isNull()) {
pixmap_read = QPixmap(QString(":/Throne/") + "Throne" + ".png");
}
if (!pixmap_read.isNull()) pixmap = pixmap_read;
} else if (status == SYSTEM_PROXY_DNS)
{
auto pixmap_read = QPixmap(QString(":/Throne/") + "Proxy-Dns" + ".png");
if (Configs::dataStore->use_custom_icons) {
pixmap_read = QPixmap(QString("icons/") + "Proxy-Dns" + ".png");
}
if (pixmap_read.isNull()) {
pixmap_read = QPixmap(QString(":/Throne/") + "Proxy-Dns" + ".png");
}
if (!pixmap_read.isNull()) pixmap = pixmap_read;
} else if (status == SYSTEM_PROXY)
{
auto pixmap_read = QPixmap(QString(":/Throne/") + "Proxy" + ".png");
if (Configs::dataStore->use_custom_icons) {
pixmap_read = QPixmap(QString("icons/") + "Proxy" + ".png");
}
if (pixmap_read.isNull()) {
pixmap_read = QPixmap(QString(":/Throne/") + "Proxy" + ".png");
}
if (!pixmap_read.isNull()) pixmap = pixmap_read;
} else if (status == DNS)
{
auto pixmap_read = QPixmap(QString(":/Throne/") + "Dns" + ".png");
if (Configs::dataStore->use_custom_icons) {
pixmap_read = QPixmap(QString("icons/") + "Dns" + ".png");
}
if (pixmap_read.isNull()) {
pixmap_read = QPixmap(QString(":/Throne/") + "Dns" + ".png");
}
if (!pixmap_read.isNull()) pixmap = pixmap_read;
} else if (status == VPN)
{
auto pixmap_read = QPixmap(QString(":/Throne/") + "Tun" + ".png");
if (Configs::dataStore->use_custom_icons) {
pixmap_read = QPixmap(QString("icons/") + "Tun" + ".png");
}
if (pixmap_read.isNull()) {
pixmap_read = QPixmap(QString(":/Throne/") + "Tun" + ".png");
}
if (!pixmap_read.isNull()) pixmap = pixmap_read;
} else
{
MW_show_log("Icon::GetTrayIcon: Unknown status");
auto pixmap_read = QPixmap(QString(":/Throne/") + "Off" + ".png");
if (Configs::dataStore->use_custom_icons) {
pixmap_read = QPixmap(QString("icons/") + "Off" + ".png");
}
if (pixmap_read.isNull()) {
pixmap_read = QPixmap(QString(":/Throne/") + "Off" + ".png");
}
if (!pixmap_read.isNull()) pixmap = pixmap_read;
}

View File

@ -108,6 +108,30 @@ DialogBasicSettings::DialogBasicSettings(QWidget *parent)
//
ui->theme->addItems(QStyleFactory::keys());
ui->theme->addItem("QDarkStyle");
ui->enable_custom_icon->setChecked(Configs::dataStore->use_custom_icons);
connect(ui->select_custom_icon, &QPushButton::clicked, this, [=, this] {
auto n = QMessageBox::information(this, "Custom Icon Manual", tr(Configs::Information::CustomIconManual.toStdString().c_str()), QMessageBox::Open | QMessageBox::Cancel);
if (n == QMessageBox::Open) {
auto fileNames = QFileDialog::getOpenFileNames(this,
tr("Select png icons"), QDir::homePath(), tr("Image Files (*.png)"));
// process files
QString errors;
for (const auto& fileName : fileNames) {
CACHE.updateTrayIcon = true;
QFileInfo fileInfo(fileName);
if (auto pixMap = QPixmap(fileName); pixMap.isNull()) errors += "Failed to load " + fileName + "\n";
else if (pixMap.width() != pixMap.height()) errors += "Image does not have equal width and height: " + fileName + "\n";
else if (!Configs::Information::iconNames.contains(fileInfo.fileName())) errors += "Icon name is not valid: " + fileInfo.fileName() + "\n";
else {
QFile::remove(QDir("icons").filePath(fileInfo.fileName()));
if (!QFile::copy(fileName, QDir("icons").filePath(fileInfo.fileName()))) errors += "Failed to copy " + fileName + "\n";
}
}
if (!errors.isEmpty()) {
QMessageBox::warning(this, "Select custom image error", errors);
}
}
});
//
bool ok;
auto themeId = Configs::dataStore->theme.toInt(&ok);
@ -117,7 +141,7 @@ DialogBasicSettings::DialogBasicSettings(QWidget *parent)
ui->theme->setCurrentText(Configs::dataStore->theme);
}
//
connect(ui->theme, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, [=,this](int index) {
connect(ui->theme, &QComboBox::currentIndexChanged, this, [=,this](int index) {
themeManager->ApplyTheme(ui->theme->currentText());
Configs::dataStore->theme = ui->theme->currentText();
Configs::dataStore->Save();
@ -205,6 +229,9 @@ void DialogBasicSettings::accept() {
Configs::dataStore->enable_stats = ui->connection_statistics->isChecked();
Configs::dataStore->language = ui->language->currentIndex();
auto oldUseCustomIcon = Configs::dataStore->use_custom_icons;
Configs::dataStore->use_custom_icons = ui->enable_custom_icon->isChecked();
if (oldUseCustomIcon != Configs::dataStore->use_custom_icons) CACHE.updateTrayIcon = true;
D_SAVE_BOOL(start_minimal)
D_SAVE_INT(max_log_line)
Configs::dataStore->show_system_dns = ui->show_sys_dns->isChecked();
@ -255,6 +282,7 @@ void DialogBasicSettings::accept() {
if (CACHE.needRestart) str << "NeedRestart";
if (CACHE.updateDisableTray) str << "UpdateDisableTray";
if (CACHE.updateSystemDns) str << "UpdateSystemDns";
if (CACHE.updateTrayIcon) str << "UpdateTrayIcon";
if (needChoosePort) str << "NeedChoosePort";
MW_dialog_message(Dialog_DialogBasicSettings, str.join(","));
QDialog::accept();