From 720ebb535da200906bff79bb2f802d869898b194 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E8=88=AA=E5=AE=87?= <3364451258@qq.com> Date: Sat, 2 May 2026 01:32:56 +0800 Subject: [PATCH] =?UTF-8?q?feat(ui):=20=E6=B7=BB=E5=8A=A0=E6=B7=B1?= =?UTF-8?q?=E8=89=B2=E6=A8=A1=E5=BC=8F=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增深色/浅色模式切换按钮,位于主窗口底部 - 在配置文件中定义主题颜色(浅色/深色背景、列表背景、前景色) - 更新 UI 工具函数以支持动态主题切换,包括列表斑马纹适配 - 添加翻译条目(Dark Mode/Light Mode)并更新编译脚本 - 修改主窗口创建逻辑,集成主题切换回调 - 调整列表背景色属性从 BACKCOLOR 改为 BGCOLOR 以保持一致性 --- include/controller/callbacks.h | 1 + include/ui/ui_utils.h | 4 ++ include/utils/ui_constants.h | 1 + locale/en_US/LC_MESSAGES/en_US.mo | Bin 6404 -> 6889 bytes locale/zh_CN/LC_MESSAGES/zh_CN.mo | Bin 6200 -> 6703 bytes lua/config.lua | 14 +++++++ po/compile_mo.py | 67 ++++++++++++++++++++++++++++++ po/en_US.po | 10 ++++- po/messages.pot | 8 ++++ po/zh_CN.po | 10 ++++- src/controller/callbacks_sys.c | 33 +++++++++++++++ src/core/lua_config.c | 29 +++++++++++++ src/ui/main_window.c | 22 ++++++++-- src/ui/ui_utils.c | 61 +++++++++++++++++---------- 14 files changed, 233 insertions(+), 27 deletions(-) create mode 100644 po/compile_mo.py diff --git a/include/controller/callbacks.h b/include/controller/callbacks.h index 1205c2b..38530ba 100644 --- a/include/controller/callbacks.h +++ b/include/controller/callbacks.h @@ -17,6 +17,7 @@ int btn_ok_cb(Ihandle *self); int btn_cancel_cb(Ihandle *self); int btn_help_cb(Ihandle *self); int btn_lang_cb(Ihandle *self); +int darkmode_cb(Ihandle *self); // 撤销/重做回调 int btn_undo_cb(Ihandle *self); diff --git a/include/ui/ui_utils.h b/include/ui/ui_utils.h index 9b57c4e..eec015c 100644 --- a/include/ui/ui_utils.h +++ b/include/ui/ui_utils.h @@ -17,4 +17,8 @@ void refresh_single_list_style(Ihandle *list); // 会先清空列表然后重新添加所有项,最后刷新样式 void sync_string_list_to_ui(Ihandle *list_ui, const StringList *str_list); +// 深色模式状态管理 +void set_dark_mode(int enabled); +int get_dark_mode(void); + #endif // UI_UTILS_H diff --git a/include/utils/ui_constants.h b/include/utils/ui_constants.h index b052aa8..bae36bc 100644 --- a/include/utils/ui_constants.h +++ b/include/utils/ui_constants.h @@ -35,6 +35,7 @@ #define CTRL_BTN_CANCEL "BTN_CANCEL" #define CTRL_BTN_HELP "BTN_HELP" #define CTRL_BTN_LANG "BTN_LANG" +#define CTRL_BTN_DARKMODE "BTN_DARKMODE" // 撤销/重做按钮 #define CTRL_BTN_UNDO "BTN_UNDO" diff --git a/locale/en_US/LC_MESSAGES/en_US.mo b/locale/en_US/LC_MESSAGES/en_US.mo index 439c72f81efb56c158d9bae3bee55b2689b8fb24..b4a29a473867bd90c15e0ee6ae3614277871f884 100644 GIT binary patch literal 6889 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 F{Tr-?x2pgE literal 6404 zcmeH~Ux-~t9mfaV+8C?-6KyqCovWm_CcT@cNtB!7y4hW`vCSWLlO`glv-ixs_jJ!a z=jQy`%@x$P7O7Yi1dH^ct%wh$BBG+AB9cB-P$WcOw9xuk#6HxbDD8uv@AE%pV-CWP!4Jaw;rrl^;Vtlw z@ET~YHs(5T4V-~DLw?Ptc$^RkjzzYyj<~6ttzK)0FZLhf#UQhc2 zkRj#}d^bD{TkzBHD)<203BL&61fPW3*VFJF@L9;O`BVMZ9L+SG{)Oufqn*UhMr{E#lKY`N!mHPX{_R6k1p!7Zp=`zQm=AVK5 znjR0WGlH`7c{l+dg4)l+@V)Sx@Bn-U$}azaXW@+~C6~rf^Up!q|Fckb`5KfTzYE_5 ze-EY4%TRj!9m<|pG5H4g9*Agj2b8=+HIKl(v=^cD{Q|V`%TW7%8r}$h4(H(|C_Q$* z8JTbrD$b6>DfkflBzy)+pBBL)eQtpoe*nrary!zCzwXcATWLQCwa%kZt(2T z{3nzhcOz7OorE{RDM**O7rqk)Q2Kwq{;oWbo?n5=hgYHU;WR-YJ3bDj&qXM?e}T$} zDHi9|dL(D}ec`w%7h7+XMZBTNjq5Q6RQ9gVd z%5J}g^535!t~A&3klznN#m_R7{735V--BBB5|rE*AX_(otGR>C$({$G^f?RL@KGo^ zzk!rjA=JDFAX_kxL9O#0h-mXm zC_i3=+RsZ+`EV~aYzALdb7`S2~M`QL}K|L@>kZ~~>|#~xHZJOxWTLh11W zls&hR49bW7Jfzo2D0#h_pM!g8{}f8!zd{RlVmw5cyP@(Sh4b(tlpePeyozrJWse_0 z<-=||l@Gsy(q}i5rOy=9_#s4uc>+q0U)247fXav0pw>A{kZarzp!E7J+y}S4y|Uwn zA*L`3kS;TZnAAKDl@HHC>Hi9x4u)YIW_jxJB)zg_M`^ecM!xSe>r!u1f365qAJ{yx z;c%3sxji|3YQFW~&&u42x7~q@5y>=Uj+4S>Mg6ks;#|VJNt}mq;ivxnkZd^|*(KLq zE=G(h@^<`zH;2n_cG2gE=x63|k`HV)&T>DrE)Hyw`LtBq`gkQwlX&P^bj78i zTZ-7|Kq@YIAKPLSxZDRVb2v>_Ghd^F=*V|-8(@kqHXGYsnhb3ZLpAHL1s4QuGvnf} zkIc*Xfe?dn^gU z-gx6Ob3+$K9ed}U2lwwkaL2v_ANt6-b8ReQ=Hh-7W&?QHV~udDwTaxhG}ZIG!m@^4 zEV^BvWlgh(c=@v$MA((Ry&`IvkCt7EJ%wn?90~LJVyR;Xc|OWIlau{09~4VcW|AP= zi*l1E*-yRpk;E$O%+WF_YS`&0YFEZ@}pnd z9F2R4xrg*DT;H2x9)}0^Wa?K!ziN&Vj&?R#jjOlQBXf*svS|@pmzm>?;BWH)prd1_ zgTTg&P$0}nWAN?9BDo-3lfPWd`6nN+4vl?w!Dfygi} zjn+bB?#b3wI0RY|D%RkYeBdr_q- z&1)a*3G{BhuB?@1rDd=#Wux&PyHw;=IlnxtN+oHg32d*DzBYi}2jyPGeOL5EMtT_&-iaeP8GG*e-jFP!|ogn<#gvx<2A0op{k>UA&wgO)8m{m-i`; z%N=v#Cn>&VN=6Cbd}5`dTU9R#i=D0VkZz)V&1oB&l@H4yYnCq^vY7|74KAw7;bk9Q zDW~+{8J8-b&6%W#g7OX#h0CS&y5#|<3|955gLmzyo-+8*Y&b(rd46knx1hRm8>mWB zbLz@4nW;mnxxJLZ;6|eeQ*G#KUwI$8vbA-m+PYH_Pg{4Ytvl7$ovJzA|K~fE4hyb2 j`k!L6GdA0Bo7%c_mH!T}9p77buB|(l{x$x8zH|KxSdlIm diff --git a/locale/zh_CN/LC_MESSAGES/zh_CN.mo b/locale/zh_CN/LC_MESSAGES/zh_CN.mo index 375f38beab177330a248561aa0dde134e72efd9e..2048a6a15b3471f27ebac77ec885c22d2522a6e8 100644 GIT binary patch literal 6703 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 TqV8#yRJtr*<*)dJplAOBk3ELw diff --git a/lua/config.lua b/lua/config.lua index 5d7ca2b..374ee5f 100644 --- a/lua/config.lua +++ b/lua/config.lua @@ -26,6 +26,18 @@ local config = { backcolor = "255 255 255" }, + -- 主题颜色 + theme = { + light_bg = "240 240 240", + light_list_bg = "255 255 255", + light_list_alt = "245 245 245", + light_fg = "0 0 0", + dark_bg = "30 30 30", + dark_list_bg = "40 40 40", + dark_list_alt = "50 50 50", + dark_fg = "220 220 220", + }, + -- 按钮设置(使用英文原文,供 gettext 翻译) button = { rastersize = "100x32", @@ -43,6 +55,8 @@ local config = { help = "Help", undo = "Undo", redo = "Redo", + darkmode = "Dark Mode", + lightmode = "Light Mode", }, -- 标签文本(使用英文原文,供 gettext 翻译) diff --git a/po/compile_mo.py b/po/compile_mo.py new file mode 100644 index 0000000..15ef553 --- /dev/null +++ b/po/compile_mo.py @@ -0,0 +1,67 @@ +import struct, re, os + +def compile_po_to_mo(po_path, mo_path): + with open(po_path, 'r', encoding='utf-8') as f: + content = f.read() + + entries = [] + for match in re.finditer( + r'msgid "(.+?)"\s*\nmsgstr "(.*?)"', + content, re.DOTALL + ): + msgid = match.group(1) + msgstr = match.group(2) + if msgid: + entries.append((msgid, msgstr)) + + if not entries: + print(f'No entries in {po_path}') + return + + N = len(entries) + header_sz = 28 + table_sz = N * 8 * 2 + + orig_off = header_sz + table_sz + orig_data = b'' + orig_offsets = [] + for eid, estr in entries: + orig_offsets.append(len(orig_data)) + orig_data += eid.encode('utf-8') + b'\x00' + + trans_off = orig_off + len(orig_data) + trans_data = b'' + trans_offsets = [] + for eid, estr in entries: + trans_offsets.append(len(trans_data)) + trans_data += estr.encode('utf-8') + b'\x00' + + buf = bytearray() + buf += struct.pack(' {mo_path} ({N} strings)') + +base = os.path.dirname(os.path.abspath(__file__)) +root = os.path.dirname(base) + +compile_po_to_mo(os.path.join(root, 'po', 'zh_CN.po'), os.path.join(root, 'locale', 'zh_CN', 'LC_MESSAGES', 'zh_CN.mo')) +compile_po_to_mo(os.path.join(root, 'po', 'en_US.po'), os.path.join(root, 'locale', 'en_US', 'LC_MESSAGES', 'en_US.mo')) +compile_po_to_mo(os.path.join(root, 'po', 'zh_CN.po'), os.path.join(root, 'build', 'locale', 'zh_CN', 'LC_MESSAGES', 'zh_CN.mo')) +compile_po_to_mo(os.path.join(root, 'po', 'en_US.po'), os.path.join(root, 'build', 'locale', 'en_US', 'LC_MESSAGES', 'en_US.mo')) diff --git a/po/en_US.po b/po/en_US.po index 7bb3616..89359c6 100644 --- a/po/en_US.po +++ b/po/en_US.po @@ -406,4 +406,12 @@ msgstr "Please select an item to delete first" #: src/controller/callbacks_basic.c msgid "This path already exists and will not be added again." -msgstr "This path already exists and will not be added again." \ No newline at end of file +msgstr "This path already exists and will not be added again." + +#: src/ui/main_window.c +msgid "Dark Mode" +msgstr "Dark Mode" + +#: src/ui/main_window.c +msgid "Light Mode" +msgstr "Light Mode" \ No newline at end of file diff --git a/po/messages.pot b/po/messages.pot index 69dbf35..82bb00b 100644 --- a/po/messages.pot +++ b/po/messages.pot @@ -124,3 +124,11 @@ msgstr "" #: src/controller/callbacks_nav.c msgid "Redo completed" msgstr "" + +#: src/ui/main_window.c +msgid "Dark Mode" +msgstr "" + +#: src/ui/main_window.c +msgid "Light Mode" +msgstr "" diff --git a/po/zh_CN.po b/po/zh_CN.po index 8412ab6..5c92c63 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -406,4 +406,12 @@ msgstr "请先选择要删除的项" #: src/controller/callbacks_basic.c msgid "This path already exists and will not be added again." -msgstr "该路径已存在,不会重复添加。" \ No newline at end of file +msgstr "该路径已存在,不会重复添加。" + +#: src/ui/main_window.c +msgid "Dark Mode" +msgstr "深色模式" + +#: src/ui/main_window.c +msgid "Light Mode" +msgstr "浅色模式" \ No newline at end of file diff --git a/src/controller/callbacks_sys.c b/src/controller/callbacks_sys.c index e13ba7f..a090e01 100644 --- a/src/controller/callbacks_sys.c +++ b/src/controller/callbacks_sys.c @@ -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; +} diff --git a/src/core/lua_config.c b/src/core/lua_config.c index 4efddc9..88b8403 100644 --- a/src/core/lua_config.c +++ b/src/core/lua_config.c @@ -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) diff --git a/src/ui/main_window.c b/src/ui/main_window.c index f710758..eb8b6e2 100644 --- a/src/ui/main_window.c +++ b/src/ui/main_window.c @@ -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"))); } \ No newline at end of file diff --git a/src/ui/ui_utils.c b/src/ui/ui_utils.c index 91a13c9..f17eae4 100644 --- a/src/ui/ui_utils.c +++ b/src/ui/ui_utils.c @@ -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 #include #include +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); -} \ No newline at end of file +}