diff --git a/CMakeLists.txt b/CMakeLists.txt index e0f4929..2f7da6b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -127,6 +127,7 @@ set(PROJECT_SOURCES sys/AutoRun.cpp ui/ThemeManager.cpp + ui/TrayIcon.cpp ui/mainwindow_grpc.cpp ui/mainwindow.cpp diff --git a/translations/zh_CN.ts b/translations/zh_CN.ts index 593330e..852692d 100644 --- a/translations/zh_CN.ts +++ b/translations/zh_CN.ts @@ -159,6 +159,26 @@ Include Pre-release when checking update 检查更新时包括 Pre-release + + Set custom icon + 自定义图标 + + + Please select a PNG file. + 请选择一个 PNG 文件。 + + + Reset + 重置 + + + Please select a valid square image. + 请选择有效的正方形图像。 + + + Cancel + 取消 + DialogEditGroup diff --git a/ui/TrayIcon.cpp b/ui/TrayIcon.cpp new file mode 100644 index 0000000..f0a367a --- /dev/null +++ b/ui/TrayIcon.cpp @@ -0,0 +1,39 @@ +#include "TrayIcon.hpp" + +#include + +QIcon TrayIcon::GetIcon(TrayIcon::TrayIconStatus status) { + auto icon = QIcon::fromTheme("nekoray"); + auto pixmap = QPixmap("../nekoray.png"); + if (!pixmap.isNull()) icon = QIcon(pixmap); + pixmap = QPixmap("./nekoray.png"); + if (!pixmap.isNull()) icon = QIcon(pixmap); + + if (status == TrayIconStatus::NONE) return icon; + + pixmap = icon.pixmap(icon.availableSizes().first()); + auto p = QPainter(&pixmap); + + auto side = pixmap.width(); + auto radius = side * 0.4; + auto d = side * 0.3; + auto margin = side * 0.1; + + if (status == TrayIconStatus::RUNNING) { + p.setBrush(QBrush(Qt::darkGreen)); + } else if (status == TrayIconStatus::SYSTEM_PROXY) { + p.setBrush(QBrush(Qt::blue)); + } else if (status == TrayIconStatus::VPN) { + p.setBrush(QBrush(Qt::red)); + } + p.drawRoundedRect( + QRect(side - d - margin, + side - d - margin, + d, + d), + radius, + radius); + p.end(); + + return pixmap; +} diff --git a/ui/TrayIcon.hpp b/ui/TrayIcon.hpp new file mode 100644 index 0000000..00e43ba --- /dev/null +++ b/ui/TrayIcon.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include + +namespace TrayIcon { + + enum TrayIconStatus { + NONE, + RUNNING, + SYSTEM_PROXY, + VPN, + }; + + QIcon GetIcon(TrayIconStatus status); + +} diff --git a/ui/dialog_basic_settings.cpp b/ui/dialog_basic_settings.cpp index 3d9e23f..9fa923b 100644 --- a/ui/dialog_basic_settings.cpp +++ b/ui/dialog_basic_settings.cpp @@ -9,6 +9,7 @@ #include #include #include +#include class ExtraCoreWidget : public QWidget { public: @@ -223,3 +224,26 @@ void DialogBasicSettings::accept() { dialog_message(Dialog_DialogBasicSettings, "UpdateDataStore"); QDialog::accept(); } + +void DialogBasicSettings::on_set_custom_icon_clicked() { + auto title = ui->set_custom_icon->text(); + auto c = QMessageBox::question(this, title, tr("Please select a PNG file."), + tr("Select"), tr("Reset"), tr("Cancel"), 2, 2); + if (c == 0) { + auto fn = QFileDialog::getOpenFileName(this, QObject::tr("Select"), QDir::currentPath(), + "*.png", nullptr, QFileDialog::Option::ReadOnly); + if (!fn.isEmpty()) { + QImage img(fn); + if (img.isNull() || img.height() != img.width()) { + MessageBoxWarning(title, tr("Please select a valid square image.")); + return; + } + QFile::copy(fn, "./nekoray.png"); + } + } else if (c == 1) { + QFile::remove("./nekoray.png"); + } else { + return; + } + dialog_message(Dialog_DialogBasicSettings, "UpdateIcon"); +} diff --git a/ui/dialog_basic_settings.h b/ui/dialog_basic_settings.h index a6c69f1..1b61d69 100644 --- a/ui/dialog_basic_settings.h +++ b/ui/dialog_basic_settings.h @@ -28,6 +28,11 @@ private: QString custom_inbound; bool needRestart = false; } CACHE; + +private slots: + + void on_set_custom_icon_clicked(); + }; #endif // DIALOG_BASIC_SETTINGS_H diff --git a/ui/dialog_basic_settings.ui b/ui/dialog_basic_settings.ui index 41a33ac..3068d79 100644 --- a/ui/dialog_basic_settings.ui +++ b/ui/dialog_basic_settings.ui @@ -264,6 +264,49 @@ Style + + + + + + + + 0 + 0 + + + + Theme + + + + + + + + System + + + + + flatgray + + + + + lightblue + + + + + blacksoft + + + + + + + @@ -302,6 +345,33 @@ + + + + + + + Minimize to tray icon on startup + + + + + + + Qt::Vertical + + + + + + + Set custom icon + + + + + + @@ -358,62 +428,6 @@ - - - - - - - - 0 - 0 - - - - Theme - - - - - - - - System - - - - - flatgray - - - - - lightblue - - - - - blacksoft - - - - - - - - - - - - - - Minimize to tray icon on startup - - - - - - diff --git a/ui/mainwindow.cpp b/ui/mainwindow.cpp index 167292b..01c2ec3 100644 --- a/ui/mainwindow.cpp +++ b/ui/mainwindow.cpp @@ -9,6 +9,7 @@ #include "sys/AutoRun.hpp" #include "ui/ThemeManager.hpp" +#include "ui/TrayIcon.hpp" #include "ui/edit/dialog_edit_profile.h" #include "ui/dialog_basic_settings.h" #include "ui/dialog_manage_groups.h" @@ -228,15 +229,7 @@ MainWindow::MainWindow(QWidget *parent) this->refresh_groups(); // Setup Tray - auto icon = QIcon::fromTheme("nekoray"); - auto pixmap = QPixmap("../nekoray.png"); - if (!pixmap.isNull()) icon = QIcon(pixmap); - pixmap = QPixmap("./nekoray.png"); - if (!pixmap.isNull()) icon = QIcon(pixmap); - setWindowIcon(icon); - tray = new QSystemTrayIcon(this);//初始化托盘对象tray - tray->setIcon(icon);//设定托盘图标,引号内是自定义的png图片路径 tray->setContextMenu(ui->menu_program);//创建托盘菜单 tray->show();//让托盘图标显示在系统托盘上 connect(tray, &QSystemTrayIcon::activated, this, @@ -483,6 +476,10 @@ void MainWindow::show_group(int gid) { // callback void MainWindow::dialog_message_impl(const QString &sender, const QString &info) { + if (info.contains("UpdateIcon")) { + icon_status = -1; + refresh_status(); + } if (info.contains("UpdateDataStore")) { auto changed = NekoRay::dataStore->Save(); if (info.contains("RouteChanged")) changed = true; @@ -679,8 +676,24 @@ void MainWindow::refresh_status(const QString &traffic_update) { return tt.join(isTray ? "\n" : " "); }; + auto icon_status_new = TrayIcon::NONE; + if (title_spmode == NekoRay::SystemProxyMode::SYSTEM_PROXY) { + icon_status_new = TrayIcon::SYSTEM_PROXY; + } else if (title_spmode == NekoRay::SystemProxyMode::VPN) { + icon_status_new = TrayIcon::VPN; + } else if (!running.isNull()) { + icon_status_new = TrayIcon::RUNNING; + } + setWindowTitle(make_title(false)); - if (tray != nullptr) tray->setToolTip(make_title(true)); + if (icon_status_new != icon_status) setWindowIcon(TrayIcon::GetIcon(TrayIcon::NONE)); + + if (tray != nullptr) { + tray->setToolTip(make_title(true)); + if (icon_status_new != icon_status) tray->setIcon(TrayIcon::GetIcon(icon_status_new)); + } + + icon_status = icon_status_new; } // table显示 diff --git a/ui/mainwindow.h b/ui/mainwindow.h index 5fa0f60..46820fd 100644 --- a/ui/mainwindow.h +++ b/ui/mainwindow.h @@ -131,6 +131,7 @@ private: QString title_base; QString title_error; int title_spmode = NekoRay::SystemProxyMode::DISABLE; + int icon_status = -1; QSharedPointer running; QString traffic_update_cache; QTime last_test_time;