From eb3a5aa9398d8748bad8aa8e753f4c45e40099ee Mon Sep 17 00:00:00 2001 From: JiXieShi Date: Fri, 12 Sep 2025 17:50:53 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20=E6=96=B0=E5=A2=9E:=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E4=B8=8D=E5=90=8C=E5=B9=B3=E5=8F=B0=E7=9A=84=E9=80=9A?= =?UTF-8?q?=E7=9F=A5=E6=98=BE=E7=A4=BA=E5=87=BD=E6=95=B0=20=E2=9C=A8=20?= =?UTF-8?q?=E6=96=B0=E5=A2=9E:=20=E5=AE=9E=E7=8E=B0=20Windows=E3=80=81macO?= =?UTF-8?q?S=20=E5=92=8C=20Linux=20=E7=9A=84=E9=80=9A=E7=9F=A5=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=20=F0=9F=93=9D=20=E4=BF=AE=E6=94=B9:=20=E8=B0=83?= =?UTF-8?q?=E6=95=B4=E5=91=BD=E4=BB=A4=E5=8E=86=E5=8F=B2=E8=AE=B0=E5=BD=95?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E6=B3=A8=E9=87=8A=20=F0=9F=93=9D=20=E4=BF=AE?= =?UTF-8?q?=E6=94=B9:=20=E5=A2=9E=E5=8A=A0=E5=B7=A5=E4=BD=9C=E7=9B=AE?= =?UTF-8?q?=E5=BD=95=E7=AE=A1=E7=90=86=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/inc/AppState.h | 4 ++- app/inc/TrayIcon.h | 8 ++++- app/src/AppState.cpp | 18 ++++++++-- app/src/TrayIcon.cpp | 86 +++++++++++++++++++++++++++++++------------- 4 files changed, 88 insertions(+), 28 deletions(-) diff --git a/app/inc/AppState.h b/app/inc/AppState.h index 0af4be7..aff72b8 100644 --- a/app/inc/AppState.h +++ b/app/inc/AppState.h @@ -16,7 +16,7 @@ public: void SaveSettings(); void ApplySettings(); - // 新增:启动命令历史记录管理 + // 启动命令历史记录管理 void AddCommandToHistory(const std::string& command); void RemoveCommandFromHistory(int index); void ClearCommandHistory(); @@ -27,6 +27,8 @@ public: CLIProcess cli_process; char command_input[256]{}; char send_command[256]{}; + char working_directory[256]{}; + bool auto_working_dir; bool auto_scroll_logs; bool enable_colored_logs; int max_log_lines; diff --git a/app/inc/TrayIcon.h b/app/inc/TrayIcon.h index 0300acd..f4286d5 100644 --- a/app/inc/TrayIcon.h +++ b/app/inc/TrayIcon.h @@ -35,7 +35,13 @@ public: // 设置回调函数 void SetShowWindowCallback(const ShowWindowCallback &callback); void SetExitCallback(const ExitCallback &callback); - +#ifdef _WIN32 + void ShowWindowsNotification(const std::wstring& title, const std::wstring& message); +#elif __APPLE__ + void ShowMacNotification(const std::string& title, const std::string& message); +#else + void ShowLinuxNotification(const std::string& title, const std::string& message); +#endif private: void CreateMenu(); void DestroyMenu(); diff --git a/app/src/AppState.cpp b/app/src/AppState.cpp index 6145df0..b9b93bc 100644 --- a/app/src/AppState.cpp +++ b/app/src/AppState.cpp @@ -7,6 +7,7 @@ AppState::AppState() : show_main_window(true), auto_start(false), auto_scroll_logs(true), + auto_working_dir(true), enable_colored_logs(true), max_log_lines(1000), stop_timeout_ms(5000), @@ -77,7 +78,7 @@ std::string AppState::SerializeCommandHistory() const { return oss.str(); } -// 新增:反序列化命令历史记录 +// 反序列化命令历史记录 void AppState::DeserializeCommandHistory(const std::string& serialized) { command_history.clear(); if (serialized.empty()) return; @@ -185,6 +186,9 @@ void AppState::LoadSettings() { if (key == "CommandInput") { strncpy_s(command_input, value.c_str(), sizeof(command_input) - 1); } + else if (key == "WorkingDirectory") { + strncpy_s(working_directory, value.c_str(), sizeof(working_directory) - 1); + } else if (key == "MaxLogLines") { max_log_lines = std::stoi(value); max_log_lines = std::max(100, std::min(max_log_lines, 10000)); @@ -198,6 +202,9 @@ void AppState::LoadSettings() { else if (key == "AutoStart") { auto_start = (value == "1"); } + else if (key == "AutoWorkDirectory") { + auto_working_dir = (value == "1"); + } else if (key == "WebUrl") { strncpy_s(web_url, value.c_str(), sizeof(web_url) - 1); } @@ -240,10 +247,12 @@ void AppState::SaveSettings() { file << "[Settings]\n"; file << "CommandInput=" << command_input << "\n"; + file << "WorkingDirectory=" << working_directory << "\n"; file << "MaxLogLines=" << max_log_lines << "\n"; file << "AutoScrollLogs=" << (auto_scroll_logs ? "1" : "0") << "\n"; file << "EnableColoredLogs=" << (enable_colored_logs ? "1" : "0") << "\n"; file << "AutoStart=" << (auto_start ? "1" : "0") << "\n"; + file << "AutoWorkDirectory=" << (auto_working_dir ? "1" : "0") << "\n"; file << "WebUrl=" << web_url << "\n"; // 停止命令相关配置的保存 @@ -284,6 +293,11 @@ void AppState::ApplySettings() { cli_process.SetEnvironmentVariables({}); } - // 新增:应用输出编码设置 + if (strlen(working_directory)>0) { + cli_process.SetWorkingDirectory(working_directory); + } + + cli_process.SetAutoWorkingDir(auto_working_dir); + // 应用输出编码设置 cli_process.SetOutputEncoding(output_encoding); } diff --git a/app/src/TrayIcon.cpp b/app/src/TrayIcon.cpp index 02c9d4c..9202d24 100644 --- a/app/src/TrayIcon.cpp +++ b/app/src/TrayIcon.cpp @@ -4,7 +4,6 @@ #ifdef _WIN32 TrayIcon::TrayIcon(HWND hwnd, HICON icon) : m_hwnd(hwnd), m_icon(icon), m_visible(false), m_menu(nullptr) { - ZeroMemory(&m_nid, sizeof(m_nid)); m_nid.cbSize = sizeof(m_nid); m_nid.hWnd = m_hwnd; @@ -53,14 +52,15 @@ void TrayIcon::Hide() { } #ifdef _WIN32 -void TrayIcon::UpdateWebUrl(const std::wstring& url) { +void TrayIcon::UpdateWebUrl(const std::wstring &url) { m_web_url = url; // 重新创建菜单以更新Web URL显示 DestroyMenu(); CreateMenu(); } #else -void TrayIcon::UpdateWebUrl(const std::string& url) { +void TrayIcon::UpdateWebUrl(const std::string &url) +{ m_web_url = url; // 重新创建菜单以更新Web URL显示 DestroyMenu(); @@ -76,6 +76,32 @@ void TrayIcon::SetExitCallback(const ExitCallback &callback) { m_exit_callback = callback; } +#ifdef _WIN32 +void TrayIcon::ShowWindowsNotification(const std::wstring &title, const std::wstring &message) { + NOTIFYICONDATA nid = m_nid; + nid.uFlags |= NIF_INFO; + wcsncpy_s(nid.szInfoTitle, title.c_str(), _TRUNCATE); + wcsncpy_s(nid.szInfo, message.c_str(), _TRUNCATE); + nid.dwInfoFlags = NIIF_INFO; // 信息图标,可选 NIIF_WARNING, NIIF_ERROR + Shell_NotifyIcon(NIM_MODIFY, &nid); +} +#elif __APPLE__ +void TrayIcon::ShowMacNotification(const std::string &title, const std::string &message) +{ + // 通过 AppleScript 或 Objective-C 桥接 + std::string script = "display notification \"" + message + "\" with title \"" + title + "\""; + std::string cmd = "osascript -e '" + script + "'"; + system(cmd.c_str()); +} +#else +void TrayIcon::ShowLinuxNotification(const std::string &title, const std::string &message) +{ + // 使用 notify-send 命令 + std::string cmd = "notify-send \"" + title + "\" \"" + message + "\""; + system(cmd.c_str()); +} +#endif + void TrayIcon::CreateMenu() { #ifdef _WIN32 if (m_menu) { @@ -165,8 +191,8 @@ LRESULT CALLBACK TrayIcon::WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM } break; - default: - return DefWindowProc(hwnd, msg, wParam, lParam); + default: + return DefWindowProc(hwnd, msg, wParam, lParam); } return 0; } @@ -174,47 +200,56 @@ LRESULT CALLBACK TrayIcon::WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM void TrayIcon::UpdateStatus(const std::wstring &status, const std::wstring &pid) { m_status = status; m_pid = pid; -// 重新创建菜单以更新Status显示 + // 重新创建菜单以更新Status显示 DestroyMenu(); CreateMenu(); } #else // macOS 特定实现 -void TrayIcon::ShowMacTrayIcon() { +void TrayIcon::ShowMacTrayIcon() +{ // 通过 Objective-C 接口显示托盘图标 ShowMacTrayIconImpl(m_app_delegate, m_icon); } -void TrayIcon::HideMacTrayIcon() { +void TrayIcon::HideMacTrayIcon() +{ // 通过 Objective-C 接口隐藏托盘图标 HideMacTrayIconImpl(m_app_delegate); } -void TrayIcon::CreateMacMenu() { +void TrayIcon::CreateMacMenu() +{ // 通过 Objective-C 接口创建菜单 CreateMacMenuImpl(m_app_delegate, m_web_url.c_str()); } -void TrayIcon::DestroyMacMenu() { +void TrayIcon::DestroyMacMenu() +{ // 通过 Objective-C 接口销毁菜单 DestroyMacMenuImpl(m_app_delegate); } -void TrayIcon::OnMacMenuAction(int action) { - switch (action) { +void TrayIcon::OnMacMenuAction(int action) +{ + switch (action) + { case 1001: // 显示主窗口 - if (m_show_window_callback) { + if (m_show_window_callback) + { m_show_window_callback(); } break; case 1002: // 打开Web页面 - if (!m_web_url.empty()) { + if (!m_web_url.empty()) + { OpenWebPageMac(m_web_url.c_str()); } break; case 1003: // 退出 - if (m_exit_callback) { + if (m_exit_callback) + { m_exit_callback(); } break; @@ -222,18 +257,21 @@ void TrayIcon::OnMacMenuAction(int action) { } // C 接口函数,供 Objective-C 调用 -extern "C" void TrayIconMenuCallback(void* tray_instance, int action) { - if (tray_instance) { - static_cast(tray_instance)->OnMacMenuAction(action); +extern "C" void TrayIconMenuCallback(void *tray_instance, int action) +{ + if (tray_instance) + { + static_cast(tray_instance)->OnMacMenuAction(action); } } // 外部声明的 Objective-C 接口函数 -extern "C" { - void ShowMacTrayIconImpl(void* app_delegate, void* icon); - void HideMacTrayIconImpl(void* app_delegate); - void CreateMacMenuImpl(void* app_delegate, const char* web_url); - void DestroyMacMenuImpl(void* app_delegate); - void OpenWebPageMac(const char* url); +extern "C" +{ + void ShowMacTrayIconImpl(void *app_delegate, void *icon); + void HideMacTrayIconImpl(void *app_delegate); + void CreateMacMenuImpl(void *app_delegate, const char *web_url); + void DestroyMacMenuImpl(void *app_delegate); + void OpenWebPageMac(const char *url); } #endif