Compare commits

...

3 Commits

Author SHA1 Message Date
JiXieShi deecdbeeab 新增: 加入托盘图标、通知消息 2025-09-12 20:51:47 +08:00
JiXieShi 57cac6a451 新增: 添加自定义主题设置相关函数 2025-09-12 19:04:20 +08:00
JiXieShi 0a57dae39c 📝 修改: 更新README 2025-09-12 18:02:41 +08:00
12 changed files with 702 additions and 141 deletions
+2 -1
View File
@@ -31,6 +31,7 @@ file(GLOB SRC
${IMGUI_DIR}/misc/cpp/*.cpp ${IMGUI_DIR}/misc/cpp/*.cpp
app/src/*.* app/src/*.*
main.cpp main.cpp
) )
@@ -63,7 +64,7 @@ if(IMGUI_BACKENDS STREQUAL "win32_dx11")
endif() endif()
# generate binary # generate binary
add_executable(${PROJECT_NAME} WIN32 ${SRC} ${PLATFORM_SRC}) add_executable(${PROJECT_NAME} WIN32 ${SRC} ${PLATFORM_SRC} logo.rc)
if(IMGUI_BACKENDS STREQUAL "glfw_opengl") if(IMGUI_BACKENDS STREQUAL "glfw_opengl")
target_link_libraries(${PROJECT_NAME} target_link_libraries(${PROJECT_NAME}
+28 -2
View File
@@ -5,7 +5,26 @@
#include <string> #include <string>
#include <map> #include <map>
#include <vector> #include <vector>
#include <imgui.h>
#include "imgui.h"
struct LogColors {
ImVec4 error_color = ImVec4(1.0f, 0.4f, 0.4f, 1.0f);
ImVec4 warn_color = ImVec4(1.0f, 1.0f, 0.4f, 1.0f);
ImVec4 info_color = ImVec4(0.4f, 1.0f, 0.4f, 1.0f);
ImVec4 debug_color = ImVec4(0.6f, 0.6f, 1.0f, 1.0f);
ImVec4 trace_color = ImVec4(0.8f, 0.8f, 0.8f, 1.0f);
void ResetToDefaults() {
error_color = ImVec4(1.0f, 0.4f, 0.4f, 1.0f);
warn_color = ImVec4(1.0f, 1.0f, 0.4f, 1.0f);
info_color = ImVec4(0.4f, 1.0f, 0.4f, 1.0f);
debug_color = ImVec4(0.6f, 0.6f, 1.0f, 1.0f);
trace_color = ImVec4(0.8f, 0.8f, 0.8f, 1.0f);
}
};
class AppState { class AppState {
public: public:
@@ -50,8 +69,12 @@ public:
std::vector<std::string> command_history; std::vector<std::string> command_history;
int max_command_history; int max_command_history;
LogColors log_colors;
bool use_custom_log_colors = false;
bool use_ansi_colors = true;
bool settings_dirty; bool settings_dirty;
private: private:
// 环境变量序列化辅助函数 // 环境变量序列化辅助函数
std::string SerializeEnvironmentVariables() const; std::string SerializeEnvironmentVariables() const;
@@ -60,8 +83,11 @@ private:
// 编码序列化辅助函数 // 编码序列化辅助函数
std::string SerializeOutputEncoding() const; std::string SerializeOutputEncoding() const;
void DeserializeOutputEncoding(const std::string& serialized); void DeserializeOutputEncoding(const std::string& serialized);
// 日志颜色序列化辅助
std::string SerializeLogColors() const;
void DeserializeLogColors(const std::string &serialized);
// 新增:命令历史记录序列化辅助函数 // 命令历史记录序列化辅助函数
std::string SerializeCommandHistory() const; std::string SerializeCommandHistory() const;
void DeserializeCommandHistory(const std::string& serialized); void DeserializeCommandHistory(const std::string& serialized);
}; };
+74 -58
View File
@@ -37,6 +37,7 @@
#include "AppState.h" #include "AppState.h"
#include "TrayIcon.h" #include "TrayIcon.h"
class Manager { class Manager {
public: public:
// 构造函数和析构函数 // 构造函数和析构函数
@@ -45,59 +46,68 @@ public:
~Manager(); ~Manager();
// 核心生命周期管理 // 核心生命周期管理
bool Initialize(); // 初始化应用程序 bool Initialize(); // 初始化应用程序
void Run(); // 运行主循环 void Run(); // 运行主循环
void Shutdown(); // 关闭应用程序 void Shutdown(); // 关闭应用程序
// 托盘事件回调 // 托盘事件回调
void OnTrayShowWindow(); // 托盘显示窗口事件 void OnTrayShowWindow(); // 托盘显示窗口事件
void OnTrayExit(); // 托盘退出事件 void OnTrayExit(); // 托盘退出事件
// 公共成员变量 // 公共成员变量
AppState m_app_state; // 应用程序状态 AppState m_app_state; // 应用程序状态
private: private:
// 枚举类型定义 // 枚举类型定义
enum class LayoutPreset { enum class LayoutPreset {
Classic, // 经典布局 Classic, // 经典布局
Development, // 开发布局 Development, // 开发布局
Monitoring // 监控布局 Monitoring // 监控布局
}; };
// UI渲染相关方法 // UI渲染相关方法
void RenderUI(); // 渲染主UI void RenderUI(); // 渲染主UI
void RenderMenuBar(); // 渲染菜单栏 void RenderMenuBar(); // 渲染菜单栏
void RenderMainContent(); // 渲染主内容区域 void RenderMainContent(); // 渲染主内容区域
void RenderSettingsMenu(); // 渲染设置菜单
void RenderStopCommandSettings(); // 渲染停止命令设置
void RenderEnvironmentVariablesSettings(); // 渲染环境变量设置 void RenderSettingsMenu(); // 渲染设置菜单
void RenderOutputEncodingSettings(); // 渲染输出编码设置 void RenderStopCommandSettings(); // 渲染停止命令设置
void RenderControlPanel(float buttonWidth, float buttonHeight, float inputWidth); // 渲染控制面板 void RenderEnvironmentVariablesSettings(); // 渲染环境变量设置
void RenderCommandPanel(float buttonWidth, float inputWidth); // 渲染命令面板 void RenderOutputEncodingSettings(); // 渲染输出编码设置
void RenderLogPanel(); // 渲染日志面板 void RenderColorThemeSettings();
void RenderCommandHistory(); // 渲染命令历史
void RenderStatusMessages(); // 渲染状态消息 void RenderControlPanel(float buttonWidth, float buttonHeight, float inputWidth); // 渲染控制面板
void RenderCommandPanel(float buttonWidth, float inputWidth); // 渲染命令面板
void RenderLogPanel(); // 渲染日志面板
void RenderCommandHistory(); // 渲染命令历史
void RenderStatusMessages(); // 渲染状态消息
// 布局管理相关方法 // 布局管理相关方法
void SetupDefaultDockingLayout(ImGuiID dockspace_id); // 设置默认停靠布局 void SetupDefaultDockingLayout(ImGuiID dockspace_id); // 设置默认停靠布局
void RenderLayoutMenu(); // 渲染布局菜单 void RenderLayoutMenu(); // 渲染布局菜单
static void ApplyPresetLayout(LayoutPreset preset); // 应用预设布局 static void ApplyPresetLayout(LayoutPreset preset); // 应用预设布局
void SaveCurrentLayout(); // 保存当前布局 void SaveCurrentLayout(); // 保存当前布局
void LoadSavedLayout(); // 加载已保存布局 void LoadSavedLayout(); // 加载已保存布局
// 事件处理相关方法 // 事件处理相关方法
void HandleMessages(); // 处理消息 void HandleMessages(); // 处理消息
bool ShouldExit() const; // 检查是否应该退出 bool ShouldExit() const; // 检查是否应该退出
static void ContentScaleCallback(GLFWwindow *window, float xscale, float yscale); // 内容缩放回调 static void ContentScaleCallback(GLFWwindow *window, float xscale, float yscale); // 内容缩放回调
// 窗口管理相关方法 // 窗口管理相关方法
void ShowMainWindow(); // 显示主窗口 void ShowMainWindow(); // 显示主窗口
void HideMainWindow(); // 隐藏主窗口 void HideMainWindow(); // 隐藏主窗口
// DPI相关方法 // DPI相关方法
void UpdateDPIScale(); // 更新DPI缩放 void UpdateDPIScale(); // 更新DPI缩放
void ReloadFonts() const; // 重新加载字体 void ReloadFonts() const; // 重新加载字体
void SaveCurrentTheme();
void LoadSavedTheme();
ImVec4 GetCustomLogLevelColor(const std::string &log);
// 平台相关初始化方法 // 平台相关初始化方法
#ifdef USE_WIN32_BACKEND #ifdef USE_WIN32_BACKEND
@@ -108,17 +118,17 @@ private:
static LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); // 窗口过程 static LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); // 窗口过程
#else #else
bool InitializeGLFW(); // 初始化GLFW bool InitializeGLFW(); // 初始化GLFW
void CleanupGLFW(); // 清理GLFW void CleanupGLFW(); // 清理GLFW
static void GlfwErrorCallback(int error, const char *description); // GLFW错误回调 static void GlfwErrorCallback(int error, const char *description); // GLFW错误回调
#endif #endif
// 托盘相关方法 // 托盘相关方法
bool InitializeTray(); // 初始化托盘 bool InitializeTray(); // 初始化托盘
void CleanupTray(); // 清理托盘 void CleanupTray(); // 清理托盘
#ifdef _WIN32 #ifdef _WIN32
static HWND CreateHiddenWindow(); // 创建隐藏窗口 static HWND CreateHiddenWindow(); // 创建隐藏窗口
#endif #endif
// 平台相关成员变量 // 平台相关成员变量
@@ -130,41 +140,47 @@ private:
IDXGISwapChain* m_pSwapChain = nullptr; // 交换链 IDXGISwapChain* m_pSwapChain = nullptr; // 交换链
ID3D11RenderTargetView* m_mainRenderTargetView = nullptr; // 主渲染目标视图 ID3D11RenderTargetView* m_mainRenderTargetView = nullptr; // 主渲染目标视图
#else #else
GLFWwindow *m_window = nullptr; // GLFW窗口 GLFWwindow *m_window = nullptr; // GLFW窗口
const char *m_glsl_version = nullptr; // GLSL版本 const char *m_glsl_version = nullptr; // GLSL版本
#endif #endif
// 托盘相关成员变量 // 托盘相关成员变量
std::unique_ptr<TrayIcon> m_tray; // 托盘图标 std::unique_ptr<TrayIcon> m_tray; // 托盘图标
#ifdef _WIN32 #ifdef _WIN32
HWND m_tray_hwnd = nullptr; // 托盘窗口句柄 HWND m_tray_hwnd = nullptr; // 托盘窗口句柄
#endif #endif
// 控制标志 // 控制标志
bool m_should_exit = false; // 是否应该退出 bool m_should_exit = false; // 是否应该退出
bool m_initialized = false; // 是否已初始化 bool m_initialized = false; // 是否已初始化
bool m_fullscreen = false; bool m_fullscreen = false;
bool m_padding = false; bool m_padding = false;
// DPI缩放相关 // DPI缩放相关
float m_dpi_scale = 1.0f; // 当前DPI缩放 float m_dpi_scale = 1.0f; // 当前DPI缩放
float m_last_dpi_scale = 1.0f; // 上次DPI缩放 float m_last_dpi_scale = 1.0f; // 上次DPI缩放
// 布局相关成员变量 // 布局相关成员变量
ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_None; ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_None;
ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_MenuBar; ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_MenuBar;
bool m_apply_preset_layout = false; // 是否需要应用预设布局 bool m_apply_preset_layout = false; // 是否需要应用预设布局
LayoutPreset m_pending_preset = LayoutPreset::Classic; // 待应用的预设布局 LayoutPreset m_pending_preset = LayoutPreset::Classic; // 待应用的预设布局
bool m_reset_layout = false; // 是否重置布局 bool m_reset_layout = false; // 是否重置布局
bool m_show_save_success = false; // 是否显示保存成功消息 bool m_show_save_success = false; // 是否显示保存成功消息
bool m_show_load_success = false; // 是否显示加载成功消息 bool m_show_load_success = false; // 是否显示加载成功消息
float m_save_success_timer = 0.0f; // 保存成功消息计时器 float m_save_success_timer = 0.0f; // 保存成功消息计时器
float m_load_success_timer = 0.0f; // 加载成功消息计时器 float m_load_success_timer = 0.0f; // 加载成功消息计时器
// UI状态相关成员变量 // UI状态相关成员变量
char env_key_input_[256] = {}; // 环境变量键输入缓冲区 char env_key_input_[256] = {}; // 环境变量键输入缓冲区
char env_value_input_[512] = {}; // 环境变量值输入缓冲区 char env_value_input_[512] = {}; // 环境变量值输入缓冲区
bool show_env_settings_ = false; // 是否显示环境变量设置 bool show_env_settings_ = false; // 是否显示环境变量设置
bool show_encoding_settings_ = false; // 是否显示编码设置 bool show_encoding_settings_ = false; // 是否显示编码设置
bool show_command_history_ = false; // 是否显示命令历史 bool show_command_history_ = false; // 是否显示命令历史
bool m_show_theme_save_success = true;
float m_theme_save_success_timer = 3.0f;
float m_theme_load_success_timer = 0.0f;
bool m_show_theme_load_success = false;
}; };
+10 -4
View File
@@ -35,12 +35,18 @@ public:
// 设置回调函数 // 设置回调函数
void SetShowWindowCallback(const ShowWindowCallback &callback); void SetShowWindowCallback(const ShowWindowCallback &callback);
void SetExitCallback(const ExitCallback &callback); void SetExitCallback(const ExitCallback &callback);
enum class NotifyAction {
Notify_NONE,
Notify_INFO,
Notify_WARNING,
Notify_ERROR,
Notify_USER
};
#ifdef _WIN32 #ifdef _WIN32
void ShowWindowsNotification(const std::wstring& title, const std::wstring& message); void ShowNotification(const std::wstring &title, const std::wstring &message, NotifyAction notify= NotifyAction::Notify_INFO) const;
#elif __APPLE__
void ShowMacNotification(const std::string& title, const std::string& message);
#else #else
void ShowLinuxNotification(const std::string& title, const std::string& message); void ShowNotification(const std::string& title, const std::string& message, NotifyAction notify= NotifyAction::Notify_INFO);
#endif #endif
private: private:
void CreateMenu(); void CreateMenu();
+87
View File
@@ -160,6 +160,82 @@ void AppState::DeserializeOutputEncoding(const std::string& serialized) {
} }
} }
std::string AppState::SerializeLogColors() const {
std::ostringstream oss;
// 按顺序保存每个颜色的RGBA值
oss << log_colors.error_color.x << ","
<< log_colors.error_color.y << ","
<< log_colors.error_color.z << ","
<< log_colors.error_color.w << "|";
oss << log_colors.warn_color.x << ","
<< log_colors.warn_color.y << ","
<< log_colors.warn_color.z << ","
<< log_colors.warn_color.w << "|";
oss << log_colors.info_color.x << ","
<< log_colors.info_color.y << ","
<< log_colors.info_color.z << ","
<< log_colors.info_color.w << "|";
oss << log_colors.debug_color.x << ","
<< log_colors.debug_color.y << ","
<< log_colors.debug_color.z << ","
<< log_colors.debug_color.w << "|";
oss << log_colors.trace_color.x << ","
<< log_colors.trace_color.y << ","
<< log_colors.trace_color.z << ","
<< log_colors.trace_color.w;
return oss.str();
}
// 新增:反序列化日志颜色
void AppState::DeserializeLogColors(const std::string& serialized) {
if (serialized.empty()) {
log_colors.ResetToDefaults();
return;
}
std::istringstream iss(serialized);
std::string colorStr;
int colorIndex = 0;
while (std::getline(iss, colorStr, '|') && colorIndex < 5) {
std::istringstream colorIss(colorStr);
std::string component;
float rgba[4] = {0.0f, 0.0f, 0.0f, 1.0f};
int i = 0;
while (std::getline(colorIss, component, ',') && i < 4) {
try {
rgba[i++] = std::stof(component);
} catch (const std::exception&) {
// 如果解析失败,使用默认值
}
}
ImVec4 color(rgba[0], rgba[1], rgba[2], rgba[3]);
switch (colorIndex) {
case 0: log_colors.error_color = color; break;
case 1: log_colors.warn_color = color; break;
case 2: log_colors.info_color = color; break;
case 3: log_colors.debug_color = color; break;
case 4: log_colors.trace_color = color; break;
}
colorIndex++;
}
// 如果没有足够的颜色数据,重置为默认值
if (colorIndex < 5) {
log_colors.ResetToDefaults();
}
}
void AppState::LoadSettings() { void AppState::LoadSettings() {
std::ifstream file("climanager_settings.ini"); std::ifstream file("climanager_settings.ini");
if (!file.is_open()) return; if (!file.is_open()) return;
@@ -234,6 +310,14 @@ void AppState::LoadSettings() {
else if (key == "MaxCommandHistory") { else if (key == "MaxCommandHistory") {
max_command_history = std::stoi(value); max_command_history = std::stoi(value);
max_command_history = std::max(5, std::min(max_command_history, 100)); max_command_history = std::max(5, std::min(max_command_history, 100));
}else if (key == "UseCustomLogColors") {
use_custom_log_colors = (value == "1");
}
else if (key == "UseAnsiColors") {
use_ansi_colors = (value == "1");
}
else if (key == "LogColors") {
DeserializeLogColors(value);
} }
} }
} }
@@ -271,6 +355,9 @@ void AppState::SaveSettings() {
file << "CommandHistory=" << SerializeCommandHistory() << "\n"; file << "CommandHistory=" << SerializeCommandHistory() << "\n";
file << "MaxCommandHistory=" << max_command_history << "\n"; file << "MaxCommandHistory=" << max_command_history << "\n";
file << "UseCustomLogColors=" << (use_custom_log_colors ? "1" : "0") << "\n";
file << "UseAnsiColors=" << (use_ansi_colors ? "1" : "0") << "\n";
file << "LogColors=" << SerializeLogColors() << "\n";
file.close(); file.close();
settings_dirty = false; settings_dirty = false;
-1
View File
@@ -503,7 +503,6 @@ void CLIProcess::Start(const std::string& command) {
if (result) { if (result) {
AddLog("进程已启动: " + command + " PID: " + std::to_string(pi_.dwProcessId)); AddLog("进程已启动: " + command + " PID: " + std::to_string(pi_.dwProcessId));
CloseHandle(hWritePipe_); CloseHandle(hWritePipe_);
CloseHandle(hReadPipe_stdin_); CloseHandle(hReadPipe_stdin_);
hWritePipe_ = nullptr; hWritePipe_ = nullptr;
+335 -36
View File
@@ -6,6 +6,7 @@
#include "imgui_internal.h" #include "imgui_internal.h"
#include "resource.h"
#include "Units.h" #include "Units.h"
Manager::Manager() = default; Manager::Manager() = default;
@@ -28,10 +29,10 @@ bool Manager::Initialize() {
IMGUI_CHECKVERSION(); IMGUI_CHECKVERSION();
ImGui::CreateContext(); ImGui::CreateContext();
ImGuiIO &io = ImGui::GetIO(); ImGuiIO &io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking
// io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows // io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows
io.ConfigViewportsNoAutoMerge = true; io.ConfigViewportsNoAutoMerge = true;
io.IniFilename = "imgui.ini"; io.IniFilename = "imgui.ini";
@@ -67,10 +68,10 @@ bool Manager::Initialize() {
// 加载字体 // 加载字体
#ifdef _WIN32 #ifdef _WIN32
ImFont *font = io.Fonts->AddFontFromFileTTF( ImFont *font = io.Fonts->AddFontFromFileTTF(
"C:/Windows/Fonts/msyh.ttc", "C:/Windows/Fonts/msyh.ttc",
16.0f * m_dpi_scale, 16.0f * m_dpi_scale,
nullptr, nullptr,
io.Fonts->GetGlyphRangesChineseFull() io.Fonts->GetGlyphRangesChineseFull()
); );
#elif __APPLE__ #elif __APPLE__
// macOS 中文字体路径 // macOS 中文字体路径
@@ -113,6 +114,7 @@ bool Manager::Initialize() {
m_app_state.ApplySettings(); m_app_state.ApplySettings();
m_app_state.SaveSettings(); m_app_state.SaveSettings();
LoadSavedTheme();
#ifdef _WIN32 #ifdef _WIN32
m_tray->UpdateWebUrl(StringToWide(m_app_state.web_url)); m_tray->UpdateWebUrl(StringToWide(m_app_state.web_url));
@@ -125,6 +127,7 @@ bool Manager::Initialize() {
m_app_state.cli_process.Start(m_app_state.command_input); m_app_state.cli_process.Start(m_app_state.command_input);
m_app_state.show_main_window = false; m_app_state.show_main_window = false;
HideMainWindow(); HideMainWindow();
m_tray->ShowNotification(L"CLI自启动",StringToWide(m_app_state.command_input) + L"\n当前状态:" + (m_app_state.cli_process.IsRunning() ? L"运行中" : L"已停止"));
} }
m_tray->UpdateStatus(m_app_state.cli_process.IsRunning() ? L"运行中" : L"已停止", m_app_state.cli_process.GetPid()); m_tray->UpdateStatus(m_app_state.cli_process.IsRunning() ? L"运行中" : L"已停止", m_app_state.cli_process.GetPid());
m_initialized = true; m_initialized = true;
@@ -214,7 +217,7 @@ void Manager::RenderUI() {
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoMove; ImGuiWindowFlags_NoMove;
window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus; window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;
} else { } else {
dockspace_flags &= ~ImGuiDockNodeFlags_PassthruCentralNode; dockspace_flags &= ~ImGuiDockNodeFlags_PassthruCentralNode;
@@ -226,7 +229,7 @@ void Manager::RenderUI() {
if (!m_padding) if (!m_padding)
ImGui::PopStyleVar(); ImGui::PopStyleVar();
if (m_fullscreen) if (m_fullscreen)
ImGui::PopStyleVar(2); // Submit the DockSpace ImGui::PopStyleVar(2); // Submit the DockSpace
ImGui::Begin("CLI程序管理工具", &m_app_state.show_main_window, ImGui::Begin("CLI程序管理工具", &m_app_state.show_main_window,
window_flags); window_flags);
@@ -302,9 +305,10 @@ void Manager::RenderMenuBar() {
// 添加主题菜单 // 添加主题菜单
if (ImGui::BeginMenu("主题")) { if (ImGui::BeginMenu("主题")) {
if (ImGui::MenuItem("暗黑(Dark)")) { ImGui::StyleColorsDark(); } // if (ImGui::MenuItem("暗黑(Dark)")) { ImGui::StyleColorsDark(); }
if (ImGui::MenuItem("明亮(Light)")) { ImGui::StyleColorsLight(); } // if (ImGui::MenuItem("明亮(Light)")) { ImGui::StyleColorsLight(); }
if (ImGui::MenuItem("经典(Classic)")) { ImGui::StyleColorsClassic(); } // if (ImGui::MenuItem("经典(Classic)")) { ImGui::StyleColorsClassic(); }
RenderColorThemeSettings();
ImGui::EndMenu(); ImGui::EndMenu();
} }
@@ -313,7 +317,7 @@ void Manager::RenderMenuBar() {
ImGui::MenuItem("填充(Padding)", nullptr, &m_padding); ImGui::MenuItem("填充(Padding)", nullptr, &m_padding);
ImGui::Separator(); ImGui::Separator();
if (ImGui::MenuItem("标志:不分割(Flag: NoSplit)", "", (dockspace_flags & ImGuiDockNodeFlags_NoSplit) != if (ImGui::MenuItem("标志:不分割(Flag: NoSplit)", "", (dockspace_flags & ImGuiDockNodeFlags_NoSplit) !=
0)) { dockspace_flags ^= ImGuiDockNodeFlags_NoSplit; } 0)) { dockspace_flags ^= ImGuiDockNodeFlags_NoSplit; }
if (ImGui::MenuItem("标志:不调整大小(Flag: NoResize)", "", if (ImGui::MenuItem("标志:不调整大小(Flag: NoResize)", "",
(dockspace_flags & ImGuiDockNodeFlags_NoResize) != (dockspace_flags & ImGuiDockNodeFlags_NoResize) !=
0)) { dockspace_flags ^= ImGuiDockNodeFlags_NoResize; } 0)) { dockspace_flags ^= ImGuiDockNodeFlags_NoResize; }
@@ -485,9 +489,11 @@ void Manager::RenderStatusMessages() {
} }
ImGui::End(); ImGui::End();
m_save_success_timer -= ImGui::GetIO().DeltaTime; m_save_success_timer -= ImGui::GetIO().DeltaTime;
if (m_save_success_timer <= 0.0f) { if (m_save_success_timer <= 0.0f) {
m_show_save_success = false; m_show_save_success = false;
m_tray->ShowNotification(L"CLI_Manager",L"当前布局保存成功!");
} }
} }
@@ -501,13 +507,54 @@ void Manager::RenderStatusMessages() {
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_AlwaysAutoResize)) { ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f), "布局已加载");
} }
ImGui::End(); ImGui::End();
m_load_success_timer -= ImGui::GetIO().DeltaTime; m_load_success_timer -= ImGui::GetIO().DeltaTime;
if (m_load_success_timer <= 0.0f) { if (m_load_success_timer <= 0.0f) {
m_show_load_success = false; m_show_load_success = false;
ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f), "布局已加载");
}
}
if (m_show_theme_save_success) {
ImGui::SetNextWindowPos(ImVec2(ImGui::GetMainViewport()->Size.x * 0.5f, 50), ImGuiCond_Always,
ImVec2(0.5f, 0.0f));
ImGui::SetNextWindowBgAlpha(0.8f);
if (ImGui::Begin("ThemeSaveSuccess", nullptr,
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f), "主题已保存");
}
ImGui::End();
m_theme_save_success_timer -= ImGui::GetIO().DeltaTime;
if (m_theme_save_success_timer <= 0.0f) {
m_show_theme_save_success = false;
m_tray->ShowNotification(L"CLI_Manager",L"当前主题保存成功!");
}
}
// 渲染主题加载成功提示
if (m_show_theme_load_success) {
ImGui::SetNextWindowPos(ImVec2(ImGui::GetMainViewport()->Size.x * 0.5f, 50), ImGuiCond_Always,
ImVec2(0.5f, 0.0f));
ImGui::SetNextWindowBgAlpha(0.8f);
if (ImGui::Begin("ThemeLoadSuccess", nullptr,
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f), "主题已加载");
}
ImGui::End();
m_theme_load_success_timer -= ImGui::GetIO().DeltaTime;
if (m_theme_load_success_timer <= 0.0f) {
m_show_theme_load_success = false;
} }
} }
} }
@@ -585,6 +632,7 @@ void Manager::RenderSettingsMenu() {
RenderStopCommandSettings(); RenderStopCommandSettings();
RenderEnvironmentVariablesSettings(); RenderEnvironmentVariablesSettings();
RenderOutputEncodingSettings(); RenderOutputEncodingSettings();
} }
void Manager::RenderStopCommandSettings() { void Manager::RenderStopCommandSettings() {
@@ -690,7 +738,7 @@ void Manager::RenderEnvironmentVariablesSettings() {
ImGui::Spacing(); ImGui::Spacing();
ImGui::TextWrapped( ImGui::TextWrapped(
"说明:启用后,CLI程序将使用这些自定义环境变量。这些变量会与系统环境变量合并,同名变量会被覆盖。"); "说明:启用后,CLI程序将使用这些自定义环境变量。这些变量会与系统环境变量合并,同名变量会被覆盖。");
ImGui::Unindent(); ImGui::Unindent();
} else { } else {
@@ -773,8 +821,9 @@ void Manager::RenderControlPanel(float buttonWidth, float buttonHeight, float in
m_app_state.AddCommandToHistory(m_app_state.command_input); m_app_state.AddCommandToHistory(m_app_state.command_input);
if (strlen(m_app_state.working_directory) > 0) { if (strlen(m_app_state.working_directory) > 0) {
m_app_state.cli_process.SetWorkingDirectory(m_app_state.working_directory); m_app_state.cli_process.SetWorkingDirectory(m_app_state.working_directory);
}else { } else {
strncpy_s(m_app_state.working_directory,m_app_state.cli_process.GetWorkingDirectory().c_str(),sizeof(m_app_state.working_directory)-1); strncpy_s(m_app_state.working_directory, m_app_state.cli_process.GetWorkingDirectory().c_str(),
sizeof(m_app_state.working_directory) - 1);
} }
m_tray->UpdateStatus(m_app_state.cli_process.IsRunning() ? L"运行中" : L"已停止", m_tray->UpdateStatus(m_app_state.cli_process.IsRunning() ? L"运行中" : L"已停止",
m_app_state.cli_process.GetPid()); m_app_state.cli_process.GetPid());
@@ -793,6 +842,7 @@ void Manager::RenderControlPanel(float buttonWidth, float buttonHeight, float in
m_app_state.AddCommandToHistory(m_app_state.command_input); m_app_state.AddCommandToHistory(m_app_state.command_input);
m_tray->UpdateStatus(m_app_state.cli_process.IsRunning() ? L"运行中" : L"已停止", m_tray->UpdateStatus(m_app_state.cli_process.IsRunning() ? L"运行中" : L"已停止",
m_app_state.cli_process.GetPid()); m_app_state.cli_process.GetPid());
m_tray->ShowNotification(L"CLI_Manager",m_app_state.cli_process.IsRunning() ? L"重启成功!" : L"重启失败!");
} }
} }
@@ -800,9 +850,9 @@ void Manager::RenderControlPanel(float buttonWidth, float buttonHeight, float in
// 状态显示 // 状态显示
ImGui::SeparatorText("运行状态"); ImGui::SeparatorText("运行状态");
ImVec4 statusColor = m_app_state.cli_process.IsRunning() ? ImVec4 statusColor = m_app_state.cli_process.IsRunning()
ImVec4(0.0f, 1.0f, 0.0f, 1.0f) : ? ImVec4(0.0f, 1.0f, 0.0f, 1.0f)
ImVec4(1.0f, 0.0f, 0.0f, 1.0f); : ImVec4(1.0f, 0.0f, 0.0f, 1.0f);
ImGui::TextColored(statusColor, "状态: %s", ImGui::TextColored(statusColor, "状态: %s",
m_app_state.cli_process.IsRunning() ? "运行中" : "已停止"); m_app_state.cli_process.IsRunning() ? "运行中" : "已停止");
} }
@@ -825,7 +875,7 @@ void Manager::RenderCommandPanel(float buttonWidth, float inputWidth) {
// 显示发送状态 // 显示发送状态
if (!m_app_state.cli_process.IsRunning()) { if (!m_app_state.cli_process.IsRunning()) {
ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "提示: 程序未运行,无法发送命令"); ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 0.6f), "提示: 程序未运行,无法发送命令");
} }
} }
@@ -875,11 +925,17 @@ void Manager::RenderLogPanel() {
const std::string &log = logs[i]; const std::string &log = logs[i];
if (m_app_state.enable_colored_logs) { if (m_app_state.enable_colored_logs) {
RenderColoredLogLine(log); if (m_app_state.use_ansi_colors) {
// 使用ANSI颜色转义序列解析
RenderColoredLogLine(log);
} else {
// 仅使用日志级别的颜色区分
ImVec4 textColor = GetCustomLogLevelColor(log);
ImGui::TextColored(textColor, "%s", log.c_str());
}
} else { } else {
// 简单的日志级别颜色区分 // 不启用彩色显示,使用默认白色
ImVec4 textColor = GetLogLevelColor(log); ImGui::TextUnformatted(log.c_str());
ImGui::TextColored(textColor, "%s", log.c_str());
} }
} }
} }
@@ -1047,7 +1103,12 @@ bool Manager::InitializeTray() {
return false; return false;
} }
HICON trayIcon = LoadIcon(NULL, IDI_APPLICATION); // 加载自定义图标
HICON trayIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON1));
if (!trayIcon) {
// 如果加载失败,使用默认图标
trayIcon = LoadIcon(NULL, IDI_APPLICATION);
}
m_tray = std::make_unique<TrayIcon>(m_tray_hwnd, trayIcon); m_tray = std::make_unique<TrayIcon>(m_tray_hwnd, trayIcon);
// 设置托盘窗口的用户数据,指向TrayIcon实例 // 设置托盘窗口的用户数据,指向TrayIcon实例
@@ -1087,14 +1148,14 @@ HWND Manager::CreateHiddenWindow() {
} }
return CreateWindowEx( return CreateWindowEx(
0, 0,
wc.lpszClassName, wc.lpszClassName,
L"CLI Manager Tray Window", L"CLI Manager Tray Window",
0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
NULL, NULL, NULL, NULL,
wc.hInstance, wc.hInstance,
NULL NULL
); );
} }
@@ -1303,7 +1364,7 @@ bool Manager::InitializeGLFW() {
int windowWidth = static_cast<int>(screenWidth * 0.8); int windowWidth = static_cast<int>(screenWidth * 0.8);
int windowHeight = static_cast<int>(screenHeight * 0.8); int windowHeight = static_cast<int>(screenHeight * 0.8);
// glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);//窗口隐藏 // glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);//窗口隐藏
m_window = glfwCreateWindow(windowWidth, windowHeight, "CLI程序管理工具", nullptr, nullptr); m_window = glfwCreateWindow(windowWidth, windowHeight, "CLI程序管理工具", nullptr, nullptr);
if (!m_window) if (!m_window)
@@ -1502,3 +1563,241 @@ extern "C" {
void* GetMacTrayIcon(); void* GetMacTrayIcon();
} }
#endif #endif
void Manager::RenderColorThemeSettings() {
// 预设主题
if (ImGui::MenuItem("暗黑(Dark)")) {
ImGui::StyleColorsDark();
}
if (ImGui::MenuItem("明亮(Light)")) {
ImGui::StyleColorsLight();
}
if (ImGui::MenuItem("经典(Classic)")) {
ImGui::StyleColorsClassic();
}
ImGui::Separator();
ImGui::Text("颜色主题设置");
if (ImGui::Checkbox("自定义日志颜色", &m_app_state.use_custom_log_colors)) {
m_app_state.settings_dirty = true;
}
if (m_app_state.use_custom_log_colors) {
ImGui::Indent();
ImGui::Text("日志级别颜色:");
// 编辑错误日志颜色
ImVec4 errorColor = ImVec4(
m_app_state.log_colors.error_color.x,
m_app_state.log_colors.error_color.y,
m_app_state.log_colors.error_color.z,
1.0f
);
if (ImGui::ColorEdit3("错误日志", (float *) &errorColor)) {
m_app_state.log_colors.error_color = errorColor;
m_app_state.settings_dirty = true;
}
// 编辑警告日志颜色
ImVec4 warnColor = ImVec4(
m_app_state.log_colors.warn_color.x,
m_app_state.log_colors.warn_color.y,
m_app_state.log_colors.warn_color.z,
1.0f
);
if (ImGui::ColorEdit3("警告日志", (float *) &warnColor)) {
m_app_state.log_colors.warn_color = warnColor;
m_app_state.settings_dirty = true;
}
// 编辑信息日志颜色
ImVec4 infoColor = ImVec4(
m_app_state.log_colors.info_color.x,
m_app_state.log_colors.info_color.y,
m_app_state.log_colors.info_color.z,
1.0f
);
if (ImGui::ColorEdit3("信息日志", (float *) &infoColor)) {
m_app_state.log_colors.info_color = infoColor;
m_app_state.settings_dirty = true;
}
// 编辑调试日志颜色
ImVec4 debugColor = ImVec4(
m_app_state.log_colors.debug_color.x,
m_app_state.log_colors.debug_color.y,
m_app_state.log_colors.debug_color.z,
1.0f
);
if (ImGui::ColorEdit3("调试日志", (float *) &debugColor)) {
m_app_state.log_colors.debug_color = debugColor;
m_app_state.settings_dirty = true;
}
// 重置为默认颜色按钮
if (ImGui::Button("重置为默认颜色")) {
m_app_state.log_colors.ResetToDefaults();
m_app_state.settings_dirty = true;
}
ImGui::Unindent();
}
// ANSI颜色设置
ImGui::Separator();
if (ImGui::Checkbox("使用ANSI颜色转义", &m_app_state.use_ansi_colors)) {
m_app_state.settings_dirty = true;
}
ImGui::SameLine();
ImGui::TextDisabled("(?)");
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
ImGui::TextUnformatted("启用后,日志中的ANSI颜色转义序列将被解析并显示为彩色文本。");
ImGui::TextUnformatted("例如: \\033[31m红色文本\\033[0m");
ImGui::PopTextWrapPos();
ImGui::EndTooltip();
}
// 颜色样式设置
ImGui::Text("全局颜色样式:");
static int currentTheme = 0; // 默认暗色主题
const char *themes[] = {"暗黑 (Dark)", "明亮 (Light)", "经典 (Classic)", "自定义 (Custom)"};
if (ImGui::Combo("应用主题", &currentTheme, themes, IM_ARRAYSIZE(themes))) {
switch (currentTheme) {
case 0: ImGui::StyleColorsDark();
break;
case 1: ImGui::StyleColorsLight();
break;
case 2: ImGui::StyleColorsClassic();
break;
case 3: /* 应用自定义主题 */ break;
}
}
// 自定义主题编辑器(仅在选择"自定义"主题时显示)
if (currentTheme == 3) {
if (ImGui::TreeNode("自定义主题编辑器")) {
ImGuiStyle &style = ImGui::GetStyle();
// 添加主题颜色编辑器
for (int i = 0; i < ImGuiCol_COUNT; i++) {
const char *name = ImGui::GetStyleColorName(i);
ImGui::ColorEdit4(name, (float *) &style.Colors[i]);
}
// 添加样式参数编辑器
ImGui::SliderFloat("窗口圆角", &style.WindowRounding, 0.0f, 12.0f, "%.0f");
ImGui::SliderFloat("子窗口圆角", &style.ChildRounding, 0.0f, 12.0f, "%.0f");
ImGui::SliderFloat("框架圆角", &style.FrameRounding, 0.0f, 12.0f, "%.0f");
ImGui::SliderFloat("弹出窗口圆角", &style.PopupRounding, 0.0f, 12.0f, "%.0f");
ImGui::SliderFloat("滚动条圆角", &style.ScrollbarRounding, 0.0f, 12.0f, "%.0f");
ImGui::SliderFloat("抓取圆角", &style.GrabRounding, 0.0f, 12.0f, "%.0f");
ImGui::SliderFloat("标签圆角", &style.TabRounding, 0.0f, 12.0f, "%.0f");
ImGui::Separator();
// 保存/加载主题按钮
if (ImGui::Button("保存当前主题")) {
SaveCurrentTheme();
}
ImGui::SameLine();
if (ImGui::Button("加载保存的主题")) {
LoadSavedTheme();
}
ImGui::TreePop();
}
}
}
// 保存当前主题设置
void Manager::SaveCurrentTheme() {
std::ofstream file("theme.ini", std::ios::binary);
if (file.is_open()) {
ImGuiStyle &style = ImGui::GetStyle();
// 保存主题颜色
for (int i = 0; i < ImGuiCol_COUNT; i++) {
file.write(reinterpret_cast<const char *>(&style.Colors[i]), sizeof(ImVec4));
}
// 保存样式参数
file.write(reinterpret_cast<const char *>(&style.WindowRounding), sizeof(float));
file.write(reinterpret_cast<const char *>(&style.ChildRounding), sizeof(float));
file.write(reinterpret_cast<const char *>(&style.FrameRounding), sizeof(float));
file.write(reinterpret_cast<const char *>(&style.PopupRounding), sizeof(float));
file.write(reinterpret_cast<const char *>(&style.ScrollbarRounding), sizeof(float));
file.write(reinterpret_cast<const char *>(&style.GrabRounding), sizeof(float));
file.write(reinterpret_cast<const char *>(&style.TabRounding), sizeof(float));
file.close();
// 显示保存成功提示
m_show_theme_save_success = true;
m_theme_save_success_timer = 3.0f; // 3秒后消失
}
}
// 加载保存的主题设置
void Manager::LoadSavedTheme() {
std::ifstream file("theme.ini", std::ios::binary);
if (file.is_open()) {
ImGuiStyle &style = ImGui::GetStyle();
// 加载主题颜色
for (int i = 0; i < ImGuiCol_COUNT; i++) {
file.read(reinterpret_cast<char *>(&style.Colors[i]), sizeof(ImVec4));
}
// 加载样式参数
file.read(reinterpret_cast<char *>(&style.WindowRounding), sizeof(float));
file.read(reinterpret_cast<char *>(&style.ChildRounding), sizeof(float));
file.read(reinterpret_cast<char *>(&style.FrameRounding), sizeof(float));
file.read(reinterpret_cast<char *>(&style.PopupRounding), sizeof(float));
file.read(reinterpret_cast<char *>(&style.ScrollbarRounding), sizeof(float));
file.read(reinterpret_cast<char *>(&style.GrabRounding), sizeof(float));
file.read(reinterpret_cast<char *>(&style.TabRounding), sizeof(float));
file.close();
// 显示加载成功提示
m_show_theme_load_success = true;
m_theme_load_success_timer = 3.0f; // 3秒后消失
}
}
ImVec4 Manager::GetCustomLogLevelColor(const std::string &log) {
if (!m_app_state.use_custom_log_colors) {
// 使用默认颜色
return GetLogLevelColor(log);
}
// 使用自定义颜色
if (log.find("错误") != std::string::npos || log.find("[E]") != std::string::npos ||
log.find("[ERROR]") != std::string::npos || log.find("error") != std::string::npos) {
return m_app_state.log_colors.error_color;
} else if (log.find("警告") != std::string::npos || log.find("[W]") != std::string::npos ||
log.find("[WARN]") != std::string::npos || log.find("warning") != std::string::npos) {
return m_app_state.log_colors.warn_color;
} else if (log.find("信息") != std::string::npos || log.find("[I]") != std::string::npos ||
log.find("[INFO]") != std::string::npos || log.find("info") != std::string::npos) {
return m_app_state.log_colors.info_color;
} else if (log.find("调试") != std::string::npos || log.find("[D]") != std::string::npos ||
log.find("[DEBUG]") != std::string::npos || log.find("debug") != std::string::npos) {
return m_app_state.log_colors.debug_color;
} else if (log.find("跟踪") != std::string::npos || log.find("[T]") != std::string::npos ||
log.find("[TRACE]") != std::string::npos || log.find("trace") != std::string::npos) {
return m_app_state.log_colors.trace_color;
}
return ImVec4(1.0f, 1.0f, 1.0f, 1.0f); // 默认白色
}
+4 -4
View File
@@ -77,16 +77,16 @@ void TrayIcon::SetExitCallback(const ExitCallback &callback) {
} }
#ifdef _WIN32 #ifdef _WIN32
void TrayIcon::ShowWindowsNotification(const std::wstring &title, const std::wstring &message) { void TrayIcon::ShowNotification(const std::wstring &title, const std::wstring &message, NotifyAction notify) const {
NOTIFYICONDATA nid = m_nid; NOTIFYICONDATA nid = m_nid;
nid.uFlags |= NIF_INFO; nid.uFlags |= NIF_INFO;
wcsncpy_s(nid.szInfoTitle, title.c_str(), _TRUNCATE); wcsncpy_s(nid.szInfoTitle, title.c_str(), _TRUNCATE);
wcsncpy_s(nid.szInfo, message.c_str(), _TRUNCATE); wcsncpy_s(nid.szInfo, message.c_str(), _TRUNCATE);
nid.dwInfoFlags = NIIF_INFO; // 信息图标,可选 NIIF_WARNING, NIIF_ERROR nid.dwInfoFlags = static_cast<DWORD>(notify); // 信息图标,可选 NIIF_WARNING, NIIF_ERROR
Shell_NotifyIcon(NIM_MODIFY, &nid); Shell_NotifyIcon(NIM_MODIFY, &nid);
} }
#elif __APPLE__ #elif __APPLE__
void TrayIcon::ShowMacNotification(const std::string &title, const std::string &message) void TrayIcon::ShowNotification(const std::string &title, const std::string &message)
{ {
// 通过 AppleScript 或 Objective-C 桥接 // 通过 AppleScript 或 Objective-C 桥接
std::string script = "display notification \"" + message + "\" with title \"" + title + "\""; std::string script = "display notification \"" + message + "\" with title \"" + title + "\"";
@@ -94,7 +94,7 @@ void TrayIcon::ShowMacNotification(const std::string &title, const std::string &
system(cmd.c_str()); system(cmd.c_str());
} }
#else #else
void TrayIcon::ShowLinuxNotification(const std::string &title, const std::string &message) void TrayIcon::ShowNotification(const std::string &title, const std::string &message)
{ {
// 使用 notify-send 命令 // 使用 notify-send 命令
std::string cmd = "notify-send \"" + title + "\" \"" + message + "\""; std::string cmd = "notify-send \"" + title + "\" \"" + message + "\"";
+78 -32
View File
@@ -6,7 +6,7 @@
#include <windows.h> #include <windows.h>
#include <sstream> #include <sstream>
std::wstring StringToWide(const std::string& str) { std::wstring StringToWide(const std::string &str) {
if (str.empty()) return L""; if (str.empty()) return L"";
int size = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, nullptr, 0); int size = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, nullptr, 0);
std::wstring wstr(size, 0); std::wstring wstr(size, 0);
@@ -14,7 +14,7 @@ std::wstring StringToWide(const std::string& str) {
return wstr; return wstr;
} }
std::string WideToString(const std::wstring& wstr) { std::string WideToString(const std::wstring &wstr) {
if (wstr.empty()) return ""; if (wstr.empty()) return "";
int size = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, nullptr, 0, nullptr, nullptr); int size = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, nullptr, 0, nullptr, nullptr);
std::string str(size, 0); std::string str(size, 0);
@@ -44,9 +44,8 @@ void SetAutoStart(bool enable) {
if (enable) { if (enable) {
RegSetValueEx(hKey, keyName, 0, REG_SZ, RegSetValueEx(hKey, keyName, 0, REG_SZ,
(BYTE*)exePath, (wcslen(exePath) + 1) * sizeof(WCHAR)); (BYTE *) exePath, (wcslen(exePath) + 1) * sizeof(WCHAR));
} } else {
else {
RegDeleteValue(hKey, keyName); RegDeleteValue(hKey, keyName);
} }
RegCloseKey(hKey); RegCloseKey(hKey);
@@ -79,6 +78,7 @@ bool IsAutoStartEnabled() {
return exists; return exists;
} }
// 日志颜色处理函数
ImVec4 GetLogLevelColor(const std::string &log) { ImVec4 GetLogLevelColor(const std::string &log) {
// 简单的日志级别颜色区分 // 简单的日志级别颜色区分
if (log.find("错误") != std::string::npos || log.find("[E]") != std::string::npos || if (log.find("错误") != std::string::npos || log.find("[E]") != std::string::npos ||
@@ -90,11 +90,17 @@ ImVec4 GetLogLevelColor(const std::string &log) {
} else if (log.find("信息") != std::string::npos || log.find("[I]") != std::string::npos || } else if (log.find("信息") != std::string::npos || log.find("[I]") != std::string::npos ||
log.find("[INFO]") != std::string::npos || log.find("info") != std::string::npos) { log.find("[INFO]") != std::string::npos || log.find("info") != std::string::npos) {
return ImVec4(0.4f, 1.0f, 0.4f, 1.0f); // 绿色 return ImVec4(0.4f, 1.0f, 0.4f, 1.0f); // 绿色
} else if (log.find("调试") != std::string::npos || log.find("[D]") != std::string::npos ||
log.find("[DEBUG]") != std::string::npos || log.find("debug") != std::string::npos) {
return ImVec4(0.6f, 0.6f, 1.0f, 1.0f); // 蓝色
} else if (log.find("跟踪") != std::string::npos || log.find("[T]") != std::string::npos ||
log.find("[TRACE]") != std::string::npos || log.find("trace") != std::string::npos) {
return ImVec4(0.8f, 0.8f, 0.8f, 1.0f); // 灰色
} }
return ImVec4(1.0f, 1.0f, 1.0f, 1.0f); // 默认白色 return ImVec4(1.0f, 1.0f, 1.0f, 1.0f); // 默认白色
} }
// ANSI颜色处理增强版本
void RenderColoredLogLine(const std::string &log) { void RenderColoredLogLine(const std::string &log) {
auto segments = ParseAnsiColorCodes(log); auto segments = ParseAnsiColorCodes(log);
@@ -197,18 +203,18 @@ ParseAnsiColorCode(const std::string &code, const ImVec4 &currentColor, bool cur
for (int colorCode: codes) { for (int colorCode: codes) {
switch (colorCode) { switch (colorCode) {
case 0: // 重置 case 0: // 重置
newColor = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); newColor = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
newBold = false; newBold = false;
break; break;
case 1: // 粗体/亮色 case 1: // 粗体/亮色
newBold = true; newBold = true;
break; break;
case 22: // 正常强度 case 22: // 正常强度
newBold = false; newBold = false;
break; break;
// 前景色 (30-37) // 前景色 (30-37)
case 30: case 30:
newColor = GetAnsiColor(0, newBold); newColor = GetAnsiColor(0, newBold);
break; // 黑色 break; // 黑色
@@ -237,13 +243,13 @@ ParseAnsiColorCode(const std::string &code, const ImVec4 &currentColor, bool cur
newColor = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); newColor = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
break; // 默认前景色 break; // 默认前景色
// 亮色前景色 (90-97) // 亮色前景色 (90-97)
case 90: case 90:
newColor = GetAnsiColor(8, false); newColor = GetAnsiColor(8, false);
break; // 亮黑色(灰色) break; // 亮黑色(灰色)
case 91: case 91:
newColor = GetAnsiColor(9, false); newColor = GetAnsiColor(9, false);
break; // 亮红色 break; // 亮红色
case 92: case 92:
newColor = GetAnsiColor(10, false); newColor = GetAnsiColor(10, false);
break; // 亮绿色 break; // 亮绿色
@@ -263,10 +269,49 @@ ParseAnsiColorCode(const std::string &code, const ImVec4 &currentColor, bool cur
newColor = GetAnsiColor(15, false); newColor = GetAnsiColor(15, false);
break; // 亮白色 break; // 亮白色
// 背景色暂时忽略 (40-47, 100-107) // 增加对背景色的支持 (40-47, 100-107)
case 40:
case 41:
case 42:
case 43:
case 44:
case 45:
case 46:
case 47:
case 100:
case 101:
case 102:
case 103:
case 104:
case 105:
case 106:
case 107:
// 背景色暂时不处理,因为ImGui不容易实现背景色
break;
default: default:
// 处理256色和RGB色彩(38;5;n 和 38;2;r;g;b) // 处理256色和RGB色彩(38;5;n 和 38;2;r;g;b)
// 这里可以根据需要扩展 if (colorCode == 38 || colorCode == 48) {
// 38=前景,48=背景
// 检查是否是256色模式或RGB模式
if (codes.size() > 1) {
if (codes[1] == 5 && codes.size() > 2) {
// 256色模式
// 暂时不实现所有256色,仅处理基本的16色
int colorIndex = codes[2];
if (colorIndex >= 0 && colorIndex < 16) {
newColor = GetAnsiColor(colorIndex, newBold);
}
} else if (codes[1] == 2 && codes.size() > 4) {
// RGB模式
// 处理RGB值
float r = static_cast<float>(codes[2]) / 255.0f;
float g = static_cast<float>(codes[3]) / 255.0f;
float b = static_cast<float>(codes[4]) / 255.0f;
newColor = ImVec4(r, g, b, 1.0f);
}
}
}
break; break;
} }
} }
@@ -277,25 +322,25 @@ ParseAnsiColorCode(const std::string &code, const ImVec4 &currentColor, bool cur
ImVec4 GetAnsiColor(int colorIndex, bool bright) { ImVec4 GetAnsiColor(int colorIndex, bool bright) {
// ANSI标准颜色表 // ANSI标准颜色表
static const ImVec4 ansiColors[16] = { static const ImVec4 ansiColors[16] = {
// 标准颜色 (0-7) // 标准颜色 (0-7)
ImVec4(0.0f, 0.0f, 0.0f, 1.0f), // 0: 黑色 ImVec4(0.0f, 0.0f, 0.0f, 1.0f), // 0: 黑色
ImVec4(0.8f, 0.0f, 0.0f, 1.0f), // 1: 红色 ImVec4(0.8f, 0.0f, 0.0f, 1.0f), // 1: 红色
ImVec4(0.0f, 0.8f, 0.0f, 1.0f), // 2: 绿色 ImVec4(0.0f, 0.8f, 0.0f, 1.0f), // 2: 绿色
ImVec4(0.8f, 0.8f, 0.0f, 1.0f), // 3: 黄色 ImVec4(0.8f, 0.8f, 0.0f, 1.0f), // 3: 黄色
ImVec4(0.0f, 0.0f, 0.8f, 1.0f), // 4: 蓝色 ImVec4(0.0f, 0.0f, 0.8f, 1.0f), // 4: 蓝色
ImVec4(0.8f, 0.0f, 0.8f, 1.0f), // 5: 洋红 ImVec4(0.8f, 0.0f, 0.8f, 1.0f), // 5: 洋红
ImVec4(0.0f, 0.8f, 0.8f, 1.0f), // 6: 青色 ImVec4(0.0f, 0.8f, 0.8f, 1.0f), // 6: 青色
ImVec4(0.8f, 0.8f, 0.8f, 1.0f), // 7: 白色 ImVec4(0.8f, 0.8f, 0.8f, 1.0f), // 7: 白色
// 亮色 (8-15) // 亮色 (8-15)
ImVec4(0.5f, 0.5f, 0.5f, 1.0f), // 8: 亮黑色(灰色) ImVec4(0.5f, 0.5f, 0.5f, 1.0f), // 8: 亮黑色(灰色)
ImVec4(1.0f, 0.0f, 0.0f, 1.0f), // 9: 亮红色 ImVec4(1.0f, 0.0f, 0.0f, 1.0f), // 9: 亮红色
ImVec4(0.0f, 1.0f, 0.0f, 1.0f), // 10: 亮绿色 ImVec4(0.0f, 1.0f, 0.0f, 1.0f), // 10: 亮绿色
ImVec4(1.0f, 1.0f, 0.0f, 1.0f), // 11: 亮黄色 ImVec4(1.0f, 1.0f, 0.0f, 1.0f), // 11: 亮黄色
ImVec4(0.0f, 0.0f, 1.0f, 1.0f), // 12: 亮蓝色 ImVec4(0.0f, 0.0f, 1.0f, 1.0f), // 12: 亮蓝色
ImVec4(1.0f, 0.0f, 1.0f, 1.0f), // 13: 亮洋红 ImVec4(1.0f, 0.0f, 1.0f, 1.0f), // 13: 亮洋红
ImVec4(0.0f, 1.0f, 1.0f, 1.0f), // 14: 亮青色 ImVec4(0.0f, 1.0f, 1.0f, 1.0f), // 14: 亮青色
ImVec4(1.0f, 1.0f, 1.0f, 1.0f), // 15: 亮白色 ImVec4(1.0f, 1.0f, 1.0f, 1.0f), // 15: 亮白色
}; };
if (colorIndex >= 0 && colorIndex < 16) { if (colorIndex >= 0 && colorIndex < 16) {
@@ -313,3 +358,4 @@ ImVec4 GetAnsiColor(int colorIndex, bool bright) {
return ImVec4(1.0f, 1.0f, 1.0f, 1.0f); // 默认白色 return ImVec4(1.0f, 1.0f, 1.0f, 1.0f); // 默认白色
} }
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

+2
View File
@@ -0,0 +1,2 @@
#include "app/inc/resource.h"
IDI_ICON1 ICON DISCARDABLE "logo.ico"
+81 -2
View File
@@ -1,6 +1,43 @@
# 通用Cli Manager工具 # 通用CLI管理工具
基于IMGUI实现的支持开机自启动、环境变量、自动编码识别转换托盘持久化 一个基于IMGUI实现的CLI管理工具,具备开机自启动、环境变量管理、自动编码识别转换托盘持久化等功能。
## 主要功能
### 命令管理
- **命令历史记录**:自动记录执行过的命令,支持命令去重和历史记录数量限制
- **命令执行**:便捷地向CLI发送命令并获取执行结果
### 环境变量管理
- 查看当前环境变量
- 添加/删除/清空环境变量
- 为每个CLI进程设置独立的环境变量
### 编码支持
- 自动识别命令输出编码
- 支持多种编码格式转换
- UTF-8编码检测和转换
### 工作目录管理
- 获取和设置工作目录
- 支持从命令中提取目录路径
- 路径有效性验证
### 系统集成
- 系统托盘集成,支持最小化到托盘
- 开机自启动选项
- 剪贴板集成,支持日志复制
### 其他特性
- 多线程安全的数据访问
- Web服务快捷打开
- 直观的图形界面
## 程序效果 ## 程序效果
@@ -9,3 +46,45 @@
![主界面](./img/img2.png) ![主界面](./img/img2.png)
![托盘显示](./img/img3.png) ![托盘显示](./img/img3.png)
## 技术实现
- 基于IMGUI构建用户界面
- 使用C++标准库实现核心功能
- 在Windows平台上提供完整的进程管理
- 线程安全设计,适用于多线程环境
## 系统要求
- Windows操作系统
- 支持现代C++编译器
## 使用指南
### 命令执行
1. 在命令输入框中输入需要执行的命令
2. 点击执行按钮或按Enter键发送命令
3. 命令输出将实时显示在日志区域
### 环境变量配置
1. 在环境变量面板中查看当前变量
2. 使用添加/删除按钮管理环境变量
3. 所有修改将即时应用到当前CLI进程
### 工作目录设置
1. 使用工作目录下拉框选择或手动输入目录路径
2. 系统会自动验证目录有效性
3. 可从命令中自动提取目录路径
### 托盘功能
1. 最小化应用时将自动缩小到系统托盘
2. 右键托盘图标可访问快捷菜单
3. 支持通过托盘快速恢复窗口或退出应用
## 开发者信息
本项目是一个开源工具,欢迎贡献代码或提出改进建议。