diff options
| author | salmonik <salmonik@tuta.io> | 2026-11-04 18:15:18 +0100 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-11-04 18:15:18 +0100 |
| commit | 83b36aaaf146325af2d93dfbaf21eccba91a5d74 (patch) | |
| tree | 2f6cd5a1d38231df4a6650e223681863f651e1df /src/gui.cpp | |
| parent | Chams: material styles and viewmodel support (diff) | |
| download | hl2h-83b36aaaf146325af2d93dfbaf21eccba91a5d74.tar.gz hl2h-83b36aaaf146325af2d93dfbaf21eccba91a5d74.zip | |
Chams: exact material styles, GUI dropdown, viewmodel hookup
- world_find_material now tries the exact requested style first (resident,
then FindMaterial) before falling back to debugwhite/vgui white. The old
candidate scan returned vgui/white for every style, so all materials looked
flat.
- Add a real CK_DROPDOWN control (overlay list, outside-click close, config
save) and use it for the Chams material selector.
- Chams viewmodel toggle included in config/UI.
Diffstat (limited to '')
| -rw-r--r-- | src/gui.cpp | 136 |
1 files changed, 130 insertions, 6 deletions
diff --git a/src/gui.cpp b/src/gui.cpp index 4a949f0..4adc440 100644 --- a/src/gui.cpp +++ b/src/gui.cpp | |||
| @@ -183,7 +183,8 @@ enum CtrlKind { | |||
| 183 | CK_SECTION, | 183 | CK_SECTION, |
| 184 | CK_INFO, | 184 | CK_INFO, |
| 185 | CK_BUTTON, | 185 | CK_BUTTON, |
| 186 | CK_COLOR | 186 | CK_COLOR, |
| 187 | CK_DROPDOWN | ||
| 187 | }; | 188 | }; |
| 188 | 189 | ||
| 189 | enum CtrlAction { ACT_NONE = 0, ACT_RELOAD, ACT_RESET, ACT_SKY_RELOAD }; | 190 | enum CtrlAction { ACT_NONE = 0, ACT_RELOAD, ACT_RESET, ACT_SKY_RELOAD }; |
| @@ -195,6 +196,8 @@ struct Ctrl { | |||
| 195 | bool *bval; // CK_TOGGLE | 196 | bool *bval; // CK_TOGGLE |
| 196 | int *ival; // CK_SLIDER | 197 | int *ival; // CK_SLIDER |
| 197 | int *rgb; // CK_COLOR: three ints, 0..255 | 198 | int *rgb; // CK_COLOR: three ints, 0..255 |
| 199 | const char *const *opts; // CK_DROPDOWN | ||
| 200 | int nopts; // CK_DROPDOWN | ||
| 198 | int min, max; | 201 | int min, max; |
| 199 | int action; // CK_BUTTON | 202 | int action; // CK_BUTTON |
| 200 | bool zero_unlimited; // slider: value 0 displays as "All" | 203 | bool zero_unlimited; // slider: value 0 displays as "All" |
| @@ -253,6 +256,8 @@ static void add_ctrl(int card, int kind, const char *label, const char *desc, | |||
| 253 | c.action = action; | 256 | c.action = action; |
| 254 | c.zero_unlimited = false; | 257 | c.zero_unlimited = false; |
| 255 | c.rgb = nullptr; | 258 | c.rgb = nullptr; |
| 259 | c.opts = nullptr; | ||
| 260 | c.nopts = 0; | ||
| 256 | c.hue = 0.0f; | 261 | c.hue = 0.0f; |
| 257 | c.sat = 0.0f; | 262 | c.sat = 0.0f; |
| 258 | c.val = 1.0f; | 263 | c.val = 1.0f; |
| @@ -282,10 +287,20 @@ static void add_color(int card, const char *label, const char *desc, | |||
| 282 | add_ctrl(card, CK_COLOR, label, desc, 0, 255, nullptr, nullptr, ACT_NONE); | 287 | add_ctrl(card, CK_COLOR, label, desc, 0, 255, nullptr, nullptr, ACT_NONE); |
| 283 | g_ctrls[i].rgb = rgb; | 288 | g_ctrls[i].rgb = rgb; |
| 284 | } | 289 | } |
| 290 | static void add_dropdown(int card, const char *label, const char *desc, | ||
| 291 | const char *const *opts, int nopts, int *value) { | ||
| 292 | int i = g_nctrl; | ||
| 293 | add_ctrl(card, CK_DROPDOWN, label, desc, 0, nopts > 0 ? nopts - 1 : 0, | ||
| 294 | nullptr, value, ACT_NONE); | ||
| 295 | g_ctrls[i].opts = opts; | ||
| 296 | g_ctrls[i].nopts = nopts; | ||
| 297 | } | ||
| 285 | 298 | ||
| 286 | static int ctrl_val(const Ctrl &c) { | 299 | static int ctrl_val(const Ctrl &c) { |
| 287 | if (c.kind == CK_TOGGLE && c.bval) | 300 | if (c.kind == CK_TOGGLE && c.bval) |
| 288 | return *c.bval ? 1 : 0; | 301 | return *c.bval ? 1 : 0; |
| 302 | if (c.kind == CK_DROPDOWN) | ||
| 303 | return c.ival ? *c.ival : 0; | ||
| 289 | if (c.kind == CK_SLIDER && c.ival) | 304 | if (c.kind == CK_SLIDER && c.ival) |
| 290 | return *c.ival; | 305 | return *c.ival; |
| 291 | return 0; | 306 | return 0; |
| @@ -294,6 +309,13 @@ static void ctrl_set(Ctrl &c, int v) { | |||
| 294 | if (c.kind == CK_TOGGLE && c.bval) { | 309 | if (c.kind == CK_TOGGLE && c.bval) { |
| 295 | *c.bval = v != 0; | 310 | *c.bval = v != 0; |
| 296 | config_save(); | 311 | config_save(); |
| 312 | } else if (c.kind == CK_DROPDOWN && c.ival) { | ||
| 313 | if (v < c.min) v = c.min; | ||
| 314 | if (v > c.max) v = c.max; | ||
| 315 | if (*c.ival != v) { | ||
| 316 | *c.ival = v; | ||
| 317 | config_save(); | ||
| 318 | } | ||
| 297 | } else if (c.kind == CK_SLIDER && c.ival) { | 319 | } else if (c.kind == CK_SLIDER && c.ival) { |
| 298 | if (v < c.min) v = c.min; | 320 | if (v < c.min) v = c.min; |
| 299 | if (v > c.max) v = c.max; | 321 | if (v > c.max) v = c.max; |
| @@ -431,11 +453,11 @@ static void build_cards() { | |||
| 431 | add_toggle(chams, "Viewmodel (hands/weapon)", | 453 | add_toggle(chams, "Viewmodel (hands/weapon)", |
| 432 | "Apply chams to the first-person hands and weapon too.", | 454 | "Apply chams to the first-person hands and weapon too.", |
| 433 | &g_chams_viewmodel); | 455 | &g_chams_viewmodel); |
| 434 | add_slider(chams, "Material", | 456 | static const char *const chams_mat_names[] = { |
| 435 | "0 Flat, 1 Wireframe, 2 Additive glow, 3 Solid, 4 White.", 0, 4, | 457 | "Flat", "Wireframe", "Additive glow", "Solid", "White"}; |
| 436 | &g_chams_mat_index); | 458 | add_dropdown(chams, "Material", "Chams material.", chams_mat_names, 5, |
| 459 | &g_chams_mat_index); | ||
| 437 | add_color(chams, "Colour", "Chams colour.", g_chams_rgb); | 460 | add_color(chams, "Colour", "Chams colour.", g_chams_rgb); |
| 438 | |||
| 439 | int view = add_card(2, nullptr, "Camera / display", | 461 | int view = add_card(2, nullptr, "Camera / display", |
| 440 | "Camera FOV, viewmodel FOV and HDR brightness.", true); | 462 | "Camera FOV, viewmodel FOV and HDR brightness.", true); |
| 441 | add_slider(view, "Camera FOV", | 463 | add_slider(view, "Camera FOV", |
| @@ -912,7 +934,7 @@ static int header_h(const Card &c) { | |||
| 912 | } | 934 | } |
| 913 | 935 | ||
| 914 | static int label_max_w(const Ctrl &c) { | 936 | static int label_max_w(const Ctrl &c) { |
| 915 | if (c.kind == CK_SLIDER) | 937 | if (c.kind == CK_SLIDER || c.kind == CK_DROPDOWN) |
| 916 | return S(214.0f); | 938 | return S(214.0f); |
| 917 | return card_content_w() - S(16.0f) - S(110.0f); | 939 | return card_content_w() - S(16.0f) - S(110.0f); |
| 918 | } | 940 | } |
| @@ -1081,6 +1103,52 @@ static bool control_rect(int ctrl_index, Rect &out_card, Rect &out_row) { | |||
| 1081 | return false; | 1103 | return false; |
| 1082 | } | 1104 | } |
| 1083 | 1105 | ||
| 1106 | // Dropdown popup state (one open at a time). The value is the Ctrl index. | ||
| 1107 | static int g_dropdown_ctrl = -1; | ||
| 1108 | |||
| 1109 | static bool dropdown_rects(int ctrl_index, Rect &btn, Rect &pop, | ||
| 1110 | int &item_h) { | ||
| 1111 | Rect card, row; | ||
| 1112 | if (!control_rect(ctrl_index, card, row)) | ||
| 1113 | return false; | ||
| 1114 | Ctrl &ct = g_ctrls[ctrl_index]; | ||
| 1115 | if (ct.kind != CK_DROPDOWN || !ct.opts || ct.nopts <= 0) | ||
| 1116 | return false; | ||
| 1117 | int bw = S(150.0f); | ||
| 1118 | btn = Rect{card.x + card.w - S(16.0f) - bw, | ||
| 1119 | row.y + (row.h - S(26.0f)) / 2, bw, S(26.0f)}; | ||
| 1120 | item_h = S(22.0f); | ||
| 1121 | pop = Rect{btn.x, btn.y + btn.h, btn.w, item_h * ct.nopts}; | ||
| 1122 | if (pop.y + pop.h > g_win_y + g_win_h - S(34.0f)) | ||
| 1123 | pop.y = btn.y - pop.h; | ||
| 1124 | return true; | ||
| 1125 | } | ||
| 1126 | |||
| 1127 | static void draw_dropdown() { | ||
| 1128 | if (g_dropdown_ctrl < 0) | ||
| 1129 | return; | ||
| 1130 | Rect btn, pop; | ||
| 1131 | int ih = 0; | ||
| 1132 | if (!dropdown_rects(g_dropdown_ctrl, btn, pop, ih)) { | ||
| 1133 | g_dropdown_ctrl = -1; | ||
| 1134 | return; | ||
| 1135 | } | ||
| 1136 | Ctrl &ct = g_ctrls[g_dropdown_ctrl]; | ||
| 1137 | fill(pop, Col{25, 28, 34, 255}); | ||
| 1138 | outline(pop, C_BORDER); | ||
| 1139 | int cur = ct.ival ? *ct.ival : 0; | ||
| 1140 | for (int i = 0; i < ct.nopts; i++) { | ||
| 1141 | Rect it{pop.x, pop.y + i * ih, pop.w, ih}; | ||
| 1142 | if (it.has(g_mouse_x, g_mouse_y)) | ||
| 1143 | fill(it, Col{45, 50, 60, 255}); | ||
| 1144 | text_in(pop, it.x + S(9), it.y + (ih - g_body_h) / 2, ct.opts[i], | ||
| 1145 | g_font_body, g_body_h, it.w - S(28), | ||
| 1146 | i == cur ? C_ACCENT : C_TEXT); | ||
| 1147 | if (i == cur) | ||
| 1148 | fill(Rect{it.x + it.w - S(14), it.y + ih / 2 - S(3), S(6), S(6)}, | ||
| 1149 | C_ACCENT); | ||
| 1150 | } | ||
| 1151 | } | ||
| 1084 | static bool picker_rects_for(int ctrl_index, PickerRects &out) { | 1152 | static bool picker_rects_for(int ctrl_index, PickerRects &out) { |
| 1085 | if (ctrl_index < 0 || ctrl_index >= g_nctrl) | 1153 | if (ctrl_index < 0 || ctrl_index >= g_nctrl) |
| 1086 | return false; | 1154 | return false; |
| @@ -1219,6 +1287,7 @@ static void close_gui() { | |||
| 1219 | g_slider_drag = -1; | 1287 | g_slider_drag = -1; |
| 1220 | g_picker_ctrl = -1; | 1288 | g_picker_ctrl = -1; |
| 1221 | g_picker_drag = 0; | 1289 | g_picker_drag = 0; |
| 1290 | g_dropdown_ctrl = -1; | ||
| 1222 | g_reset_armed = false; | 1291 | g_reset_armed = false; |
| 1223 | input_clear_mouse_states(); | 1292 | input_clear_mouse_states(); |
| 1224 | if (g_sdl_ready) { | 1293 | if (g_sdl_ready) { |
| @@ -1341,6 +1410,29 @@ static void handle_mouse_click() { | |||
| 1341 | return; | 1410 | return; |
| 1342 | int mx = g_mouse_x, my = g_mouse_y; | 1411 | int mx = g_mouse_x, my = g_mouse_y; |
| 1343 | 1412 | ||
| 1413 | // Open dropdown: consume the click on its popup first. | ||
| 1414 | if (g_dropdown_ctrl >= 0) { | ||
| 1415 | Rect btn, pop; | ||
| 1416 | int ih = 0; | ||
| 1417 | if (dropdown_rects(g_dropdown_ctrl, btn, pop, ih)) { | ||
| 1418 | if (pop.has(mx, my)) { | ||
| 1419 | Ctrl &ct = g_ctrls[g_dropdown_ctrl]; | ||
| 1420 | int i = (my - pop.y) / ih; | ||
| 1421 | if (i >= 0 && i < ct.nopts && ct.ival) { | ||
| 1422 | *ct.ival = i; | ||
| 1423 | config_save(); | ||
| 1424 | } | ||
| 1425 | g_dropdown_ctrl = -1; | ||
| 1426 | return; | ||
| 1427 | } | ||
| 1428 | if (btn.has(mx, my)) { | ||
| 1429 | g_dropdown_ctrl = -1; | ||
| 1430 | return; | ||
| 1431 | } | ||
| 1432 | } | ||
| 1433 | g_dropdown_ctrl = -1; | ||
| 1434 | } | ||
| 1435 | |||
| 1344 | // title bar: drag | 1436 | // title bar: drag |
| 1345 | Rect title{g_win_x, g_win_y, g_win_w, S(38.0f)}; | 1437 | Rect title{g_win_x, g_win_y, g_win_w, S(38.0f)}; |
| 1346 | if (title.has(mx, my)) { | 1438 | if (title.has(mx, my)) { |
| @@ -1457,6 +1549,14 @@ static void handle_mouse_click() { | |||
| 1457 | } | 1549 | } |
| 1458 | return; | 1550 | return; |
| 1459 | } | 1551 | } |
| 1552 | if (ct.kind == CK_DROPDOWN) { | ||
| 1553 | int idx = c.c0 + j; | ||
| 1554 | Rect btn, pop; | ||
| 1555 | int ih = 0; | ||
| 1556 | if (dropdown_rects(idx, btn, pop, ih) && btn.has(mx, my)) | ||
| 1557 | g_dropdown_ctrl = (g_dropdown_ctrl == idx) ? -1 : idx; | ||
| 1558 | return; | ||
| 1559 | } | ||
| 1460 | if (ct.kind == CK_COLOR) { | 1560 | if (ct.kind == CK_COLOR) { |
| 1461 | int idx = c.c0 + j; | 1561 | int idx = c.c0 + j; |
| 1462 | int base = label_row_h(ct); | 1562 | int base = label_row_h(ct); |
| @@ -1876,6 +1976,22 @@ static void draw_card(const Rect &clip, int index, int y) { | |||
| 1876 | on ? C_GOOD : C_DIM); | 1976 | on ? C_GOOD : C_DIM); |
| 1877 | } else if (ct.kind == CK_SLIDER) { | 1977 | } else if (ct.kind == CK_SLIDER) { |
| 1878 | draw_slider(clip, cr, by, rh, ct); | 1978 | draw_slider(clip, cr, by, rh, ct); |
| 1979 | } else if (ct.kind == CK_DROPDOWN) { | ||
| 1980 | int cur = ct.ival ? *ct.ival : 0; | ||
| 1981 | if (cur < 0) cur = 0; | ||
| 1982 | if (cur >= ct.nopts) cur = ct.nopts - 1; | ||
| 1983 | const char *opt = | ||
| 1984 | (ct.opts && cur >= 0 && cur < ct.nopts) ? ct.opts[cur] : "?"; | ||
| 1985 | int bw = S(150.0f); | ||
| 1986 | Rect btn{cr.x + cr.w - S(16.0f) - bw, | ||
| 1987 | by + (base_h - S(26.0f)) / 2, bw, S(26.0f)}; | ||
| 1988 | fill(btn, Col{45, 50, 60, 255}, &clip); | ||
| 1989 | outline(btn, g_dropdown_ctrl == c.c0 + j ? C_ACCENT : C_BORDER, | ||
| 1990 | &clip); | ||
| 1991 | text_in(clip, btn.x + S(9), btn.y + (btn.h - g_body_h) / 2, opt, | ||
| 1992 | g_font_body, g_body_h, btn.w - S(30), C_TEXT); | ||
| 1993 | text_in(clip, btn.x + btn.w - S(16), btn.y + (btn.h - g_body_h) / 2, | ||
| 1994 | "v", g_font_body, g_body_h, 0, C_DIM); | ||
| 1879 | } else if (ct.kind == CK_COLOR) { | 1995 | } else if (ct.kind == CK_COLOR) { |
| 1880 | int pr = ct.rgb ? ct.rgb[0] : 255; | 1996 | int pr = ct.rgb ? ct.rgb[0] : 255; |
| 1881 | int pg = ct.rgb ? ct.rgb[1] : 255; | 1997 | int pg = ct.rgb ? ct.rgb[1] : 255; |
| @@ -1893,6 +2009,13 @@ static void draw_card(const Rect &clip, int index, int y) { | |||
| 1893 | } | 2009 | } |
| 1894 | 2010 | ||
| 1895 | static void draw_tooltip() { | 2011 | static void draw_tooltip() { |
| 2012 | if (g_dropdown_ctrl >= 0) { | ||
| 2013 | Rect btn, pop; | ||
| 2014 | int ih = 0; | ||
| 2015 | if (dropdown_rects(g_dropdown_ctrl, btn, pop, ih) && | ||
| 2016 | (pop.has(g_mouse_x, g_mouse_y) || btn.has(g_mouse_x, g_mouse_y))) | ||
| 2017 | return; | ||
| 2018 | } | ||
| 1896 | if (!g_hover_desc || !g_hover_desc[0]) | 2019 | if (!g_hover_desc || !g_hover_desc[0]) |
| 1897 | return; | 2020 | return; |
| 1898 | float d = now_sec() - (float)g_hover_since; | 2021 | float d = now_sec() - (float)g_hover_since; |
| @@ -2102,6 +2225,7 @@ void gui_draw() { | |||
| 2102 | text_in(foot, hint_x, foot.y + S(6.0f), hint, g_font_body, g_body_h, 0, | 2225 | text_in(foot, hint_x, foot.y + S(6.0f), hint, g_font_body, g_body_h, 0, |
| 2103 | C_DIM); | 2226 | C_DIM); |
| 2104 | 2227 | ||
| 2228 | draw_dropdown(); | ||
| 2105 | draw_tooltip(); | 2229 | draw_tooltip(); |
| 2106 | } | 2230 | } |
| 2107 | 2231 | ||
