feat(ui): 添加深色模式支持

- 新增深色/浅色模式切换按钮,位于主窗口底部
- 在配置文件中定义主题颜色(浅色/深色背景、列表背景、前景色)
- 更新 UI 工具函数以支持动态主题切换,包括列表斑马纹适配
- 添加翻译条目(Dark Mode/Light Mode)并更新编译脚本
- 修改主窗口创建逻辑,集成主题切换回调
- 调整列表背景色属性从 BACKCOLOR 改为 BGCOLOR 以保持一致性
This commit is contained in:
2026-05-02 01:32:56 +08:00
parent 3df2988915
commit 720ebb535d
14 changed files with 233 additions and 27 deletions
+33
View File
@@ -226,3 +226,36 @@ int dlg_k_any_cb(Ihandle *self, int c)
}
return IUP_DEFAULT;
}
// 深色模式切换回调
int darkmode_cb(Ihandle *self)
{
Ihandle *dlg = IupGetDialog(self);
int dark = !get_dark_mode();
set_dark_mode(dark);
IupSetAttribute(dlg, "BGCOLOR", dark
? lua_config_get_string("theme", "dark_bg")
: lua_config_get_string("theme", "light_bg"));
IupSetAttribute(self, "TITLE", _(dark
? lua_config_get_string("button", "lightmode")
: lua_config_get_string("button", "darkmode")));
Ihandle *lists[] = {
IupGetDialogChild(dlg, CTRL_LIST_SYS),
IupGetDialogChild(dlg, CTRL_LIST_USER),
IupGetDialogChild(dlg, CTRL_LIST_MERGED),
NULL
};
for (int i = 0; lists[i]; i++)
{
const char *list_bg = dark
? lua_config_get_string("theme", "dark_list_bg")
: lua_config_get_string("theme", "light_list_bg");
IupSetAttribute(lists[i], "BGCOLOR", list_bg);
refresh_single_list_style(lists[i]);
}
return IUP_DEFAULT;
}
+29
View File
@@ -63,6 +63,14 @@ static const char *get_string_default(const char *section, const char *key)
return "取消";
if (strcmp(key, "help") == 0)
return "帮助(?)";
if (strcmp(key, "undo") == 0)
return "撤销";
if (strcmp(key, "redo") == 0)
return "重做";
if (strcmp(key, "darkmode") == 0)
return "深色模式";
if (strcmp(key, "lightmode") == 0)
return "浅色模式";
}
else if (strcmp(section, "label") == 0)
{
@@ -74,11 +82,32 @@ static const char *get_string_default(const char *section, const char *key)
return "系统变量 (System)";
if (strcmp(key, "tab_user") == 0)
return "用户变量 (User)";
if (strcmp(key, "tab_merged") == 0)
return "合并预览";
if (strcmp(key, "export_title") == 0)
return "导出 PATH";
if (strcmp(key, "import_title") == 0)
return "导入 PATH";
}
else if (strcmp(section, "theme") == 0)
{
if (strcmp(key, "light_bg") == 0)
return "240 240 240";
if (strcmp(key, "light_list_bg") == 0)
return "255 255 255";
if (strcmp(key, "light_list_alt") == 0)
return "245 245 245";
if (strcmp(key, "light_fg") == 0)
return "0 0 0";
if (strcmp(key, "dark_bg") == 0)
return "30 30 30";
if (strcmp(key, "dark_list_bg") == 0)
return "40 40 40";
if (strcmp(key, "dark_list_alt") == 0)
return "50 50 50";
if (strcmp(key, "dark_fg") == 0)
return "220 220 220";
}
else if (strcmp(section, "layout") == 0)
{
if (strcmp(key, "vbox_gap") == 0)
+18 -4
View File
@@ -1,4 +1,5 @@
#include "ui/main_window.h"
#include "ui/ui_utils.h"
#include "controller/callbacks.h"
#include "controller/callbacks_internal.h"
#include "core/lua_config.h"
@@ -14,7 +15,7 @@ static Ihandle *create_path_list(const char *name)
IupSetAttribute(list, "NAME", name);
IupSetAttribute(list, "EXPAND", "YES");
IupSetAttribute(list, "ITEMPADDING", lua_config_get_string("list", "item_padding"));
IupSetAttribute(list, "BACKCOLOR", lua_config_get_string("list", "backcolor"));
IupSetAttribute(list, "BGCOLOR", lua_config_get_string("list", "backcolor"));
IupSetAttribute(list, "BORDER", "YES");
IupSetAttribute(list, "CANFOCUS", "YES");
IupSetAttribute(list, "HLINE", "NO");
@@ -47,10 +48,10 @@ Ihandle *create_main_window(void)
IupSetAttribute(list_merged, "NAME", CTRL_LIST_MERGED);
IupSetAttribute(list_merged, "EXPAND", "YES");
IupSetAttribute(list_merged, "ITEMPADDING", lua_config_get_string("list", "item_padding"));
IupSetAttribute(list_merged, "BACKCOLOR", lua_config_get_string("list", "backcolor"));
IupSetAttribute(list_merged, "BGCOLOR", lua_config_get_string("list", "backcolor"));
IupSetAttribute(list_merged, "BORDER", "YES");
IupSetAttribute(list_merged, "HLINE", "NO");
IupSetAttribute(list_merged, "ACTIVE", "NO"); // 只读
// 不设置 ACTIVE=NO,否则会禁用滚动;合并列表无编辑回调,已是只读
// 创建搜索框
Ihandle *txt_search = IupText(NULL);
@@ -113,6 +114,11 @@ Ihandle *create_main_window(void)
IupSetAttribute(btn_lang, "NAME", CTRL_BTN_LANG);
IupSetCallback(btn_lang, "ACTION", (Icallback)btn_lang_cb);
// 创建深色模式切换按钮
Ihandle *btn_darkmode = IupButton(_(lua_config_get_string("button", "darkmode")), NULL);
IupSetAttribute(btn_darkmode, "NAME", CTRL_BTN_DARKMODE);
IupSetCallback(btn_darkmode, "ACTION", (Icallback)darkmode_cb);
// 设置按钮回调
IupSetCallback(btn_new, "ACTION", (Icallback)btn_new_cb);
IupSetCallback(btn_edit, "ACTION", (Icallback)btn_edit_cb);
@@ -140,6 +146,7 @@ Ihandle *create_main_window(void)
IupSetAttribute(btn_undo, "RASTERSIZE", btn_size);
IupSetAttribute(btn_redo, "RASTERSIZE", btn_size);
IupSetAttribute(btn_lang, "RASTERSIZE", btn_size);
IupSetAttribute(btn_darkmode, "RASTERSIZE", btn_size);
// 创建操作按钮垂直布局
Ihandle *vbox_btns = IupVbox(
@@ -183,7 +190,7 @@ Ihandle *create_main_window(void)
IupSetAttribute(btn_help, "RASTERSIZE", btn_size);
// 创建底部按钮水平布局
Ihandle *hbox_bottom = IupHbox(lbl_status, IupFill(), btn_help, btn_lang, btn_ok, btn_cancel, NULL);
Ihandle *hbox_bottom = IupHbox(lbl_status, IupFill(), btn_help, btn_darkmode, btn_lang, btn_ok, btn_cancel, NULL);
IupSetAttribute(hbox_bottom, "GAP", lua_config_get_string("layout", "hbox_gap"));
IupSetAttribute(hbox_bottom, "MARGIN", lua_config_get_string("layout", "hbox_margin"));
IupSetAttribute(hbox_bottom, "ALIGNMENT", lua_config_get_string("layout", "hbox_alignment"));
@@ -255,4 +262,11 @@ void refresh_main_window_ui(Ihandle *main_dlg)
Ihandle *btn_lang = IupGetDialogChild(main_dlg, CTRL_BTN_LANG);
if (btn_lang)
IupSetAttribute(btn_lang, "TITLE", _("Language"));
// 深色模式按钮文字根据当前模式更新
Ihandle *btn_darkmode = IupGetDialogChild(main_dlg, CTRL_BTN_DARKMODE);
if (btn_darkmode)
IupSetAttribute(btn_darkmode, "TITLE", get_dark_mode()
? _(lua_config_get_string("button", "lightmode"))
: _(lua_config_get_string("button", "darkmode")));
}
+40 -21
View File
@@ -1,10 +1,23 @@
#include "ui/ui_utils.h"
#include "utils/os_env.h"
#include "utils/string_ext.h"
#include "core/lua_config.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
static int g_dark_mode = 0;
void set_dark_mode(int enabled)
{
g_dark_mode = enabled;
}
int get_dark_mode(void)
{
return g_dark_mode;
}
// 刷新列表样式(斑马纹 + 有效性检查)
void refresh_single_list_style(Ihandle *list)
{
@@ -12,30 +25,42 @@ void refresh_single_list_style(Ihandle *list)
return;
int count = IupGetInt(list, "COUNT");
// 读取主题颜色,带默认值保护
const char *alt_color = g_dark_mode
? lua_config_get_string("theme", "dark_list_alt")
: lua_config_get_string("theme", "light_list_alt");
const char *bg_color = g_dark_mode
? lua_config_get_string("theme", "dark_list_bg")
: lua_config_get_string("theme", "light_list_bg");
const char *fg_default = g_dark_mode
? lua_config_get_string("theme", "dark_fg")
: lua_config_get_string("theme", "light_fg");
// 防止 NULL 或空字符串,使用硬编码默认值
if (!alt_color || !*alt_color) alt_color = g_dark_mode ? "50 50 50" : "245 245 245";
if (!bg_color || !*bg_color) bg_color = g_dark_mode ? "40 40 40" : "255 255 255";
if (!fg_default || !*fg_default) fg_default = g_dark_mode ? "220 220 220" : "0 0 0";
for (int i = 1; i <= count; i++)
{
char *item = IupGetAttributeId(list, "", i);
if (!item)
continue;
// 默认颜色:黑字
char fg_color[32] = "0 0 0";
char fg_color[32];
sprintf(fg_color, "%s", fg_default);
// 1. 检查有效性
if (!is_path_valid(item))
{
// 无效路径:红色
sprintf(fg_color, "255 0 0");
}
sprintf(fg_color, "255 0 0"); // 无效路径:红色
// 2. 检查重复 (只检查当前项之前的项,如果之前出现过,当前项标橙)
// 2. 检查重复
for (int j = 1; j < i; j++)
{
char *prev_item = IupGetAttributeId(list, "", j);
if (prev_item && _stricmp(item, prev_item) == 0) // Windows 路径不区分大小写
if (prev_item && _stricmp(item, prev_item) == 0)
{
// 重复路径:橙色
sprintf(fg_color, "255 128 0");
sprintf(fg_color, "255 128 0"); // 重复路径:橙色
break;
}
}
@@ -43,14 +68,8 @@ void refresh_single_list_style(Ihandle *list)
IupSetAttributeId(list, "ITEMFGCOLOR", i, fg_color);
// 斑马纹背景
if (i % 2 == 0)
{
IupSetAttributeId(list, "ITEMBGCOLOR", i, "245 245 245");
}
else
{
IupSetAttributeId(list, "ITEMBGCOLOR", i, "255 255 255");
}
IupSetAttributeId(list, "ITEMBGCOLOR", i,
(i % 2 == 0) ? alt_color : bg_color);
}
}
@@ -60,7 +79,7 @@ void sync_string_list_to_ui(Ihandle *list_ui, const StringList *str_list)
if (!list_ui || !str_list) return;
IupSetAttribute(list_ui, "REMOVEITEM", "ALL");
for (int i = 0; i < str_list->count; i++)
{
const char *item = string_list_get(str_list, i);
@@ -78,6 +97,6 @@ void sync_string_list_to_ui(Ihandle *list_ui, const StringList *str_list)
}
}
IupSetInt(list_ui, "COUNT", str_list->count);
refresh_single_list_style(list_ui);
}
}